将您的class 转换为inner class 无疑是一种方法。我还会考虑将您的ViewHolder 引用传递给您提出的Listener 或Callback 接口(或一组函数)。如果您决定将 ViewHolder 移动到不同的文件或适配器之外,这将使重构更容易。它还更好地尊重单一职责原则,因为我们从 ViewHolder 中移除了数据管理,并让这种情况发生在其他地方。
例如:
class ViewHolder(
private itemView: View
private val onDataSetChanged: () -> Unit
) : RecyclerView.ViewHolder(itemView) {
fun onUpdate() {
// Perform changes...
onDataSetChanged()
}
}
虽然 ViewHolder 会像这样操作数据项,但对我来说有点奇怪。通常,这些只保留项目的位置信息和视图状态,而不是项目本身。另一种方法可能是将位置信息传递给更新 lambda:
class ViewHolder(
private itemView: View
private val onDataSetChanged: (Int) -> Unit
) : RecyclerView.ViewHolder(itemView) {
fun onShouldUpdate() {
if (adapterPosition == RecyclerView.NO_POSITION) return
onDataSetChanged(adapterPosition)
}
}
然后在其他地方处理该更新。例如,在适配器或 ViewModel 逻辑中:
fun onCreateViewHolder(...) {
val viewHolder = ViewHolder(...) { /* do update */ }
}
这让我们处于现在适配器既负责将数据映射到视图以及管理该数据的状态的地步......这不是很干净。
我喜欢这样做:
class Adapter(private val updateItem: (Item) -> Unit) {
fun onCreateViewHolder(...) {
return ViewHolder(...) { updateItem(items[it]) }
}
}
我们在更合理的地方处理数据操作。这里:
- ViewHolder 将单击或更新事件映射到它所持有的项目的位置
- 适配器将位置映射到实际数据项
- Presentation 对象(例如架构组件 ViewModel)(向适配器提供 lambda 的人)执行操作。 (更多内容见下文)
在这种情况下,您的 notifyDataSetChanged 将由演示者处理,然后反馈给适配器。例如,也许您在 Adapter 上有一个名为 setData(items: List<Item>) 的方法。当有新的项目列表可用时,演示者会通知适配器,适配器会设置项目:
// In your adapter
fun setData(newItems: List<Item>) {
val oldItems = items
items = newItems
notifyDataSetChanged()
// Alternatively, utilize DiffUtil
}
然后您的 ViewModel 可以公开一个可观察的字段,可能使用 LiveData。
class MyViewModel : ViewModel() {
val items: LiveData<List<Item>>
fun updateItem(item: Item) { ... }
}
您的 Activity(您在其中设置适配器)随后可以观察到对此的更改:
val adapter = Adapter(viewModel::updateItem)
// Note you could probably use a method reference instead of a lambda.
viewModel.items.observe(this) { adapter.setItems(it) }
我希望这有助于概述该方法。我可以整天谈论这个,所以请随时提出任何问题。这里的想法是,我们清楚地分离了我们的关注点 (SRP),不再需要将整个适配器交给 ViewHolder (ISP),并利用 lambdas 来确保我们不直接依赖于我们不需要的东西成为(DIP)。