Android

Android RecyclerView Search Filter Example

Android SearchView RecyclerView Example
Pinterest LinkedIn Tumblr

Almost every app is using search filter functionality. As we know, a mobile screen can display a maximum of 5 to 7 items on the screen. If you have thousands of items, then it is very difficult to scroll. of course, you need a search filter that provides easy to find the item.

If you’re not familiar with RecyclerView, then read the article about RecyclerView first.

Article Contents

  1. Step-1 (Create a new project)
  2. Step-2 (Changes in Gradle file)
  3. Step-3 (Color file changes)
  4. Step-4 (Adapter design)
  5. Step-5 (Writing the modal class)
  6. Step-6 (Writing the adapter class)
  7. Step-7 (MainActivity XML layout design)
  8. Step-8 (SearchView drawable background XML file)
  9. Step-9 (MainActivity Java file)
  10. Step-10 (Project Resources)

Demo

Step-1 (Create a new project)

Open the Android studio tool and create the new project.

Step-2 (Changes in Gradle file)

Put the following dependencies in the project Gradle file and click on the sync button

dependencies {

    implementation 'androidx.appcompat:appcompat:1.3.0'
    implementation 'com.google.android.material:material:1.3.0'
    implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
    testImplementation 'junit:junit:4.+'
    androidTestImplementation 'androidx.test.ext:junit:1.1.2'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
}

Step-3 (Color file changes)

Following colors being used throughout the project. Replace the colors.xml file with these lines.

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="purple_200">#FFBB86FC</color>
    <color name="purple_500">#FF6200EE</color>
    <color name="purple_700">#FF3700B3</color>
    <color name="teal_200">#FF03DAC5</color>
    <color name="teal_700">#FF018786</color>
    <color name="black">#FF000000</color>
    <color name="white">#FFFFFFFF</color>
    <color name="grey">#ACACAC</color>
    <color name="red">#FF0000</color>
    <color name="color_heading">#3C3B3C</color>
</resources>

Step-4 (Adapter design)

Create an XML layout file by right-clicking on the layout folder and renamed it with adapter_sneakers.xml. Put the following code in this 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"
    android:layout_width="match_parent"
    android:layout_height="120dp">

    <androidx.cardview.widget.CardView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_margin="7dp"
        app:cardUseCompatPadding="true"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent">

        <androidx.constraintlayout.widget.ConstraintLayout
            android:layout_width="match_parent"
            android:layout_margin="5dp"
            android:layout_height="match_parent">

            <ImageView
                android:id="@+id/imgSneakers"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:src="@drawable/shoe_one"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toTopOf="parent" />

            <TextView
                android:id="@+id/tvTitle"
                android:textColor="@color/color_heading"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_marginTop="5dp"
                android:ellipsize="end"
                android:fontFamily="@font/roboto"
                android:lines="1"
                android:text="@string/tv_title_one"
                android:textSize="18sp"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintStart_toEndOf="@+id/imgSneakers"
                app:layout_constraintTop_toTopOf="parent" />

            <TextView
                android:id="@+id/tvDescription"
                android:text="@string/tv_description_dummy"
                android:lines="2"
                android:fontFamily="@font/roboto"
                android:ellipsize="end"
                app:layout_constraintEnd_toEndOf="parent"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:textColor="@color/color_heading"
                android:textSize="14sp"
                app:layout_constraintStart_toEndOf="@+id/imgSneakers"
                app:layout_constraintTop_toBottomOf="@+id/tvTitle" />

            <TextView
                android:id="@+id/tvDiscountedValue"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintBottom_toBottomOf="parent"
                android:text="50% discount"
                android:textSize="16sp"
                android:fontFamily="@font/roboto"
                android:textStyle="bold"
                android:textColor="@color/red"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content" />
        </androidx.constraintlayout.widget.ConstraintLayout>

    </androidx.cardview.widget.CardView>

</androidx.constraintlayout.widget.ConstraintLayout>

