LayoutManager实现的右滑列表

作者: 钉某人 | 来源:发表于2018-05-10 22:10 被阅读200次

    在逛Dribbble时无意间看到这样的效果,看着挺cool的,就自己来实现一番。

    Dribble.gif

    实现的效果:

    效果 效果
    SkidRightLayoutManager1.gif SkidRightLayoutManager2.gif

    实现思路是自定义LayoutManager来实现Item中的布局,通过RecyclerView来展示。

    1.必须实现的方法

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

    2.通过重写onLayoutChildren方法来实现初始化的布局以及初始化一些需要的数据

        @Override
        public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
            if (state.getItemCount() == 0 || state.isPreLayout()) return;
            //首先将所有的Item回收到缓存中
            removeAndRecycleAllViews(recycler);
            //第一进来,声明Item的宽高,并标识Item的数目不为0.
            if (!mHasChild) {
                mItemViewHeight = getVerticalSpace();
                mItemViewWidth = (int) (mItemViewHeight / mItemHeightWidthRatio);
                mHasChild = true;
            }
            mItemCount = getItemCount();
            mScrollOffset = makeScrollOffsetWithinRange(mScrollOffset);
            //对Item进行布局
            fill(recycler);
        }
    

    3.Item的布局以及缩放的关键就是在fill(recycler)函数和fillChild()函数中,同时对Item进行回收处理

     public void fill(RecyclerView.Recycler recycler) {
            //获取最右边Item的位置
            int bottomItemPosition = (int) Math.floor(mScrollOffset / mItemViewWidth);
            //获取最右边的Item的可见宽度
            int bottomItemVisibleSize = mScrollOffset % mItemViewWidth;
            //获取最右边Item可见宽度相对于Item宽度的比例
            final float offsetPercent = bottomItemVisibleSize * 1.0f / mItemViewWidth;
            final int space = getHorizontalSpace();
            //用于存储要显示的Item的信息,从最右边的Item开始添加
            ArrayList<ItemViewInfo> layoutInfos = new ArrayList<>();
            for (int i = bottomItemPosition - 1, j = 1, remainSpace = space - mItemViewWidth;
                 i >= 0; i--, j++) {
                //左右两个Item间最大偏移量
                double maxOffset = (getHorizontalSpace() - mItemViewWidth) / 2 * Math.pow(mScale, j);
                //Item左边的位置
                int start = (int) (remainSpace - offsetPercent * maxOffset);
                ItemViewInfo info = new ItemViewInfo(start,
                        (float) (Math.pow(mScale, j - 1) * (1 - offsetPercent * (1 - mScale))),
                        offsetPercent,
                        start * 1.0f / space
                );
                layoutInfos.add(0, info);
    
                remainSpace -= maxOffset;
                if (remainSpace <= 0) {
                    info.setTop((int) (remainSpace + maxOffset));
                    info.setPositionOffset(0);
                    info.setLayoutPercent(info.getTop() / space);
                    info.setScaleXY( (float) Math.pow(mScale, j - 1));
                    break;
                }
            }
         
            if (bottomItemPosition < mItemCount) {
                final int start = space - bottomItemVisibleSize;
                layoutInfos.add(new ItemViewInfo(start, 1.0f,
                        bottomItemVisibleSize * 1.0f / mItemViewWidth, start * 1.0f / space).
                        setIsBottom());
            } else {
                bottomItemPosition -= 1;
            }
             //Item回收处理的逻辑
            int layoutCount = layoutInfos.size();
            final int startPos = bottomItemPosition - (layoutCount - 1);
            final int endPos = bottomItemPosition;
            final int childCount = getChildCount();
            for (int i = childCount - 1; i >= 0; i--) {
                View childView = getChildAt(i);
                int pos = convert2LayoutPosition(getPosition(childView));
                if (pos > endPos || pos < startPos) {
                    removeAndRecycleView(childView, recycler);
                }
            }
            detachAndScrapAttachedViews(recycler);
    
            for (int i = 0; i < layoutCount; i++) {
                //将Item进行排列
                fillChild(recycler.getViewForPosition(convert2AdapterPosition(startPos + i)), layoutInfos.get(i));
            }
        }
    
        private void fillChild(View view, ItemViewInfo layoutInfo) {
            addView(view);
            measureChildWithExactlySize(view);
            //计算缩放比例
            final int scaleFix = (int) (mItemViewWidth * (1 - layoutInfo.getScaleXY()) / 2);
    
            int top = (int) getPaddingTop();
            //排列Item
            layoutDecoratedWithMargins(view, layoutInfo.getTop() - scaleFix, top
                    , layoutInfo.getTop() + mItemViewWidth - scaleFix, top + mItemViewHeight);
            //对Item进行缩放
            ViewCompat.setScaleX(view, layoutInfo.getScaleXY());
            ViewCompat.setScaleY(view, layoutInfo.getScaleXY());
        }
    
    

    4.设置列表的滑动方向

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

    5.滑动处理逻辑,滑动的同时也要对Item进行位置排列

        @Override
        public int scrollHorizontallyBy(int dx, RecyclerView.Recycler recycler, RecyclerView.State state) {
            int pendingScrollOffset = mScrollOffset + dx;
            mScrollOffset = makeScrollOffsetWithinRange(pendingScrollOffset);
            fill(recycler);
            return mScrollOffset - pendingScrollOffset + dx;
        }
    
    

    主要逻辑已将完成,一句代码实现cool~~~列表

     mSkidRightLayoutManager = new SkidRightLayoutManager(1.5f, 0.85f);
            mRecyclerView.setLayoutManager(mSkidRightLayoutManager);
    

    详细代码请看github源码哦

    相关文章

      网友评论

      本文标题:LayoutManager实现的右滑列表

      本文链接:https://www.haomeiwen.com/subject/nsztdftx.html