通过自定义LayoutManager来实现RecyclerView布局扩展

1 RecyclerView机制

RecyclerView之所以性能优秀,被广泛使用,是因为他的优秀的回收复用机制,查看源代码知道,recyclerview在layoutmanager里面帮我们完成了相关操作,当我们需要自定义LayoutManager时,我们需要将不用的View回收掉;在需要获取新的View时直接申请,即通过getViewForPosition()方法,返回的View可能是之前回收的垃圾View,也可能是new出来的新View,这些都是RecyclerView帮我们做的。那么RecyclerView内部的垃圾View缓存是什么样子的呢?我们接下来看看~

1.1RecyclerView的二级缓存

RecyclerView中,有两个缓存:ScrapRecycleScrap中文就是废料的意思,Recycle对应是回收的意思。这两个缓存有什么作用呢?首先Scrap缓存是指里面缓存的View是接下来需要用到的,即里面的绑定的数据无需更改,可以直接拿来用的,是一个轻量级的缓存集合;而Recycle的缓存的View为里面的数据需要重新绑定,即需要通过Adapter重新绑定数据。

当我们去获取一个新的View时,RecyclerView首先去检查Scrap缓存是否有对应的positionView,如果有,则直接拿出来可以直接用,不用去重新绑定数据;如果没有,则从Recycle缓存中取,并且会回调AdapteronBindViewHolder方法(当然了,如果Recycle缓存为空,还会调用onCreateViewHolder方法),最后再将绑定好新数据的View返回。

1.2 将View缓存的两种方式

前面我们了解到,RecyclerView中有二级缓存,我们可以自己选择将View缓存到哪里。我们有两种选择的方式:DetachRemoveDetachView放在Scrap缓存中,Remove掉的View放在Recycle缓存中;那我们应该如何去选择呢?

在什么样的场景中使用Detach呢?主要是在我们的代码执行结束之前,我们需要反复去将View移除并且马上又要添加进去时,选择Datach方式,比如:当我们对View进行重新排序的时候,可以选择Detach,因为屏幕上显示的就是这些position对应的View,我们并不需要重新去绑定数据,这明显可以提高效率。使用Detach方式可以通过函数detachAndScrapView()实现。

而使用Remove的方式,是当View不在屏幕中有任何显示的时候,你需要将它Remove掉,以备后面循环利用。可以通过函数removeAndRecycleView()实现。

2 开始自定义LayoutManager

新建一个类继承RecyclerView.LayoutManager,实现它的方法generateDefaultLayoutParams();

通过自定义LayoutManager来实现RecyclerView布局扩展_第1张图片

然后重写onLayoutChildren()方法,在这个方法下按照自己的想法去对itemview进行布局

   //摆放子布局
    @Override
    public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
        if(getItemCount() <=0){
            return;
        }

        // 先把所有的View先从RecyclerView中detach掉,然后标记为"Scrap"状态,表示这些View处于可被重用状态(非显示中)。
        // 实际就是把View放到了Recycler中的一个集合中。
        detachAndScrapAttachedViews(recycler);
        //开始摆放
        int offsetY = 0;
        int offsetX = 0;
        int viewH = 0;
        for (int i=0;i getWidth()) {
//                换行的View的值
                offsetY += h;
                offsetX=w;
                fram.set(0, offsetY, w, offsetY + h);
            }else {
//                不需要换行
                fram.set(offsetX, offsetY, offsetX + w, offsetY + h);
                offsetX += w;
            }
//            要 针对于当前View   生成对应的Rect  然后放到allItemframs数组
            allItemframs.put(i, fram);
            layoutDecorated(view, fram.left, fram.top, fram.right, fram.bottom);
        }

    }

看看效果

通过自定义LayoutManager来实现RecyclerView布局扩展_第2张图片

添加滑动效果,对应方法canScrollVertically和scrollVerticallyBy方法(以垂直方向为例)


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

    @Override
    public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler, RecyclerView.State state) {
        //实际滑动距离  dx
        int trval = dy;

        if (verticalScrollOffset + dy < 0) {//如果滑动到最顶部
            trval = -verticalScrollOffset;
        } else if(verticalScrollOffset+dy>totalHeight-getHeight()){
//            如果滑动到最底部  往上滑   verticalScrollOffset   +
            trval = totalHeight - getHeight() - verticalScrollOffset;
        }

        verticalScrollOffset += trval;
        //平移容器内的item
        offsetChildrenVertical(trval);
        recyclerViewFillView(recycler,state);//回收滑出去的itemview,并从缓存中拿出进行复用

        return trval;
    }

回收和复用(关键)

    private void recyclerViewFillView(RecyclerView.Recycler recycler, RecyclerView.State state) {
        //        清空RecyclerView的子View
        detachAndScrapAttachedViews(recycler);//将当前getchildcount数量的子View放入到scrap缓存池去
        Rect phoneFrame = new Rect(0, verticalScrollOffset, getWidth(), verticalScrollOffset + getHeight());//当前可见区域
        //将滑出屏幕的view进行回收
        for (int i=0;i

看看现在的效果

至此就完成了一个自动布局的自定义Layoutmanager

代码地址:https://github.com/games2sven/RecyClerViewLayoutManager

你可能感兴趣的:(通过自定义LayoutManager来实现RecyclerView布局扩展)