Как наблюдать LiveData в адаптере RecyclerView в архитектуре MVVM?

У меня в пунктах RecyclerView переходник и кнопка.

Когда я нажимаю на кнопку, я хочу удалить его элемент с сервера, а затем с RecyclerView.

Я хочу сделать это, наблюдая за LiveData (когда он удален с сервера, я должен удалить его из представления ресайклера, поэтому мне нужен результат сервера)

Каков наилучший способ сделать это - я должен наблюдать во фрагменте и передавать слушателя адаптеру и реализовывать это во фрагменте, и когда пользователь нажимает кнопку, вызывает метод во фрагменте или есть лучший способ сделать это ?


person hadi mohammadi    schedule 09.01.2019    source источник
comment
Наблюдать за данными во фрагменте и передавать их адаптеру или уведомлять адаптер.   -  person Jeel Vankhede    schedule 09.01.2019
comment
Передать объект LifecycleOwner адаптеру?   -  person tango whiskey double    schedule 27.06.2019


Ответы (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()
            }
        })
    }
}
person Mohammad Asheri    schedule 16.01.2019
comment
почему вы создаете экземпляр своего адаптера каждый раз, когда наблюдаете данные! - person mxml; 20.07.2020

Я знаю, что уже поздно отвечать. Но я надеюсь, что это поможет другим разработчикам, ищущим решение аналогичной проблемы.

Взгляните на 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 и уведомлять адаптер.

person RBK    schedule 23.12.2020

Я думаю, что можно использовать notifyDataSetChanged. У меня есть компании как LiveData в моей ViewModel. Я могу наблюдать это во фрагменте onCreateView вот так

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

Когда есть какие-либо изменения в компаниях, recyclerView обновляется.

person flame3    schedule 23.02.2021