Rifat
Rifat

Reputation: 1888

RecyclerView with Room db issue - Room db updated but recyvlerView is not showing all items correctly

I have set up recyclerView with room db liveData. I'm updating db and recyvlerView items changes but with inconsistency. When I recreate the recyclerView or relaunch the app, it shows all updated data correctly. I am facing this issue for months but could not find the cause.

Here is the Item for Db:

@Entity(tableName = "items_table")
data class MyItem(
    var title: String = "null",
) {
    @PrimaryKey(autoGenerate = true)
    var id: Long = 0L
}

liveData query thats observed:

@Query("SELECT * FROM items_table ORDER BY id DESC")
fun getAllItemsSortedByDate(): LiveData<List<MyItem>>

...
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertMyItem(myItem: MyItem)

MainActivity code part to observe and update recyclerView:

 ...

    setupRecyclerView()

    mainMyItemsViewModel.allItemsSortedByDate.observe(this) {
        myItemsAdapter.submitList(it)

        //issue is: nothing updates correctly on recyclerView unless view recreated.

        // myItemsAdapter.notifyItemChanged(0)
        myItemsAdapter.notifyDataSetChanged()

    }
   
  //insert on a floatingActionButton click. Then observe data change.
 binding.floatingActionButtonPool.setOnClickListener {
            CoroutineScope(Dispatchers.IO).launch {
                mainMyItemsViewModel.id++
                mainMyItemsViewModel.insertMyItem(MyItem("Hello World. ${mainMyItemsViewModel.id}"))
            }
        }
 ...

 private fun setupRecyclerView() = binding.recyclerViewPool.apply {
    myItemsAdapter = MyItemsAdapter()

    adapter = myItemsAdapter
    if (layoutManager == null)
        layoutManager = LinearLayoutManager(context, RecyclerView.VERTICAL, false)


}

The whole Adapter:

class MyItemsAdapter :
RecyclerView.Adapter<MyItemsAdapter.ViewHolder>() {
private var _binding: ItemsListPresenterBinding? = null // this just one textView in constraint layout
private val binding get() = _binding!!

private var selectedPosition: Int = 0
private var dataSet: List<MyItem> = mutableListOf()


/**
 * Provide a reference to the type of views that you are using
 * (custom ViewHolder).
 */
class ViewHolder(view: View) : RecyclerView.ViewHolder(view)

// Create new views (invoked by the layout manager)
override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): ViewHolder {
    _binding = ItemsListPresenterBinding.inflate(
        LayoutInflater.from(viewGroup.context),
        viewGroup, false
    )

    return ViewHolder(
        binding.root
    )
}


fun submitList(list: List<MyItem>) = run {

    dataSet = list

    //Log.d("Tag", " dataset size : " + dataSet.size.toString())


}

override fun onBindViewHolder(holder: ViewHolder, position: Int) {

     // I suspect this adapter is the issue.

    //holder.itemView.setBackgroundColor(if (selectedPosition == position) Color.GREEN else Color.DKGRAY)

    val item = dataSet[position] // also tested with adapterPosition...

    binding.textViewItem.text = item.title + "\nID: " + item.id


}


// Return the size of your dataset (invoked by the layout manager)
override fun getItemCount() = dataSet.size

}

The issue is, when I insert new item or update item, it does not show new item but keeps populating a duplicated previous item.

If required I'll upload the whole test project.

If this is enough to check the cause, and anyone know about the solution let me know the solution.

Image of the app showing the duplicacy issue:

enter image description here

Upvotes: 0

Views: 393

Answers (2)

LucBerge
LucBerge

Reputation: 41

I do have the same issue with my app. Tell me if I am wrong but since there is a button after the RecyclerView, I guess you are using a RecyclerView within a ScrollView. Right?

In my case, I had this weird behaviour where I can add items to the database, the observer is triggered but the RecyclerView is not showing the new items.

I found the solution on this thread: RecyclerView inside ScrollView is not working

If you are using a ScrollView, replace it with NestedScrollView .

Upvotes: 0

Jinal Patel
Jinal Patel

Reputation: 229

Update observer callback in MainActivity

mainMyItemsViewModel.allItemsSortedByDate.observe(this) {
        myItemsAdapter.submitList(it)
}

Update submitList method in your adapter

fun submitList(list: List<MyItem>) = run {

    dataSet.clear()
    dataSet.addAll(list)
    notifyDataSetChanged()
    //Log.d("Tag", " dataset size : " + dataSet.size.toString()
}

Adapter:

class MyItemsAdapter :
    RecyclerView.Adapter<MyItemsAdapter.ViewHolder>() {
    private var selectedPosition: Int = 0
    private var dataSet: List<MyItem> = mutableListOf()

    /**
     * Provide a reference to the type of views that you are using
     * (custom ViewHolder).
     */
    class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
        var binding = ItemsListPresenterBinding.bind(view)
    }

    // Create new views (invoked by the layout manager)
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        return ViewHolder(
            LayoutInflater.from(parent.context).inflate(R.layout.item_list_presenter, parent, false)
        )
    }

    fun submitList(list: List<MyItem>) {
        dataSet.clear()
        dataSet.addAll(list)
        notifyDataSetChanged()
    }

    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        // I suspect this adapter is the issue.
        //holder.itemView.setBackgroundColor(if (selectedPosition == position) Color.GREEN else Color.DKGRAY)
        val item = dataSet[position] // also tested with adapterPosition...
        holder.binding.textViewItem.text = item.title + "\nID: " + item.id
    }


    // Return the size of your dataset (invoked by the layout manager)
    override fun getItemCount() = dataSet.size

}

Upvotes: 1

Related Questions