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