2

I have a ParentData class structured as follows:

class ParentData(
//parent data fields 
var childList: List<ChildData>

I am populating a parent RecylerView with a list of ParentData and then populating each nested child RecyclerView with the embedded List<ChildData> in onBindViewHolder of the parent RecylerView Adapter (ListAdapter) like so:

val mAdapter = ChildAdapter()

binding.childRecyclerView.apply {
    layoutManager = LinearLayoutManager(this.context)
    adapter = mAdapter
}

mAdapter.submitList(item.childList)
//item from getItem(position)

I observe a LiveData<List<ParentData>>, so every time the embedded ChildData changes, I submit the new List<ParentData> to my parent recycler, which in turn calls OnBindViewHolder and submits the new `childList' and updates the inner child RecyclerView.

The issue is val mAdapter = ChildAdapter() is called every time the data is updated, resetting the entire child RecyclerView. This results in no item add animations being seen and scroll position being reset.

Is there any way to avoid initialising a new Adapter every time or some other way I can avoid resetting the child RecyclerViews?

SQLol
  • 227
  • 2
  • 4
  • 12

1 Answers1

0

I know this is an old question but I have worked on a project that is exactly like that and this answer might help others.

Basically we have a single RecyclerView approach where the main interface has a Parent RecyclerView that receives a list of Pair<ViewType, BaseViewBinder>.

class ParentAdapter: RecyclerView.Adapter<BaseViewHolder>() {

    private var mList: List<Pair<Int, BaseViewBinder>> = listOf() //Int is the ViewType

    fun updateData(mList: List<Pair<Int, BaseViewBinder>>) {
        if (this.mList!= mList) {
            this.mList = mList
            this.notifySetDataChanged()
...

We observe a MediatorLiveData that feeds a new list to the Parent RecyclerView every time there is new data.

The problem is that this.notifySetDataChanged() will update everything in the parent recyclerview. So the solution for the issue where child RecyclerViews "reset" and scroll back to beggining when a new List is received was solved by sending one extra variable to the ParentAdapter "updateData" function informing which view type in the list have changed, so we can "notifyItemChanged" only that specific index of the list, therefore not refreshing the entire recyclerview ui.

fun updateData(mList: List<Pair<Int, BaseViewBinder>>, sectionUpdated: Int) {
        if (this.mList!= mList) {
            this.mList = mList
            
            when(sectionUpdated){
               SECTION_A -> this.notifyItemChanged(mList.indexOfFirst { it.first == VIEW_TYPE_A })
               SECTION_B -> this.notifyItemChanged(mList.indexOfFirst { it.first == VIEW_TYPE_B })
               SECTION_C -> this.notifyItemChanged(mList.indexOfFirst { it.first == VIEW_TYPE_C })
            }

So basically you need to edit whatever function is generating your LiveData, check for differences in the new data, and return the list and an Int specifying that that specific child changed. Ex:

    private var mListA: List<X>
    
    fun returnSomeLiveData(): LiveData<Pair<Int, List<X>>> {
        var result = MutableLiveData<Pair<Int, List<X>>>()

        if (mListA != someLiveData.value) { //could be DiffUtils
            mListA = someLiveData.value

            result.postValue(Pair(0, mListA))
        }
        return result
    }

    companion object {
        val SECTION_A = 0
        val SECTION_B = 1
        val SECTION_C = 2
    }