前言
之前在项目中我有写过自定义控件九宫格是根据ViewGroup写的,功能已经实现了,但是后来我感觉如果以后性能提升的话,可能在缓存这块有要求。所以,我感觉还是改变使用自定义LayoutManager来写这个控件,后期就直接使用RecyclerView本身的缓存机制来处理回收和重用的操作。
其实我们再自定义ViewGroup去编写我们的控件的时候,就有测量和布局。
在自定义LayoutManager中也有这些概念,但是还是有些区别,我们来看下。
重构和初始化
首先我们来给我们的自定义的LayoutManager取名为NineGridLayoutManager
。
public class NineGridLayoutManager extends RecyclerView.LayoutManager {
private Pool<Rect> mCacheBorders; //用于规定Item显示的区域
public NineGridLayoutManager(Context context) {
mCacheBorders = new Pool<>(new Pool.New<Rect>() {
@Override
public Rect get() {
return new Rect();
}
});
}
@Override
public RecyclerView.LayoutParams generateDefaultLayoutParams() {
return new RecyclerView.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
}
}
编写类的构造类,这里的Pool类是我自己写的一个缓存类,目的还是用于给我们后面的Grid的布局和测量存放参数。
public class Pool<T> {
private SparseArrayCompat<T> mPool;
private New<T> mNewInstance;
public Pool(New<T> newInstance) {
mPool = new SparseArrayCompat<>();
mNewInstance = newInstance;
}
public T get(int key) {
T res = mPool.get(key);
if (res == null) {
res = mNewInstance.get();
mPool.put(key, res);
}
return res;
}
public interface New<T> {
T get();
}
}
代码不难, 核心思想就是建立一个索引,便于我们后期来获取这个类别。
我们来看一个generateDefaultLayoutParams()方法,这里是专门设置当前RecyclerView类的大小,可以看见,这里我们设置了WRAP_CONTENT,设置这个的目的就是等到子控件也就是我们的Grid测量完毕后,再来确定我们控件的大小。
测量
与ViewGroup的区别
首先,我们平时在自定义ViewGroup的时候,测量子View是在onMeasure方法中统一测量的;
而在自定义LayoutManager中,子View是当需要layout的时候才测量,LayoutManager已经提供了两个方法给我们直接调用了:
measureChild(View child, int widthUsed, int heightUsed)
measureChildWithMargins(View child, int widthUsed, int heightUsed)
这两个方法都可以测量子View,不同的是第二个方法会把Item设置的Margin也考虑进去,所以如果我们的LayoutManager需要支持Margin属性的话,就用第二个了。