共计 3445 个字符,预计需要花费 9 分钟才能阅读完成。
ConcatAdapter 是 recyclerview: 1.2.0-alpha 04 中提供的一个新组件,它能够帮咱们程序地组合多个 Adapter,并让它们显示在同一个 RecyclerView 中。这使您能够更好地封装 Adapter。您不用再将许多数据源组合到一个 Adapter 中,从而在缩小 Adapter 复杂度的同时也让它们能够被复用。
这方面的一个用例,是在列表头部和底部显示加载状态: 当列表从网络中检索数据时,咱们想显示一个加载中的图标;如果呈现谬误,咱们要显示错误信息和重试按钮。
△ 一个带有底部的 RecyclerView,底部显示了加载状态: 加载进度或错误信息
ConcatAdapter 简介
ConcatAdapter 让咱们能够程序显示多个 Adapter 中的内容。例如,假如咱们有上面三个 Adapter:
val firstAdapter: FirstAdapter = …
val secondAdapter: SecondAdapter = …
val thirdAdapter: ThirdAdapter = …
val concatAdapter= ConcatAdapter(firstAdapter, secondAdapter,
thirdAdapter)
recyclerView.adapter = concatAdapter
RecyclerView
将会按 Adapter 程序显示所有的我的项目。
应用不同的适配器能够使您更好地区分列表的每个局部。例如,如果要显示一个头部,能够将其封装在它本人的 Adapter 中,而无需把头部的逻辑与解决列表显示的 Adapter 混淆在一起。
△ RecyclerView 和 Adapter 数据
在头部和底部显示加载状态
咱们能够在头部或底部显示一个进度条或错误信息。列表胜利加载数据后,头部或底部便不应该再显示任何信息。这样一来,它们就能够用 Adapter 实现有 0 个或 1 个我的项目的列表:
val concatAdapter = ConcatAdapter(headerAdapter, listAdapter,
footerAdapter)
recyclerView.adapter = concatAdapter
如果头部和底部用的是同一布局、ViewHolder 和 UI 逻辑 (例如: 进度条要何时显示、怎么显示),您能够只实现一个 Adapter,而后创立两个实例: 一个作为头部、一个作为底部。
要取得残缺的实现,请查看这里 拉取申请,它增加了:
- 从 ViewModel 中裸露进去的 LoadState
- 显示加载状态的头部和底部的布局
- 头部和底部的 ViewHolder 对象
- 一个 ListAdapter,它基于 LoadState 显示 1 或 0 个我的项目,每次 LoadState 有变动的时候,咱们会告诉相应条目进行改变、插入或移除 (您能够在 拉取申请 中查看相应的代码)。
???? 更多对于 ConcatAdapter 的信息
ViewHolder
默认状况下,每个 Adapter 保护它们本人的 ViewHolder 池,在 Adapter 之间不会进行复用。但如果多个 Adapter 应用的是同一种 ViewHolder,咱们可能会想要在 Adapter 间复用 ViewHolder 的实例。咱们能够在结构 ConcatAdapter 时应用一个 ConcatAdapter.Config 对象来实现这样的成果。只有设置 isolateViewTypes = false,就能够让所有合并进来的 Adapter 应用同一个视图池。在显示加载状态的头部和底部的例子中,两种 ViewHolder 事实上应用的是雷同的内容,所以咱们能够复用它们。
⚠️ 如果要反对不同的 ViewHolder 类型,您应该实现 Adapter.getItemViewType) 办法。当您复用 ViewHolder 时,确保同一视图类型没有对应不同的 ViewHodler!防止出现这个问题的最佳实际之一,便是将布局 ID 作为视图类型返回。
<!-- Copyright 2019 Google LLC.
SPDX-License-Identifier: Apache-2.0 -->
class HeaderAdapter() : RecyclerView.Adapter<LoadingStateViewHolderHeaderViewHolder>() {override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {return LoadingStateViewHolder(parent)
}
override fun getItemViewType(position: Int): Int {
- return 0
+ return R.layout.list_loading
}
}
class FooterAdapter() : RecyclerView.Adapter<LoadingStateViewHolderFooterViewHolder>() {override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {return LoadingStateViewHolder(parent)
}
override fun getItemViewType(position: Int): Int {
- return 0
+ return R.layout.list_loading
}
}
应用 stable id
相比于应用 stable id 搭配 notifyDataSetChanged,咱们更倡议应用 Adapter 的特定告诉事件,该事件能够为 RecyclerView 提供更多无关数据集更改的信息,从而使 RecyclerView 能够更有效率地更新 UI,同时也有更好的动画成果。如果您正在应用 ListAdapter 的话,其外部会应用 DiffUtil 回调帮您解决告诉事件。然而如果您须要应用 stable id,ConcatAdapter.Config 为其提供了三种不同的配置: NO_STABLE_IDS、ISOLATED_STABLE_IDS 和 SHARED_STABLE_IDS。其中前面两种须要您本人解决 Adapter 中的 stable id。您能够查看 StableIdMode 文档来取得更多对于其工作原理的信息。
数据变更告诉
当 ConcatAdapter
中的一个 Adapter 调用了告诉函数时,ConcatAdapter
会在更新 RecyclerView
之前计算新的我的项目地位。
从 RecyclerView
的角度来看,notifyItemRangeChanged 示意更新的我的项目雷同,只是内容有所更改;notifyDataSetChanged 示意前后数据之间没有任何关系。因而,咱们无奈将 notifyDataSetChanged 映射到 notifyItemRangeChanged
中。
如果一个 Adapter 调用了 Adapter.notifyDataSetChanged
,则 ConcatAdapter
也会调用 Adapter.notifyDataSetChanged,而不是 Adapter.notifyItemRangeChanged。与 RecyclerViews 一样,咱们要抉择更精密的更新操作,个别状况下防止调用 Adapter.notifyDataSetChanged()。也能够应用主动执行此操作的 Adapter 实现,例如 ListAdapter 或 SortedList。
查找 ViewHolder 地位
您可能应用过 ViewHolder.getAdapterPosition) 来取得 Adapter 中某个 ViewHolder
的地位。当初,因为咱们合并了多个 Adapter,作为代替,您须要调用 ViewHolder.getBindingAdapterPosition())。如果您想在共享 ViewHolder
的状况下取得最初一个绑定某个 ViewHolder
的 Adapter,能够应用 ViewHolder.getBindingAdapter())。
以上就是全副了!总结一下: 如果要程序显示不同类型的数据的同时,也心愿这些数据可能封装在它们本人的 Adapter 中,请开始应用 ConcatAdapter
;如果想要更进一步对 ViewHolder
池和 statle id 进行高级管制,则要应用 ConcatAdapter.Config
。