Android RecyclerView search tutorial for beginners

 

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.

android recyclerview search

 

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

android recyclerview search

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

Show Buttons
Hide Buttons