How to work with LiveData on RecyclerView?

Viewed 2119

**The problem is this: I press the button to delete an item in the list and it is deleted. But when I rotate the screen, the list is reloaded. The list does not save the data.

LiveData does not see changes.How to pass a new list with the removed element through it ? I spent 10 hours solving this problem (**

Adapter

class ItemAdapter(var context: Context, private var arrayList: MutableList<NumberModel>):RecyclerView.Adapter<ItemAdapter.ItemHolder>() {

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ItemHolder{

    val itemHolder = LayoutInflater.from(parent.context).inflate(
        R.layout.grid_layout_list_item,
        parent,
        false
    )
    return ItemHolder(itemHolder)
}

override fun onBindViewHolder(holder: ItemHolder, position: Int) {
    var positionOfNumber:NumberModel = arrayList.get(position)
    holder.textOfNumber.text = positionOfNumber.numberOfElement
   holder.button.setOnClickListener {
       var positionForDelete = holder.adapterPosition
       arrayList.removeAt(positionForDelete)
       notifyItemRemoved(positionForDelete)
       notifyItemRangeChanged(positionForDelete,arrayList.size)
   }
}
override fun getItemCount(): Int {
    return arrayList.size
}

class ItemHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
    var textOfNumber = itemView.findViewById<TextView>(R.id.numberTextView)
    var button:Button = itemView.findViewById(R.id.buttonClick)
}

}

MainActivity

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
    val list = mutableListOf<NumberModel>()
    adapter = ItemAdapter(this,list)
    recyclerView = findViewById(R.id.recyclerViewList)
    gridLayoutManager = GridLayoutManager(applicationContext,2,LinearLayoutManager.VERTICAL,false)
    recyclerView?.layoutManager = gridLayoutManager
    recyclerView?.setHasFixedSize(true)
    recyclerView?.adapter = adapter

    val model= ViewModelProvider(this).get(MainActivityViewModel::class.java)
    model.getListElements().observe(this, Observer {elementsSnapshot ->
        // Received elements from ViewModel
        list.clear()
        // Take new data from snapshot
        list.addAll(elementsSnapshot)
        adapter?.notifyDataSetChanged()
    })
}

ViewModel

class MainActivityViewModel : ViewModel() {
private val elementsList: MutableLiveData<MutableList<NumberModel>>

init {
    elementsList = MutableLiveData()
    elementsList.value = setElements()
}
fun getListElements() : LiveData<MutableList<NumberModel>>{
   return elementsList
}

private fun setElements() : MutableList<NumberModel> {
    val itemArrayList:MutableList<NumberModel> = ArrayList()
    itemArrayList.add(NumberModel("1"))
    itemArrayList.add(NumberModel("2"))
    itemArrayList.add(NumberModel("3"))
    itemArrayList.add(NumberModel("4"))
    itemArrayList.add(NumberModel("5"))
    return itemArrayList
}

}

1 Answers

Mutating object returned from your ViewModel is rarely a good idea. Reacting to event (such as user clicking your delete button) should be done in ViewModel. This is consistent with reactive UI style (data goes down, events go up) and will allow you to seperate your logic (ViewModel) from UI details (Activity, Adapter).

Your ViewModel could expose two elements:

class MyViewModel {
    private val localElements = MutableLiveData<List<...>>()

    val elements: LiveData<List<...>> 
        get() = localElements

    fun onElementClicked(index: Int) { 
        // get current value of localElements, make a copy of it with removed value at [index] and publish it to localElements
    }
}

Then, you can expose setElements method in your Adapter and just call it in response to viewModel.elements changes. You can pass instance of ViewModel to your Adapter and call onElementClicked when you detect the click.

This way, you can think of your Activity and Adapter as basically "stateless". When configuration changes, the valid state will be preserved in your ViewModel.

PS. Android documentation recommends retrieving your ViewModel instance during construction of your Activity/Fragment: https://developer.android.com/jetpack/guide?authuser=1#build-ui

Related