Step-5 (Writing the modal class)

Create a class named SneakerM.java. This class will have four properties, title, resource image, description, and discount price.

package com.example.recyclerviewsearchexample;

public class SneakerM {
    private int resImg;
    private String title;
    private String description;
    private double discountPrice;

    public SneakerM(int resImg, String title, String description, double discountPrice) {
        this.resImg = resImg;
        this.title = title;
        this.description = description;
        this.discountPrice = discountPrice;
    }

    public int getResImg() {
        return resImg;
    }

    public void setResImg(int resImg) {
        this.resImg = resImg;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public double getDiscountPrice() {
        return discountPrice;
    }

    public void setDiscountPrice(double discountPrice) {
        this.discountPrice = discountPrice;
    }
}

Step-6 (Writing the adapter class)

If you’re not familiar that how to write the adapter class? then read this section. Create the Java file and renamed it with AdapterSneakers.java. Put the following code in this file.

package com.example.recyclerviewsearchexample;

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 android.widget.ImageView;
import android.widget.TextView;

import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;

import java.util.ArrayList;
import java.util.List;
import java.util.Locale;

public class AdapterSneakers extends RecyclerView.Adapter<RecyclerView.ViewHolder> implements Filterable {
    private List<SneakerM> sneakerMList;
    private List<SneakerM> searchableList;
    private Context _context;


    AdapterSneakers(List<SneakerM> list) {
        this.sneakerMList = list;
        this.searchableList = list;
    }

    @Override
    public Filter getFilter() {
        return new Filter() {
            @Override
            protected FilterResults performFiltering(CharSequence constraint) {
                String query = constraint.toString();
                if (query.trim().isEmpty()) {
                    searchableList = sneakerMList;
                } else {
                    List<SneakerM> tempFilterList = new ArrayList<>();
                    for (SneakerM sneakerM : sneakerMList) {
                        // change your logic according to your requirements
                        if (sneakerM.getTitle().toLowerCase().contains(query.toLowerCase())) {
                            tempFilterList.add(sneakerM);
                        }
                    }
                    searchableList = tempFilterList;
                }
                FilterResults filterResults = new FilterResults();
                filterResults.values = searchableList;
                return filterResults;
            }

            @Override
            protected void publishResults(CharSequence constraint, FilterResults results) {
                searchableList = (List<SneakerM>) results.values;
                notifyDataSetChanged();
            }
        };
    }

    // holder class which will hold the views of layout
    private class SneakerCellViewHolder extends RecyclerView.ViewHolder {
        private TextView tvTitle;
        private TextView tvDescription;
        private TextView tvDiscountedValue;
        private ImageView imgSneaker;

        public SneakerCellViewHolder(View view) {
            super(view);
            tvTitle = view.findViewById(R.id.tvTitle);
            tvDescription = view.findViewById(R.id.tvDescription);
            tvDiscountedValue = view.findViewById(R.id.tvDiscountedValue);
            imgSneaker = view.findViewById(R.id.imgSneakers);
        }
    }

    @NonNull
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        _context = parent.getContext();
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.adapter_sneakers, parent, false);
        return new SneakerCellViewHolder(view);
    }

    @Override
    public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
        SneakerCellViewHolder sneakerCellViewHolder = (SneakerCellViewHolder) holder;
        SneakerM sneakerM = searchableList.get(position);
        sneakerCellViewHolder.tvTitle.setText(sneakerM.getTitle());
        sneakerCellViewHolder.tvDescription.setText(sneakerM.getDescription());
        sneakerCellViewHolder.tvDiscountedValue.setText(String.format(_context.getString(R.string.tv_discount_value), sneakerM.getDiscountPrice()));
        sneakerCellViewHolder.imgSneaker.setImageResource(sneakerM.getResImg());
    }

    @Override
    public int getItemCount() {
        return searchableList.size(); // return size of filtered list
    }
}

