0

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)
}
gautam
  • 522
  • 3
  • 14
  • What is your expected outcome? Which position do you want to obtain? The position of an object before or after filtering? – jsamol Jul 29 '19 at 09:45
  • I need to open the new activity onItem click and send the data of the clicked position. OnItem click is working fine when no filter is applied but when I am searching for a particular item in the list and click on that item that time I am not getting the required position and respective item from the list – gautam Jul 29 '19 at 10:34
  • From which list do you try to obtain that element with the position given from the `onItemClick` method? From the unfiltered list or from the filtered one? – jsamol Jul 29 '19 at 10:47
  • if no filter is applied then I need from unfiltered and if filter is applied then it should come from the filtered one. – gautam Jul 29 '19 at 11:07
  • That's strange. Your code works as expected on my machine. I thought maybe you were getting the position from the filtered list and then trying to use it on the unfiltered one. Could you provide any example consisting of data, filter text, both received position and item, and desired position? – jsamol Jul 29 '19 at 11:16
  • I think there is some misunderstanding between us due to my poor English i guess. let me clear, I want to achieve 1) want to display the list of vehicles 2) want to add search functionality in this list 3) after clicking on the list item, open new activity by passing the data on click position using intenet. I have achieved All these functionalities. now, if I will try to search vehicle by typing vehicle number in the edit text, and if there will be matching item in list and when i click on the item, I am not receiving correct vehicle number in the newly opened activity. – gautam Jul 29 '19 at 12:27
  • Check my answer and let me know if I got the problem correctly. – jsamol Jul 29 '19 at 12:57
  • same issue here, the interface is not called after filtering because the 'OnItemClickListener' is null after filtering in searchview. – صلي علي محمد - Atef Farouk Feb 23 '21 at 09:10

2 Answers2

0

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
}
jsamol
  • 3,042
  • 2
  • 16
  • 27
0

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;
            }