Kotlin RecyclerView SearchView Example Tutorial | Search Filter

kotlin sqlite multiple tables, kotlin recyclerview checkbox, kotlin recyclerview button, kotlin load html javascript, kotlin recyclerview searchview, kotlin recyclerview search using edittext, kotlin listview with edittext, kotlin recyclerview with edittext, kotlin swipe to delete listview, kotlin recyclerview swipe to delete, kotlin expandablelistview with checkbox, kotlin custom ratingbar, kotlin change tab, Kotlin Select Multiple Images From Gallery, Kotlin Custom Dialog With Image And Title, kotlin select multiple video

I am writing on Kotlin RecyclerView SearchView Example Tutorial.

You will learn to implement Search Filter functionality in the RecyclerView in Android using Kotlin.

When there are many items in the recycler view, search filter functionality helps user to find his desired item quickly.

First, see the following for output reference.

 

Step 1. New Studio Project

Make a new project in android studio with empty activity as a default one.

Select Kotlin as the primary source language for the entire project.

Step 2. Gradle Changes

Open your build.gradle(Module:app) file and add the following lines in it

 implementation 'com.android.support:recyclerview-v7:28.0.0'
    implementation 'com.android.support:cardview-v7:28.0.0'

First line is integrating the classes for recycler view. We will be able to use Recycler view with the help of this line.

Second line is doing the same thing for card view. Card view helps us to make better design with recycler view.

Step 3. XML In Drawable

Navigate to the app->res->drawable folder and create a new XML file in this drawable folder.

Set the name of the XML file as cardview.xml . You need to add the following code lines in this cardview.xml file.

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">

    <item android:state_focused="true">
        <shape android:shape="rectangle">
            <padding
                android:left="4dp"
                android:top="4dp"
                android:right="4dp"
                android:bottom="4dp"
                />
            <gradient android:startColor="#b71ca2" android:endColor="#cc2ec1" android:angle="270" />

            <corners android:topLeftRadius="4dp" android:topRightRadius="4dp"/>
        </shape>
    </item>

    <item android:state_focused="false"
        >
        <shape android:shape="rectangle">
            <padding
                android:left="4dp"
                android:top="4dp"
                android:right="4dp"
                android:bottom="4dp"
                />

            <gradient android:startColor="#b71ca2" android:endColor="#cc2ec1" android:angle="270" />

            <corners android:topLeftRadius="4dp" android:topRightRadius="4dp" />
        </shape>
    </item>

</selector>

This file creating some gradient effects with various colors. Using this file, we can design our card view more efficiently.

Using this file, we can also add corners to our card view. At any time, you can change the colors or a radius size.

Step 4. Special XML for Recycler Items And Model

Make a new XML resources file in app->res->layout directory and give it a name like rv_item.xml

You should write down the following source line in rv_item.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    tools:context=".MainActivity">

    <android.support.v7.widget.CardView xmlns:card_view="http://schemas.android.com/apk/res-auto"
        android:id="@+id/card_view"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:layout_marginLeft="10dp"
        android:layout_marginRight="10dp"
        android:layout_marginTop="10dp"
        card_view:cardCornerRadius="4dp">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical">

            <TextView
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:id="@+id/tv2"
                android:height="40dp"
                android:background="@drawable/cardview"
                android:gravity="center_vertical"
                android:paddingLeft="10dp"
                android:text=""
                android:textColor="#fff"
                android:textStyle="bold"
                android:textSize="18sp" />

            <TextView
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:id="@+id/tv"
                android:background="#000"
                android:height="40dp"
                android:gravity="center_vertical"
                android:paddingLeft="10dp"
                android:text="Image"
                android:textColor="#fff"
                android:textStyle="bold"
                android:textSize="18sp" />

        </LinearLayout>

    </android.support.v7.widget.CardView>
</RelativeLayout>

This file will help us to create the look and feel of the every row item of the recycler view.

A file cardview.xml that we have created in the drawable folder is used here in the first text view.

Now make a new Kotlin file and give it a name like SearchModel.kt

Below source lines for SearchModel.kt file.

class SearchModel {

    var name: String? = null

    fun getNames(): String {
        return name.toString()
    }

    fun setNames(name: String) {
        this.name = name
    }
}

A string variable name is defined in this model file.

For this variable, I have written getter and setter methods. These methods will help to maintain the data during search activity is going on.

Step 5. Adapter For Search

Make a new class and give it a name as SearchAdapter.kt

You need to write down the following code snippet in this file.

import android.content.Context
import android.support.v7.widget.RecyclerView
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import java.util.ArrayList
import java.util.Locale

/**
 * Created by Parsania Hardik on 26-Jun-17.
 */