getFilter() is the override method of Filterable because we implemented the Filterable class in AdapterSneakers.java. All search logic will contain in the getFilter() method. We have two lists, the first list will have all data and the second list will store search items. If no data search then fetches data from the first list and store it in the second list. In getItemCount() method, the size of the second list will return to the adapter.

Step-7 (MainActivity XML layout design)

According to design, we have one SearchView and RecyclerView component. Open the activity_main.xml file and make the following changes in this 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"
    tools:context=".MainActivity">

    <androidx.appcompat.widget.SearchView
        android:id="@+id/searchView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginStart="10dp"
        android:layout_marginTop="20dp"
        android:layout_marginEnd="10dp"
        android:background="@drawable/bg_search_view"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/rcvSneakers"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:layout_marginStart="10dp"
        android:layout_marginTop="20dp"
        android:layout_marginEnd="10dp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/searchView" />

</androidx.constraintlayout.widget.ConstraintLayout>

Step-8 (SearchView drawable background XML file)

In the SearchView component, we’re using a drawable resource file as a background for rounded corners. Create a drawable resource file by right-clicking on the drawable folder renamed it with bg_search_view.xml. Put the following code in the XML file

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<stroke android:color="@color/grey" android:width="1dp"/>
    <corners android:radius="5dp"/>
</shape>

Step-9 (MainActivity Java file)

Open the MainActivity.java file and add the code shown below.

  • setOnQueryTextListener listen to character when you start typing in SearchView. By using getFilter().Filter() method, value will pass to AdapterSneakers.java class and getFilter() method will receive this input query and filters the result.
  • In initComponents() method, initialize the SearchView and RecyclerView componets and set the setOnQueryTextListener to SearchView object.
  • initRcv(), create the object of LinearLayoutManager class and set the orientation verticle and set this object to the recyclerview object.
  • Prepare the dummy data by using prepareDummyData() method. Add the following code to your MainActivity.java class.
package com.example.recyclerviewsearchexample;

import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.SearchView;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;

import android.content.res.TypedArray;
import android.os.Bundle;

import java.util.ArrayList;
import java.util.List;

public class MainActivity extends AppCompatActivity {
    private SearchView searchView;
    private RecyclerView rcvSneakers;
    private List<SneakerM> sneakerMList;
    private AdapterSneakers adapterSneakers;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initComponents();
        initRcv();
        prepareDummyData();
    }

    /**
     * initialize the components
     */
    private void initComponents() {
        searchView = findViewById(R.id.searchView);
        rcvSneakers = findViewById(R.id.rcvSneakers);
        sneakerMList = new ArrayList<>();
        adapterSneakers = new AdapterSneakers(sneakerMList);
        searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
            @Override
            public boolean onQueryTextSubmit(String query) {
                return false;
            }

            @Override
            public boolean onQueryTextChange(String newText) {
                adapterSneakers.getFilter().filter(newText);
                return true;
            }
        });
    }

    /**
     * initialize the Recyclerview and Adapter
     */
    private void initRcv() {
        LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
        linearLayoutManager.setOrientation(LinearLayoutManager.VERTICAL);
        rcvSneakers.setLayoutManager(linearLayoutManager);
        rcvSneakers.setAdapter(adapterSneakers);
    }

    /**
     * set the dummy data to the shoe modal list
     */
    private void prepareDummyData() {
        String[] arrShoes = getResources().getStringArray(R.array.arr_shoes);
        TypedArray arrShoesImages = getResources().obtainTypedArray(R.array.arr_shoes_drawables);
        int index = 0;
        for (String title : arrShoes) {
            SneakerM sneakerM = new SneakerM(arrShoesImages.getResourceId(index,0), title, getString(R.string.tv_description_dummy), Math.random());
            sneakerMList.add(sneakerM);
            index++;
        }
        adapterSneakers.notifyDataSetChanged();
    }
}

Step-10 (Project Resources)

Thanks for reading the tutorial. Subscribe to my YouTube channel. Like and Share my Facebook page with your friends.

Write A Comment