viewHolder 类中的 notifyDataSetChanged

Ras*_*iri 2 android adapter kotlin android-recyclerview

我可以在视图持有者中使用 notifyDataSetChanged() 吗?

class ItemViewHolder(context: Context?, view: View) : RecyclerView.ViewHolder(view) {

       func update(){
           // ...
           // i need to update adapter for example
           adapter.notifyDataSetChanged()
       }

}
Run Code Online (Sandbox Code Playgroud)

Ale*_*art 5

把你class变成一个inner class肯定是一种方法。我也会考虑将您ViewHolder的引用传递给您提出的一个ListenerCallback接口(或一组函数)。如果您决定将 ViewHolder 移动到不同的文件或适配器之外,这将使重构更容易。它还更好地尊重单一职责原则,因为我们从 ViewHolder 中删除了数据管理,并让它在其他地方发生。

例如:

class ViewHolder(
    private itemView: View
    private val onDataSetChanged: () -> Unit
) : RecyclerView.ViewHolder(itemView) {

    fun onUpdate() {
        // Perform changes...
        onDataSetChanged()
    }

}
Run Code Online (Sandbox Code Playgroud)

虽然 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)
    }

}
Run Code Online (Sandbox Code Playgroud)

然后在其他地方处理该更新。例如,在 Adapter 或 ViewModel 逻辑中:

fun onCreateViewHolder(...) {
    val viewHolder = ViewHolder(...) { /* do update */ }
}
Run Code Online (Sandbox Code Playgroud)

这让我们现在处于适配器负责将数据映射到视图以及管理数据状态的地步……这不是很干净。

这是我喜欢这样做的方式:

class Adapter(private val updateItem: (Item) -> Unit) {
    fun onCreateViewHolder(...) {
        return ViewHolder(...) { updateItem(items[it]) }
    }
}
Run Code Online (Sandbox Code Playgroud)

我们在更合理的地方处理数据操作的地方。这里:

  1. ViewHolder 将点击或更新事件映射到它所持有的项目的位置
  2. 适配器将位置映射到实际数据项
  3. Presentation 对象(例如 Architecture Components 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
}
Run Code Online (Sandbox Code Playgroud)

然后,您的 ViewModel 可以公开一个可观察的字段,也许使用LiveData.

class MyViewModel : ViewModel() {
    val items: LiveData<List<Item>>
    fun updateItem(item: Item) { ... }
}
Run Code Online (Sandbox Code Playgroud)

您在其中设置适配器的 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) }
Run Code Online (Sandbox Code Playgroud)

我希望这有助于概述该方法。我可以整天谈论这个,所以请随时提出任何关于此的问题。这里的想法是,我们清楚地分离了我们的关注点 (SRP),不再需要将整个适配器交给 ViewHolder (ISP),并利用 lambdas 来确保我们不会直接依赖于我们不需要的东西成为(DIP)。