class SearchAdapter(ctx: Context, private val imageModelArrayList: ArrayList<SearchModel>) :
    RecyclerView.Adapter<SearchAdapter.MyViewHolder>() {

    private val inflater: LayoutInflater
    private val arraylist: ArrayList<SearchModel>

    init {

        inflater = LayoutInflater.from(ctx)
        this.arraylist = ArrayList<SearchModel>()
        this.arraylist.addAll(MainActivity.imageModelArrayList)
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SearchAdapter.MyViewHolder {

        val view = inflater.inflate(R.layout.rv_item, parent, false)

        return MyViewHolder(view)
    }

    override fun onBindViewHolder(holder: SearchAdapter.MyViewHolder, position: Int) {

        holder.time.setText(imageModelArrayList[position].getNames())
    }

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

    inner class MyViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {

        var time: TextView

        init {

            time = itemView.findViewById(R.id.tv) as TextView
        }

    }

    // Filter Class
    fun filter(charText: String) {
        var charText = charText
        charText = charText.toLowerCase(Locale.getDefault())
        MainActivity.imageModelArrayList.clear()
        if (charText.length == 0) {
            MainActivity.imageModelArrayList.addAll(arraylist)
        } else {
            for (wp in arraylist) {
                if (wp.getNames().toLowerCase(Locale.getDefault()).contains(charText)) {
                    MainActivity.imageModelArrayList.add(wp)
                }
            }
        }
        notifyDataSetChanged()
    }

}

See the following first

  override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SearchAdapter.MyViewHolder {

        val view = inflater.inflate(R.layout.rv_item, parent, false)

        return MyViewHolder(view)
    }

Above code is of onCreateViewHolder() method. It is inflating the rv_item.xml file. Using this line compiler will create the view for every child item of the recycler view.

Read the following now

 override fun onBindViewHolder(holder: SearchAdapter.MyViewHolder, position: Int) {

        holder.time.setText(imageModelArrayList[position].getNames())
    }

A method onBindViewHolder() will set the text into the text view. For this, it will imageModelArayList which is the data source. Adapter is getting this arraylist from the parameter.

We will create this arraylist in the main activity to see which kind of data it contains.

Now check the following method

  // Filter Class
    fun filter(charText: String) {
        var charText = charText
        charText = charText.toLowerCase(Locale.getDefault())
        MainActivity.imageModelArrayList.clear()
        if (charText.length == 0) {
            MainActivity.imageModelArrayList.addAll(arraylist)
        } else {
            for (wp in arraylist) {
                if (wp.getNames().toLowerCase(Locale.getDefault()).contains(charText)) {
                    MainActivity.imageModelArrayList.add(wp)
                }
            }
        }
        notifyDataSetChanged()
    }

This function is the main and important portion of the whole example.

As it’s name suggests, filter() method will filter the recycler view on the basis of it’s name.

Whenever the user types the search query, compiler will call the above function. It will send the search query in to the parameter of this method.

Based on this search query, compiler will update the items in the imageModelArraList and then it will call the notifyDataSetChanged() method.

notifyDataSetChanged() method will simply simply update the recycler view as per the new imageModelArraList.

Step 6. Last but Main Activity

Now this is the last step of our example. Go to the activity_main.xml file and add the below code in it

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              xmlns:tools="http://schemas.android.com/tools"
              android:layout_width="match_parent"
              android:layout_height="match_parent"
              android:orientation="vertical"
              tools:context=".MainActivity">

    <SearchView
            android:id="@+id/search"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:iconifiedByDefault="false">

        <requestFocus />
    </SearchView>

    <android.support.v7.widget.RecyclerView
            android:id="@+id/recycler"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_marginTop="15dp"/>

</LinearLayout>

It has only two UI widgets. One is search view and another is recycler view.

Search view is above the Recycler view so that user can type the search query and it improves over all user experience.

Now in the MainActivity.kt file, you should write the following data lines

import android.content.Context
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import android.support.v7.widget.LinearLayoutManager
import android.support.v7.widget.RecyclerView
import android.util.Log
import android.view.GestureDetector
import android.view.MotionEvent
import android.view.View
import android.widget.SearchView
import android.widget.Toast
import java.util.ArrayList

class MainActivity : AppCompatActivity(), SearchView.OnQueryTextListener {

    private var recyclerView: RecyclerView? = null
    private var adapter: SearchAdapter? = null
    private var editsearch: SearchView? = null

    private val myImageNameList = arrayOf("Benz", "Bike", "Car", "Carrera", "Ferrari", "Harly", "Lamborghini", "Silver")
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        recyclerView = findViewById(R.id.recycler) as RecyclerView

        imageModelArrayList = populateList()
        Log.d("hjhjh", imageModelArrayList.size.toString() + "")
        adapter = SearchAdapter(this, imageModelArrayList)
        recyclerView!!.adapter = adapter
        recyclerView!!.layoutManager = LinearLayoutManager(applicationContext, LinearLayoutManager.VERTICAL, false)

        recyclerView!!.addOnItemTouchListener(
            RecyclerTouchListener(
                applicationContext,
                recyclerView!!,
                object : ClickListener {

                    override fun onClick(view: View, position: Int) {
                        Toast.makeText(this@MainActivity, imageModelArrayList[position].getNames(), Toast.LENGTH_SHORT)
                            .show()
                    }

                    override fun onLongClick(view: View?, position: Int) {

                    }
                })
        )


        editsearch = findViewById(R.id.search) as SearchView
        editsearch!!.setOnQueryTextListener(this)

    }

    override fun onQueryTextSubmit(query: String): Boolean {

        return false
    }

    override fun onQueryTextChange(newText: String): Boolean {
        adapter!!.filter(newText)
        return false
    }

    private fun populateList(): ArrayList<SearchModel> {

        val list = ArrayList<SearchModel>()

        for (i in 0..7) {
            val imageModel = SearchModel()
            imageModel.setNames(myImageNameList[i])
            list.add(imageModel)
        }

        return list
    }

    interface ClickListener {
        fun onClick(view: View, position: Int)

        fun onLongClick(view: View?, position: Int)
    }

    internal class RecyclerTouchListener(
        context: Context,
        recyclerView: RecyclerView,
        private val clickListener: ClickListener?
    ) : RecyclerView.OnItemTouchListener {

        private val gestureDetector: GestureDetector

        init {
            gestureDetector = GestureDetector(context, object : GestureDetector.SimpleOnGestureListener() {
                override fun onSingleTapUp(e: MotionEvent): Boolean {
                    return true
                }

                override fun onLongPress(e: MotionEvent) {
                    val child = recyclerView.findChildViewUnder(e.x, e.y)
                    if (child != null && clickListener != null) {
                        clickListener.onLongClick(child, recyclerView.getChildPosition(child))
                    }
                }
            })
        }

        override fun onInterceptTouchEvent(rv: RecyclerView, e: MotionEvent): Boolean {

            val child = rv.findChildViewUnder(e.x, e.y)
            if (child != null && clickListener != null && gestureDetector.onTouchEvent(e)) {
                clickListener.onClick(child, rv.getChildPosition(child))
            }
            return false
        }

        override fun onTouchEvent(rv: RecyclerView, e: MotionEvent) {}

        override fun onRequestDisallowInterceptTouchEvent(disallowIntercept: Boolean) {

        }


    }

    companion object {
        lateinit var imageModelArrayList: ArrayList<SearchModel>
    }

}

