孟老板 BaseAdapter封装(五) ListAdapter
- 2021 年 6 月 9 日
- 笔记
- Andoid, Android基础, ListAdapter, RecycleView
- BaseAdapter封装(一) 简单封装
- BaseAdapter封装(二) Header,footer
- BaseAdapter封装(三) 空数据占位图
- BaseAdapter封装(四) PageHelper
- BaseAdapter封装(五) ListAdapter
- BaseAdapter封装(六) Healer,footer for List
- BaseAdapter封装(七) ConcatAdapter 改建头尾
- BaseAdapter封装(八) Paging 分页
目录:
前言:
listAdapter?? 是的你没有听错… 算了不解释了,都1202年了; 它是 androidx.recyclerview.widget 包下为RecycleView服务的类;
ListAdapter 的优势:
1. 刷新列表只需要 submitList 这一个方法; 而避免原Adapter 增删改 的各种 notifyItem.. 操作;
2. AsyncListDiffer 异步计算新旧数据差异, 并通知 Adapter 刷新数据
3. 真正的数据驱动, 无论增删改查 我们只需要关心并操作数据集.
使用:
1.新建Adapter 继承 ListAdapter; 泛型提供 数据集实体类 和 自定义的 ViewHolder; 构造函数提供了 自定义的 DiffCallback()
DiffCallback 继承自 DiffUtil.ItemCallback 稍安勿躁后面会讲到; 跳转


