CircleLayoutManager实现圆形RecyclerView

public class CircleLayoutManager extends RecyclerView.LayoutManager {
  /**
   * 默认每个item之间的角度
   **/
  private static float INTERVAL_ANGLE = 22.5f;
  /**
   * 滑动距离和角度的一个比例
   **/
  private static float DISTANCE_RATIO = 20f;
  /**
   * 默认的半径长度
   **/
  private static final int DEFAULT_RADIO = 100;
  /**
   * 滑动的方向
   */
  private static int SCROLL_LEFT = 1;
  private static int SCROLL_RIGHT = 2;
  /**
   * 半径默认为100
   **/
  private int mRadius;
  /**
   * 当前旋转的角度
   **/
  private float offsetRotate;
  private int startLeft;
  private int startTop;
  /**
   * 第一个的角度是为0
   **/
  private int firstChildRotate = 0;
  //每个item之间的角度间隔
  private float intervalAngle;
  //最大和最小的移除角度
  private int minRemoveDegree;
  private int maxRemoveDegree;
  //记录Item是否出现过屏幕且还没有回收。true表示出现过屏幕上,并且还没被回收
  private SparseBooleanArray itemAttached = new SparseBooleanArray();
  //保存所有的Item的上下左右的偏移量信息
  private SparseArray itemsRotate = new SparseArray<>();
  // 这里的每个item的大小都是一样的
  private int mDecoratedChildWidth;
  private int mDecoratedChildHeight;

  public CircleLayoutManager() {
    this(DEFAULT_RADIO, 5);
  }

  public CircleLayoutManager(int mRadius, int showCount) {
    this.mRadius = mRadius;
    offsetRotate = 0;
    INTERVAL_ANGLE = 90.0f / (showCount - 1);
    intervalAngle = INTERVAL_ANGLE;
    minRemoveDegree = 180;
    maxRemoveDegree = 270;
    firstChildRotate = minRemoveDegree;
  }

  @Override
  public RecyclerView.LayoutParams generateDefaultLayoutParams() {
    return new RecyclerView.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
  }

