Search code examples
androidandroid-studiokotlinandroid-recyclerviewandroid-spinner

How to properly select and deselect items in recycler view?


I am very beginner to Android Kotlin, I am developing a practice app which requires a selected data from recycler view to be in an arraylist. Selection works fine until I change my spinner value and items get reloaded, once items get reloaded it take two clicks to deselect but is still not removed from the selected data arrayList. (whenever spinner value is changed, data is loaded from API). I know this code is messy and has a lot of unnecessary stuff, if someone could help me with how to clean the code and solve the issue, will be very glad.

class RecyclerViewAdapter(val dataList:ArrayList<ModelClass>):RecyclerView.Adapter<RecyclerViewAdapter.ViewHolder>(){

   object ob {
        val dataSelected= ArrayList<ModelClass>()

   }

private  var _binding: ItemViewBinding? = null
private val binding get() = _binding!!
private lateinit var nListener : onItemClickListener

interface onItemClickListener{
    fun onItemClick(position: Int)

}
fun setOnItemClickListener(listener:onItemClickListener){
    nListener=listener
}

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerViewAdapter.ViewHolder {

    val v=LayoutInflater.from(parent.context).inflate(R.layout.item_view,parent,false)
    _binding= ItemViewBinding.bind(v)

    return ViewHolder(binding.root)
}
fun bindItems(data:ModelClass){


    binding.itemQuant.text=data.item_quant
    binding.itemName.text=data.item_name
    binding.mfgName.text=data.mfg
    binding.quantity.text=data.item_stock.toString()



}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
    holder.restore()
    bindItems(dataList[position])
    holder.select()



}

@SuppressLint("ResourceAsColor")
inner class ViewHolder(itemView: View):RecyclerView.ViewHolder(itemView) {

   fun restore(){


       for (i in 0 until ob.dataSelected.size){
           for (j in 0 until dataList.size){
                if (ob.dataSelected[i].sku_code==(dataList[j]).sku_code) {


                        if(adapterPosition == j){

                            itemView.isSelected = true
                            itemView.setBackgroundColor(R.color.black)
                            println("****")

                        }
                }

           }
       }

   }


 fun select(){

        itemView.setOnClickListener {



            val position: Int = adapterPosition
            if (ob.dataSelected.contains(dataList[position]) ){
                    itemView.setBackgroundResource(0)
                    itemView.isSelected = false
                 ob.dataSelected.remove(dataList[position])

                for (i in 0 until ob.dataSelected.size){
                    println(ob.dataSelected[i].sku_code)
                }
            }
            else {
                        itemView.isSelected = true
                        itemView.setBackgroundColor(R.color.black)
                        ob.dataSelected.add((dataList.get(position)))
                for (i in 0 until ob.dataSelected.size){
                    println(ob.dataSelected[i].sku_code)
                }

            }
        }


 }



}


override fun getItemCount(): Int {
    return dataList.size
}
override fun getItemId(position: Int): Long = position.toLong()

} . . . . . . edit:

class RecyclerViewAdapter(val dataList:ArrayList,val onItemClicked: (Int) -> Unit):RecyclerView.Adapter<RecyclerViewAdapter.ViewHolder>(){

object ob { val dataSelected= ArrayList()

}

private var checkedPosition = -1


fun setData(listModel: List<ModelClass>) {
    dataList.clear()
    dataList.addAll(listModel)
    notifyDataSetChanged()
}




override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
    val binding = ItemViewBinding.inflate(
        LayoutInflater.from(parent.context), parent, false
    )
    return ViewHolder(binding, parent.context)
}


override fun onBindViewHolder(holder: ViewHolder, position: Int) {
    holder.bindItems(dataList[position])
}

override fun getItemCount() = dataList.size