class TestAdapter : ListAdapter<DynamicTwo, NewViewHolder>(DiffCallback()) { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): NewViewHolder { TODO("Not yet implemented") } override fun onBindViewHolder(holder: NewViewHolder, position: Int) { TODO("Not yet implemented") } }
View Code
2. onCreateViewHolder 的代码比较简单, MVVM模式, 直接返回 用 ViewDataBinding 构造的 ViewHolder; 只有一个可变参数, 即布局资源文件ID;
onBindViewHolder 的代码更简单, 拿到数据实体 并交给 ViewHolder 处理即可;
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): NewViewHolder { return NewViewHolder( DataBindingUtil.inflate( LayoutInflater.from(parent.context), R.layout.item_dynamic_img, parent, false ) ) } override fun onBindViewHolder(holder: NewViewHolder, position: Int) { holder.bind(getItem(position)) }
3. ViewHolder 的代码:
open class NewViewHolder(val binding: ViewDataBinding) : RecyclerView.ViewHolder(binding.root){ fun bind(item: BaseItem?) { binding.setVariable(BR.item, item) binding.executePendingBindings() } }
4. DiffCallback 代码:
class DiffCallback : DiffUtil.ItemCallback<DynamicTwo>() {
override fun areItemsTheSame(oldItem: DynamicTwo, newItem: DynamicTwo): Boolean {
return oldItem === newItem
}
override fun areContentsTheSame(oldItem: DynamicTwo, newItem: DynamicTwo): Boolean {
return !oldItem.hasChanged
}
}
5. 操作数据: Adapter 新建跟以往没有区别.
mAdapter = TestAdapter()
mDataBind.rvRecycle.let {
it.layoutManager = LinearLayoutManager(mActivity)
it.adapter = mAdapter
}
5.1 增删操作比较简单, 只需要更改数据集, 并 submitList 即可
注意: 这里需要用新数据集对象操作!!! 切记
fun addData(){ val data = mutableListOf<DynamicTwo>() //currentList 不需要判空, 它有默认的空集合 data.addAll(mAdapter.currentList) repeat(10){ data.add(DynamicTwo()) } mAdapter.submitList(data) } fun deleteItem(position: Int){ val data = mutableListOf<DynamicTwo>() data.addAll(mAdapter.currentList) data.removeAt(position) mAdapter.submitList(data) } fun updateItem(position: Int){ //TODO 暂放 }
为什么这里必须要用新集合对象操作? 我们来看一下 submitList 的源码
public void submitList(@Nullable List<T> list) { mDiffer.submitList(list); } public void submitList(@Nullable final List<T> newList, @Nullable final Runnable commitCallback) { // incrementing generation means any currently-running diffs are discarded when they finish final int runGeneration = ++mMaxScheduledGeneration; if (newList == mList) { // nothing to do (Note - still had to inc generation, since may have ongoing work) if (commitCallback != null) { commitCallback.run(); } return; } final List<T> previousList = mReadOnlyList; // fast simple remove all if (newList == null) { //noinspection ConstantConditions int countRemoved = mList.size(); mList = null; mReadOnlyList = Collections.emptyList(); // notify last, after list is updated mUpdateCallback.onRemoved(0, countRemoved); onCurrentListChanged(previousList, commitCallback); return; } // fast simple first insert if (mList == null) { mList = newList; mReadOnlyList = Collections.unmodifiableList(newList); // notify last, after list is updated mUpdateCallback.onInserted(0, newList.size()); onCurrentListChanged(previousList, commitCallback); return; }
..... }
mList 为旧集合对象; 红字部分(JAVA 代码) 可以看出, 当新旧数据为同一对象时return 就不再往下执行了.
ListAdapter 认为新旧数组为同一对象时, nothing to do.
我们可以认为这是 ListAdapter 的一个特性. 也许它只是提醒我们 不要做无效刷新操作;
当然我们也可以重写 submitList 方法, 然后自动新建数据集.
5.2 DiffUtil
讲更新操作前, 需要先讲 DiffUtil
引用官方话术:
DiffUtil 是 ListAdapter 能够高效改变元素的奥秘所在。DiffUtil 会比较新旧列表中增加、移动、删除了哪些元素,然后输出更新操作的列表将原列表中的元素高效地转换为新的元素。
简单理解:
ListAdpater 就是通过 DiffUtil 计算前后集合的差异, 得出增删改的结果. 通知Adapter做出对应操作;
5.2.1 areItemsTheSame(): 比较两个对象是否是同一个 Item;
常见的比较方式: 可自行根据情况或个人习惯选用
1.比较内存地址: java(==) kotlin(===)
2.比较两个对象的 Id; 一般对象在库表中都有主键 ID 参数; 相同的情况下,必定为同一条记录;
3.equals: java(obj.equals(other)) kotlin(==)
5.2.2 areContentsTheSame(): 在已经确定同一 Item 的情况下, 再确定是否有内容更新;
网上给出的比较方式几乎全是 equals; 但 equals 运用不当根本刷新不了 Item;
1.当 areItemsTheSame() 选用 比较内存地址 的方式时, areContentsTheSame() 不能用equals方式;
2.当某个具体的 Item 更新时, 必定会替换为一个新实体对象时. 可以用 equals 方式;
也就是说,当我给某个动态条目点赞时, 必须要 copy 一个新的动态对象, 给新对象设置点赞状态为 true; 然后再用新对象替换掉数据集中的旧对象. equals 刷新才能奏效;
3.当更新某个Item, 不确定是否为新Item对象实体时, 不能用 equals 方式;
总结: 同一个内存地址的对象 equals 有个鸡儿用? 有个鸡儿用??
状态标记方式:
实体对象中增加: hasChanged: Boolean 字段; 当对象内容变化时,设置 hasChanged 为true; ViewHolder.bind()时,置为false;
给最终的 ViewHolder 和 DiffCallback
class DiffCallback : DiffUtil.ItemCallback<DynamicTwo>() { override fun areItemsTheSame(oldItem: DynamicTwo, newItem: DynamicTwo): Boolean { return oldItem === newItem } override fun areContentsTheSame(oldItem: DynamicTwo, newItem: DynamicTwo): Boolean { return !oldItem.hasChanged } } open class NewViewHolder(val binding: ViewDataBinding) : RecyclerView.ViewHolder(binding.root){ fun bind(item: BaseItem?, index: Int) { item?.hasChanged = false binding.setVariable(BR.item, item) binding.executePendingBindings() } }
5.3 更新操作:
fun updateItem(position: Int){ val data = mutableListOf<DynamicTwo>() data.addAll(mAdapter.currentList) data[position].let { it.title = "变变变 我是百变小魔女" it.hasChanged = true } mAdapter.submitList(data) }
最后:
ListAdapter 可完美的 由数据驱动 UI, 增删改可以放到 ViewModel中, 请求成功后直接操作数据集合 更新列表即可.
回到顶部