Home > Software design >  RecyclerView does not update the screen
RecyclerView does not update the screen

Time:01-03

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 objects instead of classes so you don't have to call their constructors or have multiple instances of them hanging around.

  • Related