Android Recyclerview Search :
Android Recyclerview Search is used to filter the data populated through the recycler view.The best example is contacts list where we can search the contacts and filter the data.
Popular application like facebook, whatsapp and many other apps provide a search to filter their chats and also a dictionary is also the best example.
In this part of the tutorial we will use a card view to populate the android recyclerview and there after provide a search option.
Search option enables you to filter the data and populate the view by reloading the view every time.
Android Recyclerview Search Video Tutorial :
Please refer to the following tutorial for a comprehensive understanding of RecyclerView search in Android.
Project Structure :
This image displays the project structure for the RecyclerView search project.
Dependencies :
Add dependencies retrofit, okhttp, cardview, recyclerview to your build.gradle(:app)
implementation 'com.squareup.retrofit2:retrofit:2.4.0' implementation 'com.squareup.retrofit2:converter-gson:2.4.0' implementation 'com.squareup.okhttp3:okhttp:3.10.0' implementation 'com.squareup.okhttp3:logging-interceptor:3.7.0' implementation 'androidx.cardview:cardview:1.0.0' implementation 'androidx.recyclerview:recyclerview:1.1.0'
item_row.xml :
Add a textview to populate the data through recyclerview
<TextView android:id="@+id/txtName" android:layout_width="match_parent" android:layout_height="wrap_content" android:padding="15dp" android:textColor="@android:color/white" android:textStyle="bold" android:textSize="20sp" android:text="name" />
<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout 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:layout_width="match_parent" android:layout_height="wrap_content"> <androidx.cardview.widget.CardView android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_margin="10dp" android:elevation="5dp" app:cardBackgroundColor="@android:color/black" app:cardCornerRadius="20dp" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent"> <TextView android:id="@+id/txtName" android:layout_width="match_parent" android:layout_height="wrap_content" android:padding="15dp" android:text="name" android:textColor="@android:color/white" android:textSize="20sp" android:textStyle="bold" /> </androidx.cardview.widget.CardView> </androidx.constraintlayout.widget.ConstraintLayout>
activity_main.xml :
Add a recycler view to the layout file
<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout 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:layout_width="match_parent" android:layout_height="match_parent" android:background="@android:color/white" tools:context=".MainActivity"> <androidx.recyclerview.widget.RecyclerView android:id="@+id/recyclerView" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_margin="15dp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" /> </androidx.constraintlayout.widget.ConstraintLayout>
main.xml :
Create a folder menu under res folder and add a file main.xml
<?xml version="1.0" encoding="utf-8"?> <menu xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"> <item android:id="@+id/action_search" android:icon="@android:drawable/ic_menu_search" android:title="Search" app:actionViewClass="androidx.appcompat.widget.SearchView" app:showAsAction="always|collapseActionView" /> </menu>
UserList.kt :
Add a model class to parse the data and populate them.
package com.abhi.kotlinsearchview import com.google.gson.annotations.Expose import com.google.gson.annotations.SerializedName class UserList { @SerializedName("name") @Expose var name: String? = null }
ApiClient.kt :
Add a http client and provide the http interceptor, timeout, base url to the ApiClient.
package com.abhi.kotlinsearchview import okhttp3.OkHttpClient import okhttp3.logging.HttpLoggingInterceptor import retrofit2.Retrofit import retrofit2.converter.gson.GsonConverterFactory import java.util.concurrent.TimeUnit class ApiClient { companion object { private val interceptor = HttpLoggingInterceptor().apply { level = HttpLoggingInterceptor.Level.BASIC } private val httpClient = OkHttpClient.Builder() .connectTimeout(40, TimeUnit.SECONDS) .readTimeout(20, TimeUnit.SECONDS) .addInterceptor(interceptor) .build() private val builder = Retrofit.Builder() .baseUrl("https://www.json-generator.com/api/json/get/") .addConverterFactory(GsonConverterFactory.create()) fun <S> createService(serviceClass: Class<S>): S { val retrofit = builder.client(httpClient).build() return retrofit.create(serviceClass) } } }
ApiInterface.kt :
We are specifying the getUsers() method to fetch the list of user data.
package com.abhi.kotlinsearchview import retrofit2.Call import retrofit2.http.GET interface ApiInterface { @GET("cfTosXBXsi?indent=2") fun getUsers(): Call<ArrayList<UserList>> }
Adapter.kt :
Add a recycler adapter class to populate the data and add a filter functionality.
We are searching the data and add them to a separate list and populating that list after the search.
package com.abhi.kotlinsearchview import android.content.Context import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.Filter import android.widget.Filterable import androidx.recyclerview.widget.RecyclerView import kotlinx.android.synthetic.main.item_row.view.* import java.util.* class Adapter : RecyclerView.Adapter<Adapter.ViewHolder>, Filterable { private var data: ArrayList<UserList> private var dataSearch: ArrayList<UserList> private var context: Context private var itemClickListener: OnItemClickListener? = null constructor(context: Context) : super() { this.context = context this.data = ArrayList() this.dataSearch = ArrayList() } fun setDataValue(data: ArrayList<UserList>?) { dataSearch.clear() this.data.clear() data?.let { this.data.addAll(it) this.dataSearch.addAll(it) } } override fun getFilter(): Filter { return object : Filter() { override fun performFiltering(charSequence: CharSequence): Filter.FilterResults { val charText = charSequence.toString().toLowerCase(Locale.getDefault()) data.clear() if (charText.isEmpty()) { data.addAll(dataSearch) } else { for (user in dataSearch) { if (user.name?.toLowerCase(Locale.getDefault())?.contains(charText) == true) { data.add(user) } } } val filterResults = Filter.FilterResults() filterResults.values = data return filterResults } override fun publishResults(charSequence: CharSequence, filterResults: Filter.FilterResults) { @Suppress("UNCHECKED_CAST") data = filterResults.values as ArrayList<UserList> notifyDataSetChanged() } } } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { val view = LayoutInflater.from(context).inflate(R.layout.item_row, parent, false) return ViewHolder(view) } override fun onBindViewHolder(holder: ViewHolder, position: Int) { holder.itemView.txtName.text = data[position].name } override fun getItemCount(): Int { return data.size } fun setOnItemClickListener(itemClickListener: OnItemClickListener) { this.itemClickListener = itemClickListener } inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) interface OnItemClickListener { fun onItemClicked(user: UserList) } }
MainActivity.kt :
Add searchView and adapter variables
private lateinit var searchView: SearchView private lateinit var adapter: Adapter
Provide the LinearLayoutManager with orientation vertical
val layoutManager = LinearLayoutManager(this) layoutManager.orientation = LinearLayoutManager.VERTICAL recyclerView.layoutManager = layoutManager
Configure adapter with recycler view
adapter = Adapter(baseContext) recyclerView.adapter = adapter
Configure the search view to the toolbar and provide configurations
override fun onCreateOptionsMenu(menu: Menu?): Boolean { menuInflater.inflate(R.menu.main, menu) val searchItem = menu?.findItem(R.id.action_search) searchView = searchItem?.actionView as SearchView searchView.setQueryHint("Search Title") searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener { override fun onQueryTextChange(newText: String): Boolean { if (TextUtils.isEmpty(newText)) { adapter.getFilter().filter("") } else { adapter.getFilter().filter(newText) } return true } override fun onQueryTextSubmit(query: String): Boolean { return false } }) return super.onCreateOptionsMenu(menu) }
Get data from server
If data is successfully received configure the data to adapter and notify the data.
if (response?.isSuccessful == true) { adapter.setDataValue(response.body()) adapter.notifyDataSetChanged() }
else
override fun onFailure(call: Call<ArrayList<UserList>>?, t: Throwable?) { Log.d("Status", "Failed: ${t.toString()}") }
fun getDataFromServer() { val service = ApiClient.createService(ApiInterface::class.java) val call: Call<ArrayList<UserList>> = service.getUsers() call.enqueue(object : Callback<ArrayList<UserList>> { override fun onResponse(call: Call<ArrayList<UserList>>?, response: Response<ArrayList<UserList>>?) { try { if (response?.isSuccessful == true) { adapter.setDataValue(response.body()) adapter.notifyDataSetChanged() } else { Log.d("Status", "Failed") } } catch (e: Exception) { Log.d("Status", "Failed " + e.localizedMessage) } } override fun onFailure(call: Call<ArrayList<UserList>>?, t: Throwable?) { Log.d("Status", "Failed" + t.toString()) } }) }
FullCode :
Providing the full code for recyclerview search view implementations.
package com.abhi.kotlinsearchview import androidx.appcompat.app.AppCompatActivity import android.os.Bundle import android.text.TextUtils import android.util.Log import android.view.Menu import android.widget.Toast import androidx.appcompat.widget.SearchView import androidx.recyclerview.widget.LinearLayoutManager import kotlinx.android.synthetic.main.activity_main.* import retrofit2.Call import retrofit2.Callback import retrofit2.Response class MainActivity : AppCompatActivity() { private lateinit var searchView: SearchView private lateinit var adapter: Adapter override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val layoutManager = LinearLayoutManager(this) layoutManager.orientation = LinearLayoutManager.VERTICAL recyclerView.layoutManager = layoutManager adapter = Adapter(baseContext) recyclerView.adapter = adapter getDataFromServer() } override fun onCreateOptionsMenu(menu: Menu?): Boolean { menuInflater.inflate(R.menu.main, menu) val searchItem = menu!!.findItem(R.id.action_search) searchView = searchItem.actionView as SearchView searchView.setQueryHint("Search Title") searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener { override fun onQueryTextChange(newText: String): Boolean { if (TextUtils.isEmpty(newText)) { adapter.getFilter().filter("") } else { adapter.getFilter().filter(newText) } return true } override fun onQueryTextSubmit(query: String): Boolean { return false } }) return super.onCreateOptionsMenu(menu) } fun getDataFromServer() { val service = ApiClient.createService(ApiInterface::class.java) val call: Call<ArrayList<UserList>> = service.getUsers() call.enqueue(object : Callback<ArrayList<UserList>> { override fun onResponse(call: Call<ArrayList<UserList>>?, response: Response<ArrayList<UserList>>?) { try { if (response!!.isSuccessful) { adapter.setDataValue(response.body()) adapter.notifyDataSetChanged() } else { Log.d("Status", "Failed") } } catch (e: Exception) { Log.d("Status", "Failed " + e.localizedMessage) } } override fun onFailure(call: Call<ArrayList<UserList>>?, t: Throwable?) { Log.d("Status", "Failed" + t.toString()) } }) } }
Android recyclerview search output :
Below is a depiction of how the Android RecyclerView is utilized
If you have any questions, feel free to drop them in the comments below. If you enjoyed this tutorial, show us some love by liking and sharing for more exciting updates