See the following

 private var recyclerView: RecyclerView? = null
    private var adapter: SearchAdapter? = null
    private var editsearch: SearchView? = null

    private val myImageNameList = arrayOf("Benz", "Bike", "Car", "Carrera", "Ferrari", "Harly", "Lamborghini", "Silver")

Compiler will first create the objects of recycler view, Search adapter and Search View classes.

Then an string array myImageNameList is there. It contains the names of the various vehicles. We will filter the recycler view based on this vehicle names.

See the below

imageModelArrayList = populateList()

This line will use the populateList() method to create the data source. Below is the code for populateList() method

  private fun populateList(): ArrayList<SearchModel> {

        val list = ArrayList<SearchModel>()

        for (i in 0..7) {
            val imageModel = SearchModel()
            imageModel.setNames(myImageNameList[i])
            list.add(imageModel)
        }

        return list
    }

It will create one arraylist which contains the objects of the SearchModel class. Then one for loop is there.

This for loop will create an object of the SearchModel class in it’s every iteration and it will bind one vehicle name to each object. Then all objects are added into the arraylist.

Read the below code

  recyclerView!!.addOnItemTouchListener(
            RecyclerTouchListener(
                applicationContext,
                recyclerView!!,
                object : ClickListener {

                    override fun onClick(view: View, position: Int) {
                        Toast.makeText(this@MainActivity, imageModelArrayList[position].getNames(), Toast.LENGTH_SHORT)
                            .show()
                    }

                    override fun onLongClick(view: View?, position: Int) {

                    }
                })
        )

Compiler will run the above when the user clicks the recycler view. It will simply create the Toast. This Toast contains the name of the clicked Vehicle.

Now focus on the below

 editsearch = findViewById(R.id.search) as SearchView
 editsearch!!.setOnQueryTextListener(this)

Compiler will first find the Search View using id. Then it will set the onQuery change.

It will run the following when the user types the query

  override fun onQueryTextChange(newText: String): Boolean {
        adapter!!.filter(newText)
        return false
    }

Compiler will execute the method filter() which is written in the SearchAdapter class.

Download Code for Kotlin RecyclerView SearchView

https://github.com/demonuts/Kotlin-RecyclerView-SearchView-Example-Tutorial-Search-Filter