How to add Search in RecyclerView using Kotlin

Today, I will show you how to search for items in a RecyclerView using SearchView.

In this example, we have a RecyclerView already set up, and the only thing we do is to add the searching functionality.

We have a list of countries as sample data and using the search field we get the country we want faster.

Adding SearchView in RecyclerView

Add the SearchView in your layout, inside a LinearLayout with a vertical orientation.

<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/country_content_id" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@color/colorPrimaryDark" android:orientation="vertical" tools:context=".MainActivity"> <androidx.appcompat.widget.SearchView android:id="@+id/country_search" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@color/colorPrimary" android:textCursorDrawable="@null" app:iconifiedByDefault="false" app:queryBackground="@null" /> <androidx.recyclerview.widget.RecyclerView android:id="@+id/country_rv" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@color/colorPrimaryDark" /> </LinearLayout>
Code language: HTML, XML (xml)

In the RecyclerView adapter (In this example, RecyclerView_Adapter.kt), create an ArrayList with a name countryFilterList, and pass all the items..

class RecyclerView_Adapter(private var countryList: ArrayList<String>): RecyclerView.Adapter<RecyclerView.ViewHolder>() { var countryFilterList = ArrayList<String>() init { countryFilterList = countryList } // ... }
Code language: Kotlin (kotlin)

Return the size of countryFilterList so every time will return the right amount of items that match the characters you typing in the SearchView.

class RecyclerView_Adapter(private var countryList: ArrayList<String>): RecyclerView.Adapter<RecyclerView.ViewHolder>() { // ... override fun getItemCount(): Int { return countryFilterList.size } // ... }
Code language: Kotlin (kotlin)

Now, in the onBindViewHolder get the item for each row from the countryFilterList array.

class RecyclerView_Adapter(private var countryList: ArrayList<String>): RecyclerView.Adapter<RecyclerView.ViewHolder>() { // ... override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { // ... holder.itemView.select_country_text.text = countryFilterList[position] // ... } }
Code language: Kotlin (kotlin)

Add the Filterable class in the RecyclerView_Adapter and the method getFilter()

class RecyclerView_Adapter(private var countryList: ArrayList<String>): RecyclerView.Adapter<RecyclerView.ViewHolder>(), Filterable { override fun getFilter(): Filter { } }
Code language: Kotlin (kotlin)

Inside the getFilter() method, return a Filter() object:

class RecyclerView_Adapter(private var countryList: ArrayList<String>): RecyclerView.Adapter<RecyclerView.ViewHolder>(), Filterable { override fun getFilter(): Filter { return object : Filter() { override fun performFiltering(constraint: CharSequence?): FilterResults { } @Suppress("UNCHECKED_CAST") override fun publishResults(constraint: CharSequence?, results: FilterResults?) { } } } }
Code language: Kotlin (kotlin)

The performFiltering method checks if we have typed a text in the SeachView.

If there is not any text, will return all items.

If there is a text, then we check if the characters match the items from the list and return the results in a FilterResults type.

The publishResults get these results, passes it to the countryFilterList array and updates the RecyclerView.

class RecyclerView_Adapter(private var countryList: ArrayList<String>): RecyclerView.Adapter<RecyclerView.ViewHolder>(), Filterable { override fun getFilter(): Filter { return object : Filter() { override fun performFiltering(constraint: CharSequence?): FilterResults { val charSearch = constraint.toString() if (charSearch.isEmpty()) { countryFilterList = countryList } else { val resultList = ArrayList<String>() for (row in countryList) { if (row.toLowerCase(Locale.ROOT).contains(charSearch.toLowerCase(Locale.ROOT))) { resultList.add(row) } } countryFilterList = resultList } val filterResults = FilterResults() filterResults.values = countryFilterList return filterResults } @Suppress("UNCHECKED_CAST") override fun publishResults(constraint: CharSequence?, results: FilterResults?) { countryFilterList = results?.values as ArrayList<String> notifyDataSetChanged() } } } }
Code language: Kotlin (kotlin)

Go back to the Activity file (MainActivity.kt) and add the setOnQueryTextListener.

class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) // ... country_search.setOnQueryTextListener(object: SearchView.OnQueryTextListener{ override fun onQueryTextSubmit(query: String?): Boolean { return false } override fun onQueryTextChange(newText: String?): Boolean { adapter.filter.filter(newText) return false } }) // ... } }
Code language: Kotlin (kotlin)

The onQueryTextChange method called every time we typing on the SearchView and updates the RecyclerView with the new results.

Customizing the SearchView

If you want to change the color of the search icon (magnifying glass):

class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) // ... val searchIcon = country_search.findViewById<ImageView>(R.id.search_mag_icon) searchIcon.setColorFilter(Color.WHITE) // ... } }
Code language: Kotlin (kotlin)

For the color of the cancel button:

class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) // ... val cancelIcon = country_search.findViewById<ImageView>(R.id.search_close_btn) cancelIcon.setColorFilter(Color.WHITE) // ... } }
Code language: Kotlin (kotlin)

And to change the text color of the TextView:

class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) // ... val textView = country_search.findViewById<TextView>(R.id.search_src_text) textView.setTextColor(Color.WHITE) // If you want to change the color of the cursor, change the 'colorAccent' in colors.xml // ... } }
Code language: Kotlin (kotlin)
You can find the final project here

If you have any questionsplease feel free to leave a comment below

Subscribe
Notify of
guest
58 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
akash

charSearch not defined?

akash

