How can I refresh the data source of ListAdapter at once after I launch submitList in Android?

915 views Asked by At

I use ListAdapter as the source of a RecyclerView, it will display a list of MVoice. The data source is mHomeViewModel.listVoiceBySort, it's LiveData<List<MVoice>>, you can see Code A.

After I added a record, mHomeViewModel.listVoiceBySort.observe will be launched, myAdapter.submitList(it) will be launched too.

But I found, the latest mHomeViewModel.listVoiceBySort isn't applied to ListAdapter, you can see Log A.

How can I refresh the data source of ListAdapter at once after I launch submitList ?

Log A

E/My: itemCount: 13 it: 14 CurrentList:13

Code A

binding.recyclerViewVoice.adapter = myAdapter
mHomeViewModel.listVoiceBySort.observe(this.viewLifecycleOwner) {
   myAdapter.submitList(it)
   Log.e("My","itemCount: "+myAdapter.itemCount+ " it: "+it.size+ " CurrentList:"+myAdapter.currentList.size)

}

class VoiceAdapters (private val aHomeViewModel: HomeViewModel, private val mPlay: PlayInterface):
        ListAdapter<MVoice, VoiceAdapters.VoiceViewHolder>(MVoiceDiffCallback()) {

    private lateinit var mContext: Context
    private lateinit var mLifecycleOwner:LifecycleOwner

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): VoiceViewHolder {
        mContext = parent.context
        mLifecycleOwner = mContext as LifecycleOwner

        return VoiceViewHolder(
            LayoutVoiceItemBinding.inflate(LayoutInflater.from(parent.context), parent, false).also {              
               it.lifecycleOwner = mLifecycleOwner
               it.aHomeViewModel = aHomeViewModel
            }
        )
    }

    override fun onBindViewHolder(holder: VoiceViewHolder, position: Int) {
        val inputMVoice = getItem(position)
        holder.bind(inputMVoice)
    }

   
    inner class VoiceViewHolder (private val binding: LayoutVoiceItemBinding):
          RecyclerView.ViewHolder(binding.root) {
       
        fun bind(inputMVoice: MVoice) {                        
            binding.amVoice = inputMVoice          
            binding.executePendingBindings()
        }
    }

}


class MVoiceDiffCallback : DiffUtil.ItemCallback<MVoice>() {
    override fun areItemsTheSame(oldItem: MVoice, newItem: MVoice): Boolean {
        return oldItem.id == newItem.id
    }

    override fun areContentsTheSame(oldItem: MVoice, newItem: MVoice): Boolean {
        return oldItem == newItem
    }
}

Added Content

To Tenfour04: Thanks!

The following Code B is how to get the LiveDate mHomeViewModel.listVoiceBySort.

Code B

class HomeViewModel(val mApplication: Application, private val mDBVoiceRepository: DBVoiceRepository) : AndroidViewModel(mApplication) {

    private val _listVoiceBySort=_sortBy.switchMap {
        mDBVoiceRepository.listVoiceBySort(it)
    }

    val listVoiceBySort: LiveData<List<MVoice>> =_listVoiceBySort
    ...
}

class DBVoiceRepository private constructor(private val mDBVoiceDao: DBVoiceDao){

    private val _isHaveRecord= MutableLiveData<Boolean>(false)
    val isHaveRecord:LiveData<Boolean> =_isHaveRecord

    fun listVoiceBySort(aSortBy: ESortBy): LiveData<List<MVoice>> {
        var query=when (aSortBy){
            ESortBy.StartPriority ->
               "SELECT * FROM voice_table ORDER BY starred desc, createdDate desc"
            ESortBy.DateDesc ->
               "SELECT * FROM voice_table ORDER BY createdDate desc"
            ESortBy.DateAsc ->
               "SELECT * FROM voice_table ORDER BY createdDate"
            ESortBy.TitleDesc ->
               "SELECT * FROM voice_table ORDER BY title desc"
            ESortBy.TitleAsc ->
               "SELECT * FROM voice_table ORDER BY title"
        }
        return mDBVoiceDao.runtimeQuery(SimpleSQLiteQuery(query)).map {
            _isHaveRecord.value =!it.isNullOrEmpty()
            it
        }
    }
    ...
}

@Dao
interface DBVoiceDao{   
   @RawQuery(observedEntities = [MVoice::class])
   fun  runtimeQuery(sortQuery: SupportSQLiteQuery): LiveData<List<MVoice>>
   ...
}


abstract class DBVoiceDatabase : RoomDatabase() {
   ...
}
3

There are 3 answers

2
CommonsWare On BEST ANSWER

submitList() is asynchronous. Calling other methods on myAdapter right away after submitList() may not reflect the list that you just submitted.

Instead, you could override onCurrentListChanged() and look at the adapter at that point.

Or, just count the rows that you see on the screen.

3
mehmet salim ayan On

You should change your submit code like

mHomeViewModel.listVoiceBySort.observe(this.viewLifecycleOwner) {
   (binding.recyclerViewVoice.adapter as YourAdapter).submitList(it.map { it.copy() })
   Log.e("My","itemCount: "+myAdapter.itemCount+ " it: "+it.size+ " CurrentList:"+myAdapter.currentList.size)

}

You submit your list reference to adapter and when something changes it already have that changes inside adapter and diffutil can't find any differences. You can fix that issue with copy items of the list while submitting.

0
Remc4 On

If you want to use ListAdapter, but change submitList() to submit list updates synchronously instead of async, you can create your own backing list and override a few methods like this:

inner class MyListAdapter() : ListAdapter<MyListItem, MyViewHolder>(MyDiffUtil) {
    private var currentList: List<MyListItem> = listOf()
    override fun getCurrentList(): List<MyListItem> = currentList
    override fun getItemCount(): Int = currentList.size
    override fun getItem(position: Int) = currentList[position]

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
        val binding = MyItemBinding.inflate(layoutInflater, parent, false)
        return MyViewHolder(binding)
    }

    override fun onBindViewHolder(holder: ProjectViewHolder, position: Int) {
        holder.bind(currentList[position])
    }

    override fun submitList(list: List<MyListItem>?) {
        submitList(list, null)
    }

    override fun submitList(list: List<MyListItem>?, commitCallback: Runnable?) {
        requireNotNull(list)

        //Your implementation of DiffUtil goes here. It will probably be the same as
        //MyDiffUtil in the first line of this code snippet.
        val diff = DiffUtil.calculateDiff(object : DiffUtil.Callback() {
            override fun getOldListSize() = currentList.size

            override fun getNewListSize() = list.size

            override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int) =
                currentList[oldItemPosition].myItemId == list[newItemPosition].myItemId

            override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int) =
                false

            override fun getChangePayload(oldItemPosition: Int, newItemPosition: Int) =
                "Rebind-Payload"
        })

        diff.dispatchUpdatesTo(this)
        currentList = list.toMutableList()

        commitCallback?.run()
    }
}

Note: this is an inner class, so that we can call layoutInflater on the Fragment where this is used.