RecyclerView
是 materials design 中的组件之一,相应的还有CardView
等。从名字看,它主要的特点就是复用。我们之前用的 ListView
也可以复用。但是 RecyclerView
提供了一个耦合度更低的方式来复用 ViewHolder
,将实体的内容交给用户,而将缓存的复用、管理维护以及各种动画效果,做了封装。
本文主要分析 RecyclerView
的缓存机制,布局管理器以及 Adapter
的实现方式。
继承关系
java.lang.Object
↳ android.view.View
↳ android.view.ViewGroup
↳ android.support.v7.widget.RecyclerView
从中可以看出 RecyclerView
作为一个全新组件,完全重写了 ViewGroup
,并不是对 ListView
的扩展。
构造方法:
主要作用如下:
1. 初始化 AdapterManager
2. 初始化 ChildrenHelper
(子 view 的主要管理器)
3. 初始化动画的监听
4. 从属性获取 layoutManager
的名字,并通过反射创建 LayoutManager
对象
ViewHolder 和 Adapter
ViewHolder
和 Adapter
是RecyclerView
的内部抽象类,对外提供接口,将内容的创建过程交给用户,而将 view 的复用、管理、缓存等操作封装起来。
其实对于 ListView
,也可以实现这种设计方式。请参考我的另一篇:
《ListView 优化篇:从 BaseAdapter 到 BaseViewHolder》
用 ListView
实现 Recycler.Adapter
的代码如下:
public abstract class DefaultAdapter<T> extends BaseAdapter {
@Override
public View getView(int position, View convertView, ViewGroup parent) {
BaseViewHolder holder = null;
if (convertView == null) {
// 创建 ViewHolder
holder = onCreateViewHolder();
} else {
holder = (BaseViewHolder) convertView.getTag();
}
T info = mDatas.get(position);
// 绑定数据
holder.onBindViewHolder();
return holder.getContentView();
}
protected abstract void onCreateViewHolder(BaseViewHolder holder, T data);
}
布局管理器 LayoutManager
从 LayoutManager
的方法来看,它的主要作用是:
- 负责
RecyclerView
和子 View 的测量、布局并最终以特定的效果显示在界面 - 管理着动画。
- 管理各种 view 的存储
- 实现整个
RecyclerView
的滚动
下面是几个最具代表性的方法:
- setRecyclerView 为 RecyclerView 设置测量规则,通过这种方式设置的规则都是 MeasureSpec.EXACTLY,临时值
- setMeasuredDimensionFromChildren 根据子View的大小,设置自身大小
- endAnimation 结束动画
- addView 添加子 view
- removeView 移除子 view
- findViewByPosition 查找view
缓存管理机制
RecyclerView
通过 ViewHolder
展现子视图,其中维护了一个二级缓存,滑出界面的 ViewHolder
会暂时放到 cache 结构中,而从 cache 结构中移除的ViewHolder
,则会放到一个叫做 RecycledViewPool
的循环缓存池中。RecycledViewPool
中 ViewHolder
还可以被多个 RecyclerView
共享,以提升性能。其中,cache
默认存 2 个,RecycledViewPool
默认存 5 个。对于二级缓存池中的 holder
对象,根据 viewType
进行严格分类,不同类型的 viewType
之间的缓存互不影响。
以下是 RecycledViewPool
的原理:
public static class RecycledViewPool {
// 根据 viewType 保存的被废弃的 ViewHolder 集合,以便下次使用
private SparseArray<ArrayList<ViewHolder>> mScrap = new SparseArray<ArrayList<ViewHolder>>();
// 各种 viewType 对应的最大缓存数
private SparseIntArray mMaxScrap = new SparseIntArray();
// 默认缓存 5 个
private static final int DEFAULT_MAX_SCRAP = 5;
/** * 设置最大缓存数量 说明:如果有多余,从顶部移除多余的对象 * * @param viewType * @param max */
public void setMaxRecycledViews(int viewType, int max) {
mMaxScrap.put(viewType, max);
final ArrayList<ViewHolder> scrapHeap = mScrap.get(viewType);
if (scrapHeap != null) {
while (scrapHeap.size() > max) {
scrapHeap.remove(scrapHeap.size() - 1);
}
}
}
/** * 获取缓存对象 说明:从顶部获取,获取后移除 * * @param viewType * @return */
public ViewHolder getRecycledView(int viewType) {
final ArrayList<ViewHolder> scrapHeap = mScrap.get(viewType);
if (scrapHeap != null && !scrapHeap.isEmpty()) {
final int index = scrapHeap.size() - 1;
final ViewHolder scrap = scrapHeap.get(index);
scrapHeap.remove(index);
return scrap;
}
return null;
}
public void putRecycledView(ViewHolder scrap) {
final int viewType = scrap.getItemViewType();
final ArrayList scrapHeap = getScrapHeapForType(viewType);
if (mMaxScrap.get(viewType) <= scrapHeap.size()) {
return;
}
scrap.resetInternal();
scrapHeap.add(scrap);
}
/** * 根据 viewType 获取对应缓存池 说明:如果不存在,则创建 * * @param viewType * @return */
private ArrayList<ViewHolder> getScrapHeapForType(int viewType) {
ArrayList<ViewHolder> scrap = mScrap.get(viewType);
if (scrap == null) {
scrap = new ArrayList<>();
mScrap.put(viewType, scrap);
if (mMaxScrap.indexOfKey(viewType) < 0) {
mMaxScrap.put(viewType, DEFAULT_MAX_SCRAP);
}
}
return scrap;
}
}