androidandroid-recyclerviewnestedrecyclerview

Only highlight single item in nestes recycler view


I have a nested recycler view where the user should only be able to pick one single item inside the child recycler views. When an item is clicked on it is highlighted. Unfortunately the user is able to highlight an item in each child recycler view instead of just one overall. How can I achieve only one highlighted item?

Here is my code so far:

Parent onBindViewholder:

override fun onBindViewHolder(viewHolder: ParentViewHolder, position: Int) {
    //set the name of the parentitem
    val parentItem = parentData[position]
    viewHolder.ParentTitle.text = parentItem.ParentName

    // bind child adapter
    val childAdapter = ChildAdapter(parentData[position].childList, parentData[position].parentName)
    viewHolder.childRecycler.layoutManager = LinearLayoutManager(viewHolder.childRecycler.context, LinearLayoutManager.VERTICAL,false)
    viewHolder.childRecycler.adapter = childAdapter   
}

Here is my Child Adapter:

class ChildAdapter(
list: MutableList<ChildItem> = mutableListOf(),
ParentName : String

) : RecyclerView.Adapter<ChildAdapter.ChildViewHolder>() {


var ChildData: MutableList<ChildItem> = list.toMutableList()
    set(value) {
        field = value
        notifyDataSetChanged()
    }

var selectedItem = -1

/**
 * ViewHolderClass 
 */
inner class ChildViewHolder(view: View, ParrentName: String, childList : MutableList<ChildItem>) :
    RecyclerView.ViewHolder(view) {
    var childItem : LinearLayoutCompat

    init {
        childItem = view.findViewById(R.id.child_item)
        childItem.setOnClickListener {
            if (adapterPosition == RecyclerView.NO_POSITION) return@setOnClickListener;
         
            notifyItemChanged(selectedItem , 0)
            selectedItem = adapterPosition
            notifyItemChanged(selectedItem , 1)
        }
}


override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): ChildViewHolder {
    val viewHolder = LayoutInflater.from(viewGroup.context)
        .inflate(R.layout.child_item, viewGroup, false)

    return ChildViewHolder(viewHolder,parentName, childData)
}


override fun onBindViewHolder(viewHolder: ChildViewHolder, position: Int, payloads: MutableList<Any>) {
    
    val childItem = childData[position]
    if(payloads.isNotEmpty()){
        viewHolder.itemView.setBackgroundColor(if (selectedItem == position) Color.GREY else Color.WHITE)
    }
}


override fun onBindViewHolder(viewHolder: ChildViewHolder, position: Int) {
    val childItem = childData[position]
    viewHolder.itemView.setBackgroundColor(Color.WHITE)
}

By clicking on the item in the child recycler view the item is highlightes. But since the Parentrecycler viwe has multiple child recycler views, an item in evry hild can be highlighted. I'd like to reset the highlighting if the user change his selection from one child recycler into another child recycler.

Can anyone help with this? I'll tried with LiveData but never worked with it before and not quiet sure how to use it


Solution

  • I found a solution to my problem:

    1. For the Child Item I added a variable isSelected

      data class ChildItem(
       var ChildName : String,
       var isSelected: Boolean = false)
      
    2. Added code to the ChildAdapter so that only one item can be selected. Also by clicking the item again the item is selected/deselected:

      class ChildAdapter( list: MutableList = mutableListOf(), ParentName : String

       ) : RecyclerView.Adapter<ChildAdapter.ChildViewHolder>() {
      
      
       var ChildData: MutableList<ChildItem> = list.toMutableList()
           set(value) {
               field = value
               notifyDataSetChanged()
           }
      
      
      
       /**
        * ViewHolderClass 
        */
       inner class ChildViewHolder(view: View, ParrentName: String, childList : MutableList<ChildItem>) :
           RecyclerView.ViewHolder(view) {
           var childItem : LinearLayoutCompat
      
           init {
               childItem = view.findViewById(R.id.child_item)
               childItem.setOnClickListener {
                   if (adapterPosition == RecyclerView.NO_POSITION) return@setOnClickListener;
      
                  isAnyItemSelected(adapterPosition)
                  childData[adapterPosition].isSelected = !childData[adapterPosition].isSelected
                             if(childData[adapterPosition].isSelected){
                   childItem.setBackgroundColor(getRoleBackgroundColor())
               }else{
                   childItem.setBackgroundColor(Color.WHITE)
               }
      
      
                   if(childData[adapterPosition].isSelected) {
      
                       deselectChildItem()
      
               }
            private fun isAnyItemSelected(position: Int){
           if (position != RecyclerView.NO_POSITION){
      
               val nonCurrentPositionSelectedItem = childData.indexOfFirst {
                   it.isSelected
               }
      
               if (nonCurrentPositionSelectedItem >= 0 && nonCurrentPositionSelectedItem != position) {
                   if (childData[nonCurrentPositionSelectedItem].isSelected){
                       childData[nonCurrentPositionSelectedItem].isSelected = false
                   }
      
                   notifyItemChanged(nonCurrentPositionSelectedItem, 0)
               }
           }
       }
      
       /**
        * set background if item is deselected.
        */
       fun deselectItem() {
           locationPhysicalItem.setBackgroundColor(Color.WHITE)
      
       }
       }
      
      
       override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): ChildViewHolder {
           val viewHolder = LayoutInflater.from(viewGroup.context)
               .inflate(R.layout.child_item, viewGroup, false)
      
           return ChildViewHolder(viewHolder,parentName, childData)
       }
      
      
       override fun onBindViewHolder(viewHolder: ChildViewHolder, position: Int, payloads: MutableList<Any>) {
      
           val childItem = childData[position]
           if (payloads.isNotEmpty()) {
           viewHolder.deselectItem()
       }
       }
      
      
       override fun onBindViewHolder(viewHolder: ChildViewHolder, position: Int) {
           val childItem = childData[position]
           viewHolder.itemView.setBackgroundColor(Color.WHITE)
       }
      
    3. in the onClick of the ChildAdapter I added a function. in case that the parentItem is clicked, the isSelected from child items are updated and the adapter is notified:

       fun deselectItem(name:ChildItem){
       val adapter = ParentDialog?.parentRecycler?.adapter
       if(adapter != null) {
           val childList =
               (adapter as ParentAdapter).ParentData.flatMap { it.childList }
           for (item in childList) {
               if(item != name) {
                   if (item.isSelected) {
                       item.isSelected = false
                   }
               }
           }
           parentDialog?.ParentRecycler?.adapter?.notifyDataSetChanged()
       }
      

      }

    This works. But could be improved a lot. I iterate over the whole child lists and use notifyDataSetChange. A better approch would be to update just the childItem that was selected before and only update this item on the corresponding adapter in the Parent by using notifyItemChanged(index). Unfortunately I do not know at this time how to achieve this.