  @Override
  public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
    //如果没有item,直接返回
    //跳过preLayout,preLayout主要用于支持动画
    if (state.getItemCount() <= 0 || state.isPreLayout()) {
      offsetRotate = 0;
      if (state.getItemCount() == 0) {
        removeAndRecycleAllViews(recycler);
      }
      return;
    }
    //得到子view的宽和高,这边的item的宽高都是一样的,所以只需要进行一次测量
    View scrap = recycler.getViewForPosition(0);
    addView(scrap);
    measureChildWithMargins(scrap, 0, 0);
    //计算测量布局的宽高
    mDecoratedChildWidth = getDecoratedMeasuredWidth(scrap);
    mDecoratedChildHeight = getDecoratedMeasuredHeight(scrap);
    //确定起始位置,在右下角
    startLeft = getHorizontalSpace() - mDecoratedChildWidth;
    startTop = getVerticalSpace() - mDecoratedChildHeight;
    //记录每个item旋转的角度
    float rotate = firstChildRotate;
    for (int i = 0; i < getItemCount(); i++) {
      itemsRotate.put(i, rotate);
      itemAttached.put(i, false);
      rotate += intervalAngle;
    }
    //在布局之前,将所有的子View先Detach掉,放入到Scrap缓存中
    detachAndScrapAttachedViews(recycler);
    fixRotateOffset();
    layoutItems(recycler, state);
  }

  /**
   * 进行view的回收和显示
   **/
  private void layoutItems(RecyclerView.Recycler recycler, RecyclerView.State state) {
    layoutItems(recycler, state, SCROLL_RIGHT);
  }

  /**
   * 进行view的回收和显示的具体实现
   **/
  private void layoutItems(RecyclerView.Recycler recycler, RecyclerView.State state, int oritention) {
    if (state.isPreLayout()) return;
    //移除界面之外的view
    for (int i = 0; i < getChildCount(); i++) {
      View view = getChildAt(i);
      int position = getPosition(view);
      if (itemsRotate.get(position) - offsetRotate > maxRemoveDegree || itemsRotate.get(position) - offsetRotate < minRemoveDegree) {
        itemAttached.put(position, false);
        removeAndRecycleView(view, recycler);
      }
    }
    //将要显示的view进行显示出来
    int count = getItemCount();
    for (int i = 0; i < count; i++) {
      if (itemsRotate.get(i) - offsetRotate <= maxRemoveDegree + INTERVAL_ANGLE && itemsRotate.get(i) - offsetRotate >= minRemoveDegree - INTERVAL_ANGLE) {
        if (!itemAttached.get(i)) {
          ViewGroup scrap = (ViewGroup) recycler.getViewForPosition(i);
          View childView = scrap.getChildAt(0);
          measureChildWithMargins(scrap, 0, 0);
          if (oritention == SCROLL_LEFT) {
            addView(scrap, 0);
          } else {
            addView(scrap);
          }
          float rotate = itemsRotate.get(i);
          if (count > 90 / INTERVAL_ANGLE + 1) {
            rotate -= offsetRotate;
          }
          int left = calLeftPosition(rotate);
          int top = calTopPosition(rotate);
          scrap.setRotation(rotate);
          layoutDecorated(scrap, startLeft + left, startTop + top, startLeft + left + mDecoratedChildWidth, startTop + top + mDecoratedChildHeight);
          childView.setRotation(-rotate);
          itemAttached.put(i, true);
        }
      }
    }
  }

  @Override
  public int scrollHorizontallyBy(int dx, RecyclerView.Recycler recycler, RecyclerView.State state) {
    if (90.0f / INTERVAL_ANGLE + 1 >= getItemCount()) {
      return 0;
    }
    int willScroll = -dx;
    //每个item x方向上的移动距离
    float theta = -dx / DISTANCE_RATIO;
    float targetRotate = offsetRotate + theta;
    //目标角度
    if (targetRotate < 0) {
      willScroll = (int) (-offsetRotate * DISTANCE_RATIO);
    } else if (targetRotate > getMaxOffsetDegree()) {
      willScroll = (int) ((getMaxOffsetDegree() - offsetRotate) * DISTANCE_RATIO);
    }
    theta = willScroll / DISTANCE_RATIO;
    //当前移动的总角度
    offsetRotate += theta;
    //重新设置每个item的x和y的坐标
    for (int i = 0; i < getChildCount(); i++) {
      ViewGroup view = (ViewGroup) getChildAt(i);
      View childView = view.getChildAt(0);
      float newRotate = view.getRotation() - theta;
      int offsetX = calLeftPosition(newRotate);
      int offsetY = calTopPosition(newRotate);
      view.setRotation(newRotate);
      layoutDecorated(view, startLeft + offsetX, startTop + offsetY, startLeft + offsetX + mDecoratedChildWidth, startTop + offsetY + mDecoratedChildHeight);
      childView.setRotation(-newRotate);
    }
    //根据dx的大小判断是左滑还是右滑
    if (dx > 0) {
      layoutItems(recycler, state, SCROLL_LEFT);
    } else {
      layoutItems(recycler, state, SCROLL_RIGHT);
    }
    return willScroll;
  }

  @Override
  public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler, RecyclerView.State state) {
    return scrollHorizontallyBy(-dy, recycler, state);
  }

  @Override
  public boolean canScrollHorizontally() {
    return true;
  }

  @Override
  public boolean canScrollVertically() {
    return true;
  }

  /**
   * 当前item的x的坐标
   **/
  private int calLeftPosition(float rotate) {
    return (int) (mRadius * Math.cos(Math.toRadians(90 - rotate)));
  }

  /**
   * 当前item的y的坐标
   **/
  private int calTopPosition(float rotate) {
    return (int) (mRadius * Math.sin(Math.toRadians(90 - rotate)));
  }

  /**
   * 设置滚动时候的角度
   **/
  private void fixRotateOffset() {
    if (offsetRotate < 0) {
      offsetRotate = 0;
    }
    if (offsetRotate > getMaxOffsetDegree()) {
      offsetRotate = getMaxOffsetDegree();
    }
  }

  /**
   * 最大的角度
   **/
  private float getMaxOffsetDegree() {
    return (getItemCount() - 1) * intervalAngle - 90;
  }

  private int getHorizontalSpace() {
    return getWidth() - getPaddingRight() - getPaddingLeft();
  }

  private int getVerticalSpace() {
    return getHeight() - getPaddingBottom() - getPaddingTop();
  }


  private PointF computeScrollVectorForPosition(int targetPosition) {
    if (getChildCount() == 0) {
      return null;
    }
    final int firstChildPos = getPosition(getChildAt(0));
    final int direction = targetPosition < firstChildPos ? -1 : 1;
    return new PointF(direction, 0);
  }

  @Override
  public void scrollToPosition(int position) {//移动到某一项
    if (position < 0 || position > getItemCount() - 1) return;
    float targetRotate = position * intervalAngle;
    if (targetRotate == offsetRotate) return;
    offsetRotate = targetRotate;
    fixRotateOffset();
    requestLayout();
  }

  @Override
  public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state, int position) {//平滑的移动到某一项
    LinearSmoothScroller smoothScroller = new LinearSmoothScroller(recyclerView.getContext()) {
      @Override
      public PointF computeScrollVectorForPosition(int targetPosition) {
        return CircleLayoutManager.this.computeScrollVectorForPosition(targetPosition);
      }
    };
    smoothScroller.setTargetPosition(position);
    startSmoothScroll(smoothScroller);
  }

  @Override
  public void onAdapterChanged(RecyclerView.Adapter oldAdapter, RecyclerView.Adapter newAdapter) {//adapter进行改变的时候
    removeAllViews();
    offsetRotate = 0;
  }
}

RecyclerView的布局文件如下:


    
        
        
    

你可能感兴趣的:(CircleLayoutManager实现圆形RecyclerView)