OnItem click not working properly after clicking on the item in Recyclerview

244 Views Asked by At

I have tried to implement search functionality in the recycler view.for that I have written below code in My "Main.kt" class

search functionality is working fine for me,

the issue is when I am searching the item from the list and clicking on that item I am getting the wrong position.

please help me with this issue.

adapter = DynamicListAdapter(dynamicList)
        dynamic_list_recyclerview.layoutManager = LinearLayoutManager(this, LinearLayout.VERTICAL,false) as RecyclerView.LayoutManager
        dynamic_list_recyclerview.adapter = adapter
        adapter!!.setRecyclerViewItemClickLister(this)

i have taken edit text and added the addTextChangedListener

edt_search_dynamic.addTextChangedListener(object : TextWatcher {
            override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
            }

            override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
            }
            override fun afterTextChanged(editable: Editable?) {
                filter(editable.toString())
            }
        })



fun filter(text : String){
    var filteredList =  mutableListOf<DynamicModel>()
    for(list in dynamicList){
        if(list.vehicleno.toLowerCase().contains(text.toLowerCase())){
            filteredList.add(list)
        }
    }
    adapter?.filterList(filteredList)

}

and this is my adapter class

class DynamicListAdapter (var dynamiclist : List<DynamicModel>) : RecyclerView.Adapter<DynamicListAdapter.DynamicViewHolder>()  {
    var recyclerViewOnItemClickListener : RecyclerViewOnItemClickListener? = null
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): DynamicViewHolder {
        val inflatedView = LayoutInflater.from(parent.context)
                .inflate(R.layout.singlerowdynamiclist,parent,false)
        return DynamicViewHolder(inflatedView,dynamiclist)
    }
    override fun getItemCount(): Int {
        return dynamiclist.size
    }

    override fun onBindViewHolder(holder: DynamicViewHolder, position: Int) {
        holder.bindItem(dynamiclist[position])
    }

    inner class DynamicViewHolder(itemView : View, var dynamiclists : List<DynamicModel>) : RecyclerView.ViewHolder(itemView), View.OnClickListener
    {
        private var txtStatus : TextView? = null
        init {
            txtStatus = itemView.findViewById(R.id.txtStatus)
            itemView.setOnClickListener(this)
        }
        fun bindItem(dynamiclist : DynamicModel){
            txtStatus?.text  = dynamiclist.vehiclestatus
        }
        override fun onClick(view: View?) {
            when(view){
                itemView -> if(recyclerViewOnItemClickListener!= null){
                    recyclerViewOnItemClickListener!!.onItemClick(adapterPosition,view)
                }
            }
        }
    }
    // method of outer class
    fun setRecyclerViewItemClickLister(onRecyclerviewItemClickListener : RecyclerViewOnItemClickListener){
        this.recyclerViewOnItemClickListener = onRecyclerviewItemClickListener
    }
    // for filter the list
    fun filterList(dymaniclist : List<DynamicModel>){
        this.dynamiclist = dymaniclist
        notifyDataSetChanged()
    }
}

and this is the interface which I have implemented in my Main.kt class

interface RecyclerViewOnItemClickListener {
    fun onItemClick(position : Int, view: View)
}
2

There are 2 best solutions below

0
jsamol On

You won't get the desired position (the position from the unfiltered list) after applying your filters, because you lose that information when creating a new filtered list and putting it inside the DynamicListAdapter.

Let's say inside the adapter we have a list of the following items:

val dynamicList = listOf(
    "#0 A", // 0th position
    "#1 B", // 1st position
    "#2 AB" // 2nd position
)

Now let's use your filter method:

filter("A")

This ends with replacing the old dynamicList inside the DynamicListAdapter with a completely new filteredList, which looks like this:

val filteredList = listOf(
    "#0 A",  // 0th position
    "#2 AB", // 1st position
)

Now when clicking the #2 AB you will get position of value 1 (not desired 2) as it is the item's index in the new filteredList.


There are multiple ways of how to achieve what you want, but if you only want data from the clicked item, just return the object instead of its position on the item click:

interface RecyclerViewOnItemClickListener {
    fun onItemClick(dynamicModel: DynamicModel, view: View)
}


// inside DynamicListAdapter
inner class DynamicViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {

    fun bindItem(dynamiclist : DynamicModel) {
        with (itemView) {
            txtStatus.text  = dynamiclist.vehiclestatus
            setOnClickListener { 
                recyclerViewOnItemClickListener?.onItemClick(dynamiclist, this)
            }
        }
    }        
}

However if you need to return the position and the above is not an option, I would suggest leaving the original list in the adapter and letting it handle filtering by itself, for example:

// _dynamicList is always the original list
class DynamicListAdapter (private val _dynamicList: List<DynamicModel>) : RecyclerView.Adapter<DynamicListAdapter.DynamicViewHolder>()  {
    var recyclerViewOnItemClickListener : RecyclerViewOnItemClickListener? = null

    var filterText: String = ""
        set(value) {
            field = value
            notifyDataSetChanged()
        }

    // return original list (_dynamicList) if no filter should be applied or return the filtered list based on the filterText
    private val dynamicList: List<DynamicModel>
        get() = if (filterText.isBlank()) _dynamicList
                else _dynamicList.filter { it.vehicleno.contains(filterText, ignoreCase = true) }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): DynamicViewHolder {
        val inflatedView = LayoutInflater.from(parent.context).inflate(R.layout.singlerowdynamiclist,parent,false)
        return DynamicViewHolder(inflatedView)
    }

    override fun getItemCount(): Int {
        return dynamicList.size
    }

    override fun onBindViewHolder(holder: DynamicViewHolder, position: Int) {
        holder.bindItem(dynamicList[position])
    }

    inner class DynamicViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
        fun bindItem(dynamicModel: DynamicModel) {
            with (itemView) {
                txtStatus.text = dynamicModel.vehiclestatus

                // get the item's original position (before filtering)
                val position = _dynamicList.indexOf(dynamicModel)

                setOnClickListener {
                    recyclerViewOnItemClickListener?.onItemClick(position, this)
                }
            }
        }
    }
}

// filter method
fun filter(text: String){
    adapter?.filterText = text
}
0
صلي علي محمد - Atef Farouk On

I put my solution it might help somebody.

Given: recyclerView is working fine.

Problem: on item click is not working after implementing searchview.

solution: add mAdapter.setOnItemClickListener again inside onQueryTextChange as below:

@Override
            public boolean onQueryTextChange(String newText) {
                
                mAdapter = new MyAdapter(getApplicationContext(), OtherInofYouWantToPass);
                recyclerView.setAdapter(mAdapter);

                mAdapter.setOnItemClickListener(new MyAdapter.OnItemClickListener() {
                    @Override
                    public void onItemClick(View view, MyObj obj, int position) {


                        { add your work here}

                    }


                });                    
                return false;
            }