viewHolder类中的notifyDataSetChanged

时间:2019-07-16 11:37:41

标签: android kotlin android-recyclerview adapter

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

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

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

}

2 个答案:

答案 0 :(得分:2)

class转换为inner class当然是一种方法。我还将考虑向您的ViewHolder传递对您提出的ListenerCallback接口(或一组函数)的引用。如果您决定将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)
    }

}

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

fun onCreateViewHolder(...) {
    val viewHolder = ViewHolder(...) { /* do update */ }
}

这使我们处于一个阶段,现在适配器负责将数据映射到视图以及管理数据的状态……这不是很干净。

这就是我喜欢的方式:

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

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

  1. ViewHolder将点击或更新事件映射到它所持有的项目的位置
  2. 适配器将位置映射到实际数据项
  3. Presentation对象(例如Architecture Components ViewModel)(whomever为适配器提供lambda)执行操作。 (有关更多信息,请参见下文)

在这种情况下,演示者将处理您的notifyDataSetChanged,然后将其反馈到适配器中。例如,也许您在适配器上有一个名为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) { ... }
}

您的活动(在其中设置适配器的位置)可以观察到对此的更改:

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)的需要,并利用了lambda来确保我们不直接依赖于不需要的东西成为(DIP)。

答案 1 :(得分:1)

在您的 View Holder 中实现一个 onclick 事件侦听器,然后像这样删除一个 View holder。

public void onClick(View v) {
            int id = v.getId();
            switch (id) {
                case R.id.musicPlay:
                    Intent intent = new Intent(Intent.ACTION_VIEW);
                    intent.setType("audio/*");
                    intent.setData(Uri.parse(mItem.getContentUri()));
                    context.startActivity(intent);
                    break;
                case R.id.musicShare:

                    Toast.makeText(context, "Music is shared", Toast.LENGTH_SHORT).show();
                    break;
                case R.id.musicDelete:
                    if (context.getContentResolver().delete(Uri.parse(mItem.getContentUri()), null, null)>0)
                        Toast.makeText(context, mItem.getTitle() + " is deleted", Toast.LENGTH_SHORT).show();

                    MediaScannerConnection.scanFile(context, new String[]{

                                    mItem.getData()},

                            null, new MediaScannerConnection.OnScanCompletedListener() {

                                public void onScanCompleted(String path, Uri uri) {
                                    mValues.remove(mItem);
                                    notifyDataSetChanged();
                                }

                            });
                    break;
            }
        }