你的RecyclerView还可以这样

作者: 钉某人 | 来源:发表于2018-05-09 23:17 被阅读640次

    RecyclerView的使用大家都很熟悉了,这里偶带大家来实现下面这种效果。

    EchelonLayoutManager

    对的,你猜的不错。这种效果只要操作LayoutManager就可以实现,然后就这样

    mRecyclerView.setLayoutManager(new EchelonLayoutManager(getContext()));
    

    完了??? 对,就是so easy.

    关于如何自定义LayoutManager,网上有很多文章,可以去找百度君。

    自定义LayoutManager步骤一般是:

    • 指定默认的LayoutParams,一般返回 return new RecyclerView.LayoutParams(RecyclerView.LayoutParams.WRAP_CONTENT,
      RecyclerView.LayoutParams.WRAP_CONTENT); 就可以了
    • 测量Item的信息
    • 处理Item回收,使用RecyclerView的二级缓存机制就可以,多方便
    • 滑动处理逻辑

    开启撸码模式

    必须实现的方法

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

    第一次加载的时候,从这里获取我们需要的Item的数据,同时也要把我们的Item排列好,关键函数就是layoutChild(recycler),这个后面讲,主要策略是从最后一个Item加载。

        @Override
        public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
            if (state.getItemCount() == 0 || state.isPreLayout()) return;
            removeAndRecycleAllViews(recycler);
    
            mItemViewWidth = (int) (getHorizontalSpace() * 0.87f);
            mItemViewHeight = (int) (mItemViewWidth * 1.46f);
            mItemCount = getItemCount();
            //获取到所有Item的高度和
            mScrollOffset = Math.min(Math.max(mItemViewHeight, mScrollOffset), mItemCount * mItemViewHeight);
    
            layoutChild(recycler);
        }
    

    这里说说layoutChild(recycler)里面做了什么,看注释吧,亲们

     private void layoutChild(RecyclerView.Recycler recycler) {
            if (getItemCount() == 0 ) return;
            //获取到最后一个Item的位置
            int bottomItemPosition = (int) Math.floor(mScrollOffset / mItemViewHeight);
            //获取到出去一个完整的Item的高度,还剩余多少空间
             int remainSpace = getVerticalSpace() - mItemViewHeight;
            //滑动的时候可以获取到最后一个Item在屏幕上还显示的高度
            int bottomItemVisibleHeight = mScrollOffset % mItemViewHeight;
            //最后一个Item显示高度相对于本身的比例
            final float offsetPercentRelativeToItemView = bottomItemVisibleHeight * 1.0f / mItemViewHeight;
            //把我们需要的Item添加到这个集合
            ArrayList<ItemViewInfo> layoutInfos = new ArrayList<>();
            for (int i = bottomItemPosition - 1, j = 1; i >= 0; i--, j++) {
                //计算偏移量
                double maxOffset = (getVerticalSpace() - mItemViewHeight) / 2 * Math.pow(0.8, j);
                //这个Item的top值
                int start = (int) (remainSpace - offsetPercentRelativeToItemView * maxOffset);
                //这个Item需要缩放的比例
                float scaleXY = (float) (Math.pow(mScale, j - 1) * (1 - offsetPercentRelativeToItemView * (1 - mScale)));
                float positonOffset = offsetPercentRelativeToItemView;
                //Item上面的距离占RecyclerView可用高度的比例
                float layoutPercent = start * 1.0f / getVerticalSpace();
                ItemViewInfo info = new ItemViewInfo(start, scaleXY, positonOffset, layoutPercent);
                layoutInfos.add(0, info);
                remainSpace = (int) (remainSpace - maxOffset);
                //在添加Item的同时,计算剩余空间是否可以容下下一个Item,如果不能的话,就不再添加了
                if (remainSpace <= 0) {
                    info.setTop((int) (remainSpace + maxOffset));
                    info.setPositionOffset(0);
                    info.setLayoutPercent(info.getTop() / getVerticalSpace());
                    info.setScaleXY((float) Math.pow(mScale, j - 1)); ;
                    break;
                }
            }
    
            if (bottomItemPosition < mItemCount) {
                final int start = getVerticalSpace() - bottomItemVisibleHeight;
                layoutInfos.add(new ItemViewInfo(start, 1.0f, bottomItemVisibleHeight * 1.0f / mItemViewHeight, start * 1.0f / getVerticalSpace())
                        .setIsBottom());
            } else {
                bottomItemPosition = bottomItemPosition - 1;//99
            }
            //这里做的是回收处理
            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 = getPosition(childView);
                if (pos > endPos || pos < startPos) {
                    removeAndRecycleView(childView, recycler);
                }
            }
    
            detachAndScrapAttachedViews(recycler);
            //这里主要是对需要显示的Item进行排列以及缩放
            for (int i = 0; i < layoutCount; i++) {
                View view = recycler.getViewForPosition(startPos + i);
                ItemViewInfo layoutInfo = layoutInfos.get(i);
                addView(view);
                measureChildWithExactlySize(view);
                int left = (getHorizontalSpace() - mItemViewWidth) / 2;
                layoutDecoratedWithMargins(view, left, layoutInfo.getTop(), left + mItemViewWidth, layoutInfo.getTop() + mItemViewHeight);
                view.setPivotX(view.getWidth() / 2);
                view.setPivotY(0);
                view.setScaleX(layoutInfo.getScaleXY());
                view.setScaleY(layoutInfo.getScaleXY());
            }
        }
    

    下面就是滑动的处理,如果想要RecyclerView滑动的话,就要打开这个开关。

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

    返回true,你就可以在竖直方向上滑动,相对的也有横向的开关,这里只关注竖向滑动。
    滑动的处理在下面这个函数里

     @Override
        public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler, RecyclerView.State state) {
            int pendingScrollOffset = mScrollOffset + dy;
            mScrollOffset = Math.min(Math.max(mItemViewHeight, mScrollOffset + dy), mItemCount * mItemViewHeight);
            //每次滑动都要对Item进行排列等操作
            layoutChild(recycler);
            return mScrollOffset - pendingScrollOffset + dy;
        }
    

    以上就是主要的逻辑,更多详细的请移步github : https://github.com/DingMouRen/LayoutManagerGroup

    相关文章

      网友评论

      • juexingzhe:能实现镜像效果吗?上面View的Scale是大的,下面的Scale下
      • f1b5d59b78a6:好炫哦 支持下

      本文标题:你的RecyclerView还可以这样

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