As shown in the image, I would like the unit of the Detail item
to be changed at once according to the toggle button
.
Detail list items
were set as companion objects
because it was determined that it was not necessary to have a unit
property individually.
However, it seems that DiffUtil
determines that there is no change between the new list
and the old list
, perhaps because the unit
property is set as a companion object
.
So there is no update of the view either.
How can I make DiffUtil responsive while changing the companion object?
Detail
@Entity
data class Detail(
@PrimaryKey(autoGenerate = true)
var id: Int,
val set: Int,
var weight: String = "",
var reps: String = "") {
companion object {
var title: String = ""
var unit: String = "kg"
val memo = ""
}
}
ViewModel
class DetailViewModel(application: Application) : ViewModel() {
private val repository: DetailRepository
private val _items: MutableLiveData<List<Detail>> = MutableLiveData()
val items = _items
private val list: List<Detail>
get() = _items.value ?: emptyList()
init {
val detailDao = DetailDatabase.getDatabase(application)!!.detailDao()
repository = DetailRepository(detailDao)
}
fun changeUnit(unit: String) {
Detail.unit = unit
if(list == null)
return
_items.postValue(list) // To notify the observer.
}
fun addDetail() {
viewModelScope.launch(Dispatchers.IO){
val item = Detail(0, set = list.size 1)
repository.add(item)
// If use plus(), a new List is returned.
// Therefore, the change is notified to the Observer by postValue of the new list added.
_items.postValue(list.plus(item))
}
}
fun deleteDetail() {
// Delete the last set and return a new list to postValue to notify the Observer of the change.
_items.postValue(list.dropLast(1))
}
}
DiffUtil
class DetailDiffCallback : DiffUtil.ItemCallback<Detail>() {
override fun areItemsTheSame(
oldItem: Detail,
newItem: Detail
): Boolean {
return (oldItem.id == newItem.id)
}
override fun areContentsTheSame(
oldItem: Detail,
newItem: Detail
): Boolean {
return oldItem == newItem
}
}
Fragment
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
vm.items.observe(viewLifecycleOwner) { newList ->
adapter.submitList(newList)
}
}
CodePudding user response:
Detail list items were set as companion objects because it was determined that it was not necessary to have a unit property individually.
This is the root of the problem. If you want DiffUtil
to be able to "see" these changes, you will have to move this information out of the companion object.
DiffUtil
works by taking in two instances of your class and doing work (the areItemsTheSame()
and areContentsTheSame()
methods) to see if anything has changed. Since this information is part of the companion object, it will always be identical for all instances, which means there's no way for DiffUtil
to detect a change, even if one has happened.
CodePudding user response:
Like Ben P says, when you change the value in the companion object, that affects the entire class (since they all share that object). It's not included in the generated equals()
code for the data class - because why would it need to be? Every instance shares the same value, it's not part of the state!
Even if it were, when you compare oldList
and newList
, oldList
has still been "updated" with the new unit
value, because it's in that shared companion object. If you want oldList
and newList
to be able to have different values for unit
, they need to be per-instance properties.
But the way you're doing things here, the unit
doesn't even seem to be part of the data, right? It's not like you're storing a value and a unit of measure, and doing conversions when the displayed unit type changes. It looks like this is just a general display option, that just applies to how the data is displayed.
So in that case, why not just call notifyDataSetChanged()
on the adapter, or something similar? Force a redraw, let it show the new unit type, that's it. I'm not sure if you need to do anything special if you're using DiffUtil
, but that's what I'd look into.
(I feel like storing the unit type in the data would be a way better approach, seems important to what the stored number actually means, but for what you're doing right now, a display refresh should be enough)