I'm working on adding an item via a button.
I used ListAdapter
and DiffUtil
.
However, when the button is pressed, the first item is updated, but the screen does not update after that.
I tried debugging
with Adapter
and DiffUtil
.
I checked whether the List
was submitted
well by using the currentList
in the Adapter, but the break point works only on the first item and does not stop for the items added after that.
In DiffUtil
, we confirmed that break point
does not work at all.
What code did I write wrong?
Fragment
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
_binding = FragmentWriteRoutine2Binding.inflate(inflater, container, false)
adapter = DetailAdapter()
binding.apply {
rv.adapter = adapter
rv.itemAnimator = null
header.add.setOnClickListener {
vm.addDetail()
}
header.delete.setOnClickListener {
vm.deleteDetail()
}
}
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
vm.items.observe(viewLifecycleOwner) { newList ->
adapter.submitList(newList)
}
}
RoutineItem
sealed class RoutineItem(
val id: String,
) {
class Header(
id: String = UUID.randomUUID().toString(),
val workout: String,
val unit: String,
) : RoutineItem(id)
class Detail(
id: String = UUID.randomUUID().toString(),
val set: Int,
var weight: String = "",
var reps: String = "",
) : RoutineItem(id)
}
Adapter
class DetailAdapter
: ListAdapter<RoutineItem, DetailAdapter.ViewHolder>(RoutineDiffCallback()) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
return ViewHolder(
ItemRoutineDetailBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val cur = currentList
}
inner class ViewHolder(val binding: ItemRoutineDetailBinding) : RecyclerView.ViewHolder(binding.root) {
fun bind(item: RoutineItem.Detail) {
}
}
}
DiffUtilCallback
class DetailDiffCallback : DiffUtil.ItemCallback<RoutineItem>() {
override fun areItemsTheSame(
oldItem: RoutineItem,
newItem: RoutineItem
): Boolean = (oldItem.id == newItem.id)
override fun areContentsTheSame(
oldItem: RoutineItem,
newItem: RoutineItem
): Boolean = (oldItem == newItem)
}
CodePudding user response:
Since you are not using data
classes for your Header and Detail classes, their equals()
functions are inadequate for using ==
in your DiffUtil.ItemCallback
. A data
class automatically compares all its constructor properties in equals
, but a regular class does not. You can either manually write proper equals
and hashcode
implementations for them (IntelliJ IDEA can generate it for you), or you can change them to data classes.
Also, sealed interfaces are a bit cleaner to use than sealed classes in most cases. Probably the reason there are so many examples of sealed classes in Kotlin is that sealed interfaces were not supported until relatively recently (Kotlin 1.5).
Here's how you can define your item classes:
sealed interface RoutineItem {
val id: String
data class Header(
override val id: String = UUID.randomUUID().toString(),
val workout: String,
val unit: String,
) : RoutineItem
data class Detail(
override val id: String = UUID.randomUUID().toString(),
val set: Int,
var weight: String = "",
var reps: String = "",
) : RoutineItem
}
Other than that, the DetailAdapter code you shared looks very incomplete. It makes no actual attempt to bind any data to the views.
Another small tip: Since DiffUtil.ItemCallbacks typically do not carry any state, you can define them as object
s instead of class
es so you don't have to call their constructors or have multiple instances of them hanging around.