【问题标题】:How to observe LiveData in RecyclerView adapter in MVVM architecture?如何在 MVVM 架构中观察 RecyclerView 适配器中的 LiveData?
【发布时间】:2023-06-08 09:42:01
【问题描述】:

我有一个RecyclerView 适配器和它的项目中的一个按钮。

当我点击按钮时,我想从服务器中删除它的项目,然后从RecyclerView 中删除。

我想通过观察LiveData 来做到这一点(当它从服务器中删除时,我必须将其从回收站视图中删除,因此我需要服务器的结果)

最佳实践方法是什么 - 我必须在片段中观察并将侦听器传递给适配器并在片段中实现它,当用户单击按钮时调用片段中的方法或有更好的方法这样做?

【问题讨论】:

  • 观察片段中的数据并将其传递给适配器或通知给适配器。
  • 将 LifecycleOwner 对象传递给适配器?

标签: android kotlin mvvm android-recyclerview android-livedata


【解决方案1】:

我认为使用 notifyDataSetChanged 是可以的。 我的 ViewModel 中有 LiveData 公司。我可以像这样在 onCreateView 片段中观察到它

 viewModel.companies.observe(viewLifecycleOwner, {
        binding.searchResultsRecyclerView.adapter?.notifyDataSetChanged()
    })

当公司发生任何变化时,recyclerView 会更新。

【讨论】:

    【解决方案2】:

    我知道现在回答为时已晚。 但我希望它能帮助其他开发人员寻找类似问题的解决方案。

    看看LiveAdapter

    你只需要在 Gradle 中添加最新的依赖即可。

    dependencies {
        implementation 'com.github.RaviKoradiya:LiveAdapter:1.3.2-1608532016'
        // kapt 'com.android.databinding:compiler:GRADLE_PLUGIN_VERSION' // this line only for Kotlin projects
    }
    

    并将适配器与您的 RecyclerView 绑定

    LiveAdapter(
                data = liveListOfItems,
                lifecycleOwner = this@MainActivity,
                variable = BR.item )
                .map<Header, ItemHeaderBinding>(R.layout.item_header) {
                    areContentsTheSame { old: Header, new: Header ->
                        return@areContentsTheSame old.text == new.text
                    }
                }
                .map<Point, ItemPointBinding>(R.layout.item_point) {
                    areContentsTheSame { old: Point, new: Point ->
                        return@areContentsTheSame old.id == new.id
                    }
                }
                .into(recyclerview)
    

    就是这样。适配器实现无需额外编写代码,观察LiveData并通知适配器。

    【讨论】:

      【解决方案3】:

      经过几篇文章的全面搜索,我终于找到了推荐的解决方案。 第 1 步:在您的适配器中声明一个接口,如下所示:

      class AddExpenseLabelAdapter(
          val items: List<LabelResponse>, 
          val context: Context, 
          val listener: OnLabelClickListener
      ) : RecyclerView.Adapter<AddExpenseLabelAdapter.ViewHolder>() {
      
          interface OnLabelClickListener {
              fun onLabelDeleteButtonClicked(request : SubCategoryLabelRequest)
          }
      
          lateinit var binding: ItemListExpenseAddLabelBinding
      
          override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
              val inflater = LayoutInflater.from(context)
              val binding = ItemListExpenseAddLabelBinding.inflate(inflater)
              this.binding = binding
              return ViewHolder(binding)
          }
      
          override fun onBindViewHolder(holder: ViewHolder, position: Int) {
              holder.bind(items[position])
          }
      
          override fun getItemCount(): Int = items.size
      
          inner class ViewHolder(val binding: ItemListExpenseAddLabelBinding) : RecyclerView.ViewHolder(binding.root), OnClickListener {
              lateinit var item: LabelResponse
              fun bind(item: LabelResponse) {
                  this.item = item
                  binding.itemListLabelLayout.setBackgroundColor(Color.parseColor("#" + item.color))
                  binding.labelResponse = item
                  binding.onClickListener = this
                  binding.executePendingBindings()
              }
      
              override fun onClick(view: View) {
                  if (view.id == binding.itemListLabelLayout.id) {
                      val subCategoryLabelRequest = SubCategoryLabelRequest(item.id)
                      listener.onLabelDeleteButtonClicked(subCategoryLabelRequest)
                  }
              }
          }
      }
      

      第 2 步:在视图中实现接口并将其传递给适配器,如下所示:

      class AddExpenseLabelDialog : DialogFragment(), AddExpenseLabelAdapter.OnLabelClickListener {
      
          lateinit var binding: DialogAddExpenseLabelBinding
      
          lateinit var view: Any
      
          var expenseId: Int = 0
          var categoryId: Int = 0
      
          lateinit var application: MyApplication
      
          lateinit var addExpenseLabelViewModel: AddExpenseLabelViewModel
      
          fun newInstance(expenseId: Int, categoryId: Int): AddExpenseLabelDialog = 
              AddExpenseLabelDialog().also { fragment ->
                  arguments = Bundle().also { bundle ->
                      bundle.putInt("expenseId", expenseId)
                      bundle.putInt("categoryId", categoryId)
                  }
              }
      
          override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
              binding = DataBindingUtil.inflate(layoutInflater, R.layout.dialog_add_expense_label, container, false)
              addExpenseLabelViewModel = ViewModelProviders.of(this).get(AddExpenseLabelViewModel::class.java)
              expenseId = arguments!!.getInt("expenseId")
              categoryId = arguments!!.getInt("categoryId")
              initialize()
              view = binding.root
              return view as View
          }
      
          fun initialize() {
      
              binding.labelRec.layoutManager = LinearLayoutManager(context)
              addExpenseLabelViewModel.liveData.observe(this, Observer { response ->
                  binding.labelRec.adapter = AddExpenseLabelAdapter(response as ArrayList<LabelResponse>, context!!, this)
              })
          }
      
          override fun onLabelDeleteButtonClicked(request : SubCategoryLabelRequest) {
              addExpenseLabelViewModel.createExpenseLabel(categoryId, expenseId, request).observe(this, Observer { response ->
                  when (response?.status) {
                      Status.LOADING -> Toast.makeText(activity, "LOADING", Toast.LENGTH_SHORT).show()
                      Status.SUCCESS -> {
                          dismiss()
                          Toast.makeText(activity, "SUCCESS", Toast.LENGTH_SHORT).show()
                      }
                      else -> Toast.makeText(activity, InjectorUtil.convertCodeToMessage(response?.error?.code!!), Toast.LENGTH_SHORT).show()
                  }
              })
          }
      }
      

      【讨论】:

      • 为什么每次观察数据时都要创建适配器实例!