0

I have a recyclerView of elements that contain checkboxes. Currently when a checkbox is checked it goes to the last position and then if it is unchecked it goes to the top position (index 0). Therefore all unchecked are in a top section and those checked in a bottom section. I would like for it however to not go directly to the top or bottom when check/uncheck but to go to it's sorted position based on two criteria from the data class (checked(boolean) and order(int)).

For a clarification example I have 10 elements. There are currently 3 checked elements in the bottom section (who have order@ 1,3,5 respectively) and I just checked a new element (whose order# is 2) I want that element to go to the "checked" section and go directly to the correct slot (1,2,3,5) where 5 is in the last position. The same should go for unchecking an element. If I uncheck the element with order# 5, it should go to the top section of unchecked elements and go directly to it's correct order position of unchecked elements like (4,5,6,7,8,9,10).

In Adapter.kt

These are the methods I currently have:

            fun unchecked() {
                val toPosition = list.size - 1
                val item = list[holder.position]
                list.removeAt(holder.position)
                list.add(toPosition, item)
                notifyItemMoved(holder.position, toPosition)
            }

            fun checked() {
                val toPosition = 0
                val item = list[holder.position]
                list.removeAt(holder.position)
                list.add(toPosition, item)
                notifyItemMoved(holder.position, toPosition)
            }

I have tried to implement the following in within the above methods or as a standalone method called after and other ways, but getting issues where the elements duplicate or don't reflect their proper position and other weird stuff.

fun sort() {
   list.sortWith(compareBy<Model> { it.checked }.thenBy { it.order})
notifyDataSetChanged()
}

Thanks for any suggestions.

DJTalls
  • 1
  • 1

1 Answers1

0

Instead of interacting with the RecyclerView directly to remove or sort certain items, you could use the DiffUtil instead. It already has all this logic implemented and you only need to implement a Callback with a few methods to tell it how to check the items for equality. This will enable the RecyclerView and its adapter to detect changes to the dataset automatically whenever you pass new data to it.

The filtering itself could take place in your viewModel or presenter and just pass the filtered list on to the UI for display.

Example

Implement a DiffUtil.Callback:

class DiffUtilCallback(private val oldList: List<Any>, private val newList: List<Any>) :
        DiffUtil.Callback() {

        // old size
        override fun getOldListSize(): Int = oldList.size

        // new list size
        override fun getNewListSize(): Int = newList.size

        // if items are same
        override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
            val oldItem = oldList[oldItemPosition]
            val newItem = newList[newItemPosition]
            return oldItem.javaClass == newItem.javaClass
        }

        // check if contents are same
        override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
            val oldItem = oldList[oldItemPosition]
            val newItem = newList[newItemPosition]

            return oldItem.hashCode() == newItem.hashCode()
        }
    }

And use it to dispatch the updates to your RecyclerViewAdapter:

class YourAdapter : RecyclerView.Adapter {

   fun setNewData(newData: List<Any>) {
        val diffCallback = DiffUtilCallback(data, newData)
        val diffResult = DiffUtil.calculateDiff(diffCallback)
        data.clear()
        data.addAll(newData)
        diffResult.dispatchUpdatesTo(this)
    }

}

Source: How to use DiffUtils In RecyclerViewAdapter on Android

Markus
  • 1,649
  • 1
  • 22
  • 41
  • As long as you are using DiffUtil, you might as well instead of DiffUtil.Callback use DiffUtil.ItemCallback (which is easier to implement) with a ListAdapter so the changes in the RecyclerView will also be cleanly animated automatically. – Tenfour04 Jul 22 '23 at 12:40
  • I have looked into DiffUtil and haven't really figured it out yet as I'm fairly new and under a slight time crunch to produce. I have a ton of code using the "old" method (which was definitely a pain to figure out on it's own right) so it would be an undertaking to switch it over. Do either of you have any suggestions using the "old" method. I'll definitely research up DiffUtils more after. – DJTalls Jul 23 '23 at 15:48