and also adapter.filter.filter?

Anuj

I can not get the adapter.filter.filter(newText) to work, would you be able to assist. Its highlighted in red and calls it an unsolved reference. I tried the workaround you posted in the comments and that also presents the same issue. The middle filter is a problem. I am using this in conjunction with an api connection to get the country names, but i do not see how it would present such an issue.

Mohammed

The (constraint) in the “performFiltering” method is not recognized ..

Mohammed

Yes, I did.
Sorry, the problem was in the parameter’s name which was “p0” not “constraint”.
Thank you.

Mohammed

Thank you very much

Lovanto

I got this problem lateinit property adapter has not been initialized How to fix it ?

Everton Rodrigues

HI, i cannot use toLowerCase() on row, do you have any idea why? toLowerCase() gives unresolved reference and “type mismatch”. I’m stuck on this part, please, help

Everton Rodrigues

I used toString before the toLowerCase() and it looks like it worked out. Thank you.

Everton Rodrigues

Looks like i can’t use resultList.add(row) if my class is Array(Class) and not a ArrayList(Class) 🙁

Everton Rodrigues

If i use ArrayList, List or MutableList, my Gson crashs telling me that it expected an object and got an array. Strangely, if i use an Array<MyClass>, it works and shows my data on the RecyclerView.

Dea Malaha

what if i use listOf<ToDo> ?

Dea Malaha

listOf<MyClass> . please explain to me i’m new to kotlin 🙂

Dea Malaha

No, not the .toLowerCase(), but on the resultList.add(row). Apparently i can’t use it too if i’m using listOf.

Dea Malaha

Yes, it is a data class. it kinda looks something like that. it’s a data class that carry Entity for my room database. I’m sorry if i can’t explain what i mean very well but I hope you understand. In my adapter class, i’m using listOf<ToDo>

Dea Malaha

Thank you so much. But one last question, what about the MainActivity? I mean i’m using room database, so it search for the things i want it to seach (for example the goal that you’ve just said). How should i write the function that do the filter? Because I’m seeing that you are not using room as i do so it must be kinda different than what you do in your MainActivity

Roshani

my item gets double

Roshani

no i am checking the list is correct. after that i tries with removeallviewinlayout this is working for one to two search then again some unrelated data get inflate.

Roshani

Let me try this

Joao Pedro

Where does the adapter in adapter.filter.filter(newText) comes from?
It’s highlighted in red and asks to create a variable adapter.

Codex
java.lang.NullPointerException: Attempt to invoke virtual method 'void androidx.appcompat.widget.SearchView.setOnQueryTextListener(androidx.appcompat.widget.SearchView$OnQueryTextListener)' on a null object reference

exception is being thrown for

setOnQueryTextListener(object : SearchView.OnQueryTextListener {

I have already tried this out and it worked. But now I am trying to replicate it but it’s throwing this exception. Any idea? If I comment this method, it doesn’t crash but then I won’t be able to use the search feature!

PS, I’m using this along with a PageAdapter, so I have 2 recycler view loading when I get into the fragment. Would that be a problem? Should I implement this for both the recycler views? I’ll give that a try in the meantime.

bhcvvvvj

It’s showing an error in “FilterResults()”

John

Hi,

I finish practice. Thank you. I have one question.

We listed all country and searchable. But I need when I click one of them open new page.

For example I am searching United Kingdom then I click and open introduction page united kingdom it was possible ?

Sampath Patro

I’m getting the following error for my code:

Type mismatch.
Required: SearchView.OnQueryTextListener!
Found:
 
Code:

searchBar.setOnQueryTextListener(object: SearchView.OnQueryTextListener{
    override fun onQueryTextSubmit(query: String?): Boolean {
        return false
    }

    override fun onQueryTextChange(query: String?): Boolean {
        adapter.filter.filter(query)
        return false
    }

})

The error is shown for “object: SearchView.OnQueryTextListener”

Oscar Unzueta

There are two SearchView widgets:

  • android.widget.SearchView
  • androidx.appcompat.widget.SearchView

You should use the first

e-GO

can add a new item ether then country, for example, product and display products page with information
Also can make the list float?

e-GO

I mean make list like this picture

You mean I can add any information to the page of the item?

e-GO

No, this appears when I press to the icon-search and appear the item.
Do you have any external contact information?

e-GO

which file used to change the items?

e-GO

So, How Can I change the Item?
I’m sorry for bothering you but I am the beginner to the android
I am in university also I am in final level I need your help to gruduate
please could give me any feedback about your contact information

like a phone number or email

because I want to see the project documentation to estimate the can be successful or not.
I know you are bussy but just this time ….

I try to implemnt the project idea over 10 thousend time
But I dont get any benifie result …

e-GO

how I can change this method to accept my Item like for example katkit chocolate

Fatma
override fun onQueryTextChange(newText: String?): Boolean {
    CardViewGithubAdapter.filter.filter(newText)
    return false
}

Hello,
I would like to ask, what’s wrong with the filter?
CardViewGithubAdapter.filter.filter(newText)

any advice? what should I do?

maul badar
 "Potential NullPointerException. The resource is missing in some of layout versions"
 
on my searchView ID like this (sv_kasirkita.setOnQueryTextListener(object : SearchView.OnQueryTextListener)

Hello i got the problem in my searchView Id after i call in activity for setOnquery

Screen Shot 2021-04-21 at 13.48.17.png
Becky

Android Studio does not recognise FilterResults for me. What imports are needed for this to work?