# 仓库地址: https://github.com/lzyprime/android_demos/tree/recyclerview
git clone -b recyclerview https://github.com/lzyprime/android_demos
RecyclerView作 Android 列表项的展示组件。相比ListView,缓存机制做的更细致,提升流畅度。以空间换时间
两个重要参数:
LayoutManager: 排版RecyclerView.Adapter: 列表项获取方式LayoutManager 可以在xml中直接配置. 也可在逻辑代码中设置。
// xml
全部可配参数:

public class LinearLayoutManager extends RecyclerView.LayoutManager implements
ItemTouchHelper.ViewDropHandler, RecyclerView.SmoothScroller.ScrollVectorProvider
单栏线性布局。无法多栏展示。构造函数参数:
stackFromEnd 用来兼容 android.widget.AbsListView.setStackFromBottom(boolean)。相当于reverseLayout 的效果。
同时实现了ItemTouchHelper.ViewDropHandler, RecyclerView.SmoothScroller.ScrollVectorProvider

public class GridLayoutManager extends LinearLayoutManager
网格布局。LinearLayoutManager 升级版,可以通过spanCount设置分几栏

public class StaggeredGridLayoutManager extends RecyclerView.LayoutManager implements
RecyclerView.SmoothScroller.ScrollVectorProvider
流布局。 当列表项尺寸不一致时, GridLayoutManager 根据尺寸较大项确定网格尺寸。导致较小项会有空白部分。StaggeredGridLayoutManager 则紧凑拼接每一项。 通过 setGapStrategy(int) 设置间隙处理策略。

RecyclerView.Adapterpublic abstract static class Adapter<VH extends ViewHolder> {
...
@NonNull
public abstract VH onCreateViewHolder(@NonNull ViewGroup parent, int viewType);
public abstract void onBindViewHolder(@NonNull VH holder, int position);
public abstract int getItemCount();
}
public abstract static class ViewHolder {
public ViewHolder(@NonNull View itemView) {
... }
}
一个Adapter至少需要override这三个函数。
getItemCount返回列表项的个数。
onCreateViewHolder, getItemViewType创建一个ViewHolder, 如果 ViewHolder 有多种类型,可以通过viewType参数判断。 viewType 的值来自 getItemViewType(position: Int) 函数。默认返回0。 0 <= position < getItemCount()
以聊天消息为例:
sealed class Msg {
data class Text(val content: String) : Msg()
data class Image(val url: String) : Msg()
data class Video(...) : Msg()
...
}
class MsgListAdapter : RecyclerView.Adapter<MsgListAdapter.MsgViewHolder>() {
sealed class MsgViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
class Text(...) : MsgViewHolder(...)
class Image(...) : MsgViewHolder(...)
...
}
private var dataList: List<Msg> = listOf()
override fun getItemViewType(position: Int): Int =
when (dataList[position]) {
is Msg.Text -> 1
is Msg.Image -> 2
...
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MsgViewHolder =
when (viewType) {
1 -> MsgViewHolder.Text(...)
2 -> MsgViewHolder.Image(...)
...
}
}
onBindViewHolderView 创建完成,开始绑定数据。包括事件监听注册。
class VBViewHolder<VB : ViewBinding>(private val binding : VB) : ViewHolder(binding.root) {
fun bind(data: T, onClick:() -> Unit) {
binding.data = data
...
binding.anyView.setOnClickListener {
onClick() }
...
}
}
class Adapter(private val onItemClick: () -> Unit) : RecyclerView.Adapter<VBViewHolder<XXX>>() {
override fun onBindViewHolder(holder: VBViewHolder<XXX>, position: Int) =
holder.bindHolder(dataList[position], onItemClick)
}
由于缓存机制,更新完数据源, ViewHolder 也并不会立刻刷新。需要通过Adapter的一系列方法,显式通知发生变化的列表项。
notifyDataSetChanged()notifyItemChanged(position: Int), notifyItemChanged(position: Int, payload: Any?)notifyItemRangeChanged(positionStart: Int, itemCount: Int), notifyItemRangeChanged(positionStart: Int, itemCount: Int, payload: Any?)notifyItemMoved(fromPosition: Int, toPosition: Int)notifyItemInserted(position: Int)notifyItemRangeInserted(positionStart: Int, itemCount: Int)notifyItemRemoved(position: Int)notifyItemRangeRemoved(positionStart: Int, itemCount: Int)payload: Any? 要配合 Adapter 的 onBindViewHolder(holder: VH, position: Int, payloads: MutableList 实现 View 的局部刷新。否则,执行 onBindViewHolder(holder: VBViewHolder
主要逻辑在 RecyclerView.Recycler。 缓存主要有 Scrap, CachedView, RecycledViewPool。 ViewCacheExtension 用于额外自定义缓存。
Scrap: 当前正在展示的部分。CachedView: 刚划出展示区域的部分,默认最大存储 DEFAULT_CACHE_SIZE = 2。 FIFO更新RecycledViewPool: CachedView 淘汰后,只保留 ViewHolder, 清空数据绑定。 复用时需要重新执行onBindViewHolder。RecycledViewPool 内部是一个SparseArray 下标为 holder.viewType。ScrapData 内嵌ArrayList, 默认最大存储 DEFAULT_MAX_SCRAP = 5 个 ViewHolder。 所以简化一下RecycledViewPool ~= SparseArray。
public final class Recycler {
final ArrayList<ViewHolder> mAttachedScrap = new ArrayList<>();