Android RecyclerView

本文详细介绍了Android的RecyclerView组件,包括其优于ListView的缓存机制,LayoutManager的三种类型:LinearLayoutManager、GridLayoutManager和StaggeredGridLayoutManager。重点讲解了Adapter的实现和更新逻辑,以及如何使用ListAdapter简化数据绑定。此外,还讨论了RecyclerView的滑动行为自定义,如添加震动、半透明效果,并提到了ConcatAdapter用于列表拼接。
摘要由CSDN通过智能技术生成

λ:

# 仓库地址: https://github.com/lzyprime/android_demos/tree/recyclerview

git clone -b recyclerview https://github.com/lzyprime/android_demos

RecyclerView作 Android 列表项的展示组件。相比ListView,缓存机制做的更细致,提升流畅度。以空间换时间

两个重要参数:

  1. LayoutManager: 排版
  2. RecyclerView.Adapter: 列表项获取方式

LayoutManager

LayoutManager 可以在xml中直接配置. 也可在逻辑代码中设置。

// xml
   <androidx.recyclerview.widget.RecyclerView
        ...
        // LayoutManager类型
        app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
        // 几栏
        app:spanCount="1"
        />

全部可配参数:

在这里插入图片描述

1. LinearLayoutManager

public class LinearLayoutManager extends RecyclerView.LayoutManager implements
        ItemTouchHelper.ViewDropHandler, RecyclerView.SmoothScroller.ScrollVectorProvider

单栏线性布局。无法多栏展示。构造函数参数:

  1. orientation: 方向
  2. reverseLayout: 反转,倒序列表项

stackFromEnd 用来兼容 android.widget.AbsListView.setStackFromBottom(boolean)。相当于reverseLayout 的效果。

同时实现了ItemTouchHelper.ViewDropHandler, RecyclerView.SmoothScroller.ScrollVectorProvider

在这里插入图片描述

2. GridLayoutManager

public class GridLayoutManager extends LinearLayoutManager

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

在这里插入图片描述

3. StaggeredGridLayoutManager

public class StaggeredGridLayoutManager extends RecyclerView.LayoutManager implements
        RecyclerView.SmoothScroller.ScrollVectorProvider

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

在这里插入图片描述

Adapter

RecyclerView.Adapter<VH : RecyclerView.ViewHolder>

public 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(...)
            ...
        }
}

onBindViewHolder

View 创建完成,开始绑定数据。包括事件监听注册。

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? 要配合 AdapteronBindViewHolder(holder: VH, position: Int, payloads: MutableList<Any>) 实现 View 的局部刷新。否则,执行 onBindViewHolder(holder: VBViewHolder<VH>, position: Int)

缓存机制

主要逻辑在 RecyclerView.Recycler。 缓存主要有 Scrap, CachedView, RecycledViewPoolViewCacheExtension 用于额外自定义缓存。

  • Scrap: 当前正在展示的部分。
  • CachedView: 刚划出展示区域的部分,默认最大存储 DEFAULT_CACHE_SIZE = 2FIFO更新
  • RecycledViewPool: CachedView 淘汰后,只保留 ViewHolder, 清空数据绑定。 复用时需要重新执行onBindViewHolder

RecycledViewPool 内部是一个SparseArray<ScrapData> 下标为 holder.viewTypeScrapData 内嵌ArrayList<ViewHolder>, 默认最大存储 DEFAULT_MAX_SCRAP = 5ViewHolder。 所以简化一下RecycledViewPool ~= SparseArray<ArrayList<ViewHolder>>

public final class Recycler {
   
    final ArrayList<ViewHolder> mAttachedScrap = new ArrayList<>()
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值