Little story of this topic : the app just updating clicked row's values with dialog when confirmed. Uses pagination scenario on room database.
When an item added or removed, the latest dataset is fetched and passed to submitList method, then all changes are seen and worked well.
The problem starts there, if an existing item updated, again the latest dataset is fetched properly and passed to submitList, but this time changes didn't seem.
When i debug the DIFF_CALLBACK
and caught my item in areItemsTheSame
, the newHistory
and oldHistory
values are same! (How!)
There could be any bug in submitList
method ?
After initializing, observe
fetches list from room and passes to mHistoryAdapter.submitList(it)
. Then if i update an item, observe gets triggered again(and i'm seeing updated value in param it
) and passes to submitList
.
Unfortunately, adapter wont change...
mResolvedAddressViewModel = ViewModelProviders.of(this).get(ResolvedAddressViewModel::class.java)
mResolvedAddressViewModel.getAddresses(false).observe(this, Observer {
mHistoryAdapter.submitList(it)
})
All the parts
Model
@Parcelize
@Entity
data class ResolvedAddress(
@PrimaryKey var id: String = UUID.randomUUID().toString(),
var requestedLat: Double = 0.0,
var requestedLon: Double = 0.0,
var requestedAddress: String = "",
var lat: Double,
var lon: Double,
var address: String,
var country: String,
var countryCode: String,
var city: String,
var alias: String? = null,
var favorite: Boolean = false,
var provider: String? = null,
var lastUseDate: Long = 0L) : Parcelable
Adapter
class HistoryAdapter(var context: Context)
: PagedListAdapter<ResolvedAddress, HistoryItemHolder>(DIFF_CALLBACK) {
companion object {
private val DIFF_CALLBACK = object : DiffUtil.ItemCallback<ResolvedAddress>() {
override fun areItemsTheSame(
oldHistory: ResolvedAddress, newHistory: ResolvedAddress): Boolean {
return oldHistory.id == newHistory.id
}
override fun areContentsTheSame(
oldHistory: ResolvedAddress, newHistory: ResolvedAddress): Boolean {
return oldHistory == newHistory
}
}
}
}
Fragment
class HistoryFragment : Fragment() {
private lateinit var mHistoryAdapter: HistoryAdapter
private lateinit var mResolvedAddressViewModel: ResolvedAddressViewModel
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.fragment_history, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
recyclerViewHistory.setHasFixedSize(true)
recyclerViewHistory.layoutManager = LinearLayoutManager(activity)
recyclerViewHistory.itemAnimator = DefaultItemAnimator()
mHistoryAdapter = HistoryAdapter(context!!)
recyclerViewHistory.adapter = mHistoryAdapter
mResolvedAddressViewModel = ViewModelProviders.of(this)
.get(ResolvedAddressViewModel::class.java)
mResolvedAddressViewModel.getAddresses(false).observe(this, Observer {
mHistoryAdapter.submitList(it)
})
}
}
There's a couple things missing from the question that could help provide a more detailed answer.
Ex. What does your RecyclerView.Adapter
look like? Does it extend PagedListAdapter
?
What does your model class look like? Is it a Kotlin data class?
For the sake of providing an answer, let's assume those unknowns are what we expect.
If I understand the question, it seems like you're just updating
an item and not removing or adding any items.
Therefore, the DiffUtil.ItemCallback
's areItemsTheSame
will always return true, because the old list and new list has not been modified in terms of their size.
Meaning, if you've updated an item, you've probably updated it's contents and not removed it from the list.
Therefore, areItemsTheSame
will return true, because their ids are still the same.
It's more likely that the second method, areContentsTheSame
will return false since you've updated the item's content.
If your model class, ResolvedAddress
, is a Kotlin data class, then the method areContentsTheSame
should return false when comparing the item that was updated from the old list and the new list. This should trigger the onBindViewHolder
method in your adapter at this point for you to rebind that item with the updated data.
If that model is not a Kotlin data class
, than you must make sure the class implements the compareTo
method. If not, you are comparing the object's memory address vs the actual contents of the object. If that is the case, the method areContentsTheSame
will always return true, since the object's memory address has not changed.
These are some debugging tips, as it is difficult to provide a clearer answer without more knowledge about how the code has been implemented.
I was having a similar issue but managed to fix it by updating the existing item with a new object rather than directly updating the existing item, as suggested by this answer here:
https://stackoverflow.com/a/54505078/10923311
The issue is with how submitList
processes changes. If you are passing a reference to the same list, it will not show updates as it determines it is the same datasource. In Kotlin if you want to update the sourceList and pass it back to submitList
, you can do so as follows:
submitList(originalList.toList().toMutableList().let {
it[index] = it[index].copy(property = newvalue) // To update a property on an item
it.add(newItem) // To add a new item
it.removeAt[index] // To remove an item
// and so on....
it
})
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With