RecyclerView之所以性能优秀,被广泛使用,是因为他的优秀的回收复用机制,查看源代码知道
,recyclerview在layoutmanager里面帮我们完成了相关操作,当我们需要自定义LayoutManager
时,我们需要将不用的View
回收掉;在需要获取新的View时直接申请,即通过getViewForPosition()
方法,返回的View
可能是之前回收的垃圾View
,也可能是new
出来的新View
,这些都是RecyclerView帮我们做的。那么RecyclerView
内部的垃圾View
缓存是什么样子的呢?我们接下来看看~
在RecyclerView
中,有两个缓存:Scrap
和Recycle
。Scrap
中文就是废料的意思,Recycle
对应是回收的意思。这两个缓存有什么作用呢?首先Scrap
缓存是指里面缓存的View
是接下来需要用到的,即里面的绑定的数据无需更改,可以直接拿来用的,是一个轻量级的缓存集合;而Recycle
的缓存的View
为里面的数据需要重新绑定,即需要通过Adapte
r重新绑定数据。
当我们去获取一个新的View
时,RecyclerView
首先去检查Scrap
缓存是否有对应的position
的View
,如果有,则直接拿出来可以直接用,不用去重新绑定数据;如果没有,则从Recycle
缓存中取,并且会回调Adapter
的onBindViewHolder
方法(当然了,如果Recycle
缓存为空,还会调用onCreateViewHolder
方法),最后再将绑定好新数据的View返回。
前面我们了解到,RecyclerView
中有二级缓存,我们可以自己选择将View
缓存到哪里。我们有两种选择的方式:Detach
和Remove
。Detach
的View
放在Scrap
缓存中,Remove
掉的View
放在Recycle缓存中;那我们应该如何去选择呢?
在什么样的场景中使用Detach
呢?主要是在我们的代码执行结束之前,我们需要反复去将View
移除并且马上又要添加进去时,选择Datach
方式,比如:当我们对View进行重新排序的时候,可以选择Detach,因为屏幕上显示的就是这些position对应的View,我们并不需要重新去绑定数据,这明显可以提高效率。使用Detach
方式可以通过函数detachAndScrapView()
实现。
而使用Remove的方式,是当View不在屏幕中有任何显示的时候,你需要将它Remove掉,以备后面循环利用。可以通过函数removeAndRecycleView()
实现。
新建一个类继承RecyclerView.LayoutManager,实现它的方法generateDefaultLayoutParams();
然后重写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);
}
}
看看效果
添加滑动效果,对应方法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