inner class ViewHolder(
    val binding: ItemViewBinding,
    val context: Context
): RecyclerView.ViewHolder(binding.root) {
    @SuppressLint("ResourceAsColor", "ResourceType")
    fun bindItems(data: ModelClass) = with(binding) {
        /*
        * Map Your data Here
        * example: tvTitle.text = model.title
        * */
        binding.itemQuant.text=data.item_quant
        binding.itemName.text=data.item_name
        binding.mfgName.text=data.mfg
        binding.quantity.text=data.item_stock.toString()

        when (checkedPosition) {
            -1 -> {
                itemView.setBackgroundResource(0)
            }
            else -> when (checkedPosition) {
                adapterPosition -> {
                    itemView.setBackgroundColor(R.color.black)

                }
                else -> {
                    itemView.setBackgroundResource(0)
                }
            }
        }
        root.setOnClickListener {
           itemView.setBackgroundResource(R.color.black)
            if (checkedPosition != adapterPosition) {
                notifyItemChanged(checkedPosition)
                checkedPosition = adapterPosition
            }

            // Handle the clicked item

            if (ob.dataSelected.contains(dataList[adapterPosition])){
                ob.dataSelected.remove(dataList[adapterPosition])
                itemView.isSelected=false
                for (i in ob.dataSelected){
                    println(i.sku_code)
                }
                itemView.setBackgroundColor(R.color.black)
            }
            else {
                ob.dataSelected.add(dataList[adapterPosition])
                itemView.isSelected=true
                for (i in ob.dataSelected){
                    println(i.sku_code)
                }
                itemView.setBackgroundResource(0)
            }
            onItemClicked.invoke(adapterPosition)
        }
    }
}

}


Solution

  • here try this one, i tried refactoring your code

    class RecyclerViewAdapter(
        val onItemClicked: (Int) -> Unit
    ) : RecyclerView.Adapter<RecyclerViewAdapter.ViewHolder>() {
    
        private val data = ArrayList<ModelClass>()
        private var checkedPosition = -1 // -1: No Item Selected, 0: First Item Selected
    
        fun setData(listModel: List<ModelClass>) {
            data.clear()
            data.addAll(listModel)
            notifyDataSetChanged()
        }
    
        override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
            val binding = ItemViewBinding.inflate(
                LayoutInflater.from(parent.context), parent, false
            )
            return ViewHolder(binding, parent.context)
        }
    
        override fun onBindViewHolder(holder: ViewHolder, position: Int) {
            holder.bindItems(data[position])
        }
    
        override fun getItemCount() = data.size
    
        inner class ViewHolder(
            val binding: ItemViewBinding,
            val context: Context
        ): RecyclerView.ViewHolder(binding.root) {
            fun bindItems(model: ModelClass) = with(binding) {
                /*
                * Map Your data Here
                * example: tvTitle.text = model.title
                * */
    
                when (checkedPosition) {
                    -1 -> {
                        clLayout.setBackgroundResource(0)
                    }
                    else -> when (checkedPosition) {
                        absoluteAdapterPosition -> {
                            clLayout.setBackgroundResource(R.drawable.bg_curved_grey)
                        }
                        else -> {
                            clLayout.setBackgroundResource(0)
                        }
                    }
                }
                root.setOnClickListener {
                    clLayout.setBackgroundResource(R.drawable.bg_curved_grey)
                    if (checkedPosition != absoluteAdapterPosition) {
                        notifyItemChanged(checkedPosition)
                        checkedPosition = absoluteAdapterPosition
                    }
                    // Handle the clicked item 
                    onItemClicked.invoke(absoluteAdapterPosition)
                }
            }
        }
    }
    

    here how you use it in Your Activity or fragment

    private val recyclerViewAdapter: RecyclerViewAdapter by lazy {
        //RecyclerViewAdapter(this::onClickedItem) you can call the function below like this or
        //RecyclerViewAdapter { onClickedItem(it) } or
        RecyclerViewAdapter { position ->
          onClickedItem(position ) 
        }
    }
    
    private fun onClickedItem(position: Int) {
        //do something
        Toast.makeText(this, position.toString, Toast.LENGTH_SHORT).show()
    }
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(binding.root)
    
        binding.recyclerview.apply {
            layoutManager = LinearLayoutManager(this)
            adapter = recyclerViewAdapter
        }
    
        // yourdata = dataList:ArrayList<ModelClass>
        recyclerViewAdapter.setData(yourData)
    }
    

    some explanation

    its a Higher order function, you can read it more here Higher order function

    val onItemClicked: (Int) -> Unit
    

    with this piece of code we can remove this

    private lateinit var nListener : onItemClickListener
    
    interface onItemClickListener{
        fun onItemClick(position: Int)
    
    }
    fun setOnItemClickListener(listener:onItemClickListener){
        nListener=listener
    }
    

    with this piece of code

    fun setData(listModel: List<ModelClass>) {
        data.clear()
        data.addAll(listModel)
        notifyDataSetChanged()
    }
    

    we make the adapter flexible with any data set List<ModelClass>