Android RecyclerView Filter Search Functionality with Search Suggest

In this tutorial, we are going to learn how to implement Android recyclerview filter search functionality with search suggestion.

If you can used ListView component to filter item lists you will see how easy it is because ListView class Adapter has default implementation of Filter. This is not the case with RecyclerView adapter class.

I have also written a tutorial on using the ListView filter search functionality in Android Dictionary Application with search suggest. I will suggest you read this post first. Please note that Google favors RecyclerView over ListView so it is advisable to use RecyclerView that is one reason why I wrote this android blog post.

We are going to create our android project. Our main layout file will consist of a single EditText and RecyclerView widgets. The EditText will be used for search filtering of the list items in the RecyclerView.

Before we go deeper into this tutorial, it is important for us to understand what we are planning to achieve. Below is the screen-shot of the application we will be creating.

Recyclerview item search

Lets start to soil our hands with codes. we will create our project in our IDE. For this tutorial, I am using the following tools and environment, feel free to use what works for you.

Windows 7

Android Studio

Sony Xperia ZL

Min SDK 14

Target SDK 23

To create a new android application project, follow the steps as stipulated below.

Go to File menu

Click on New menu

Click on Android Application

Enter Project name: AndroidRecyclerViewSearch

Package: com.inducesmile.androidrecyclerviewsearch

Select Blank Activity

Keep other default selections

Continue to click on next button until Finish button is active, then click on Finish Button.

We are going to add the RecycleView to our project. Open your app build.gradle file located at Gradle Script folder, copy and paste this line of code. When you see a link with Sync Now. Click on Sync Now to add this library to your project dependency.

compile 'com.android.support:recyclerview-v7:23.2.1'

Now, lets go to our application main layout file which will contain the RecycleView widget. Open the layout file activity_main.xml located at res > layout folder. Copy and paste the code below inside the layout file.

<?xml version="1.0" encoding="utf-8"?>
<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="match_parent"
    tools:context="com.inducesmile.androidrecycleviewsearch.MainActivity">
    <EditText
        android:id="@+id/search_box"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginLeft="16dp"
        android:layout_marginRight="16dp"
        android:layout_marginTop="8dp"
        android:layout_marginBottom="8dp"
        android:paddingLeft="8dp"
        android:paddingBottom="12dp"
        android:paddingTop="12dp"
        android:textSize="14sp"
        android:hint="@string/search_hint"
        android:background="@drawable/bottom_border"
        android:layout_alignParentTop="true"
        android:layout_centerHorizontal="true" />
    <android.support.v7.widget.RecyclerView
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:id="@+id/item_list"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_centerHorizontal="true"
        app:layoutManager="LinearLayoutManager"
        android:layout_below="@+id/search_box">
    </android.support.v7.widget.RecyclerView>
</RelativeLayout>

Go to the res folder, then double click on the values folder and double click on the colors.xml file and add the following lines of codes. This will holder all the colors we will use in this tutorial.

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="colorPrimary">#3F51B5</color>
    <color name="colorPrimaryDark">#303F9F</color>
    <color name="colorAccent">#FF4081</color>
    <color name="colorBlack">#000000</color>
    <color name="divider">#727272</color>
    <color name="colorWhite">#ffffff</color>
    <color name="colorPink">#D2B6FF</color>
</resources>

Now, head over to the strings.xml file and modify the content with the code below.

<resources>
    <string name="app_name">Android RecyclerView Search</string>
    <string name="search_hint">Search</string>
</resources>

In the drawable folder, right clcik and choose create a file, we are going to create a drawable shape which will use as a background border for the EditText control. Name the file bottom_border.xml and click ok button. Open the created drawable file, copy and paste the below code inside the file.

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
    <stroke
        android:width="1dp"
        android:color="@color/divider" />
    <solid
        android:color="#00FFFFFF"
        android:paddingLeft="10dp"
        android:paddingTop="10dp"/>
    <padding
        android:left="10dp"
        android:top="10dp"
        android:right="10dp"
        android:bottom="10dp" />
</shape>

We will do the same again be now we the name the new drawable file line_divider.xml. The RecyclerView decorator class will use this drawable file to draw a line demarcation between items in the RecyclerView components. Copy and paste the code below inside the file.

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">
    <size
        android:width="1dp"
        android:height="1dp" />
    <solid android:color="@color/divider" />
</shape>

For the RecyclerView item decoration class, create a new java class in your project package, name it SimpleDividerItemDecoration.java. This class will inherit from the RecyclerView.ItemDecoration class as shown

public class SimpleDividerItemDecoration extends RecyclerView.ItemDecoration {
}

The complete code for this class is as shown below.

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
import android.support.v4.content.ContextCompat;
import android.support.v7.widget.RecyclerView;
import android.view.View;
public class SimpleDividerItemDecoration extends RecyclerView.ItemDecoration {
    private Drawable mDivider;
    public SimpleDividerItemDecoration(Context context) {
        mDivider = ContextCompat.getDrawable(context, R.drawable.line_divider);
    }
    @Override
    public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
        int left = parent.getPaddingLeft();
        int right = parent.getWidth() - parent.getPaddingRight();
        int childCount = parent.getChildCount();
        for (int i = 0; i < childCount; i++) {
            View child = parent.getChildAt(i);
            RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();
            int top = child.getBottom() + params.bottomMargin;
            int bottom = top + mDivider.getIntrinsicHeight();
            mDivider.setBounds(left, top, right, bottom);
            mDivider.draw(c);
        }
    }
}

Now let us focus in create the layout file our RecyclerView adapter will us. We are going to create an inner class adapter. Create a new layout file in the layout folder and name it item_list_content.xml. The layout file will contain two TextView controls, One will hold the list item position while the other will hold list item content. Open the layout file, copy and paste the code below inside the file.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:orientation="horizontal">
    <TextView
        android:id="@+id/id"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_margin="16dp"
        android:textSize="14sp"
        android:textAppearance="?attr/textAppearanceListItem" />
    <TextView
        android:id="@+id/content"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginRight="16dp"
        android:layout_marginTop="16dp"
        android:layout_marginBottom="16dp"
        android:textSize="14sp"
        android:textAppearance="?attr/textAppearanceListItem" />
</LinearLayout>

For the RecyclerView Adapter inner class in the MainActivity class is as shown belwo.

// create a custom RecycleViewAdapter class
public class SimpleItemRecyclerViewAdapter extends RecyclerView.Adapter<SimpleItemRecyclerViewAdapter.ViewHolder> implements Filterable {
    private List<QuizObject> mValues;
    private CustomFilter mFilter;
    public SimpleItemRecyclerViewAdapter(List<QuizObject> items) {
        mValues = items;
        mFilter = new CustomFilter(SimpleItemRecyclerViewAdapter.this);
    }
    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_list_content, parent, false);
        return new ViewHolder(view);
    }
    @Override
    public void onBindViewHolder(final ViewHolder holder, int position) {
        holder.mItem = mValues.get(position);
        holder.mIdView.setText(String.valueOf(mValues.get(position).getId()));
        holder.mContentView.setText(mValues.get(position).getWord());
    }
    @Override
    public int getItemCount() {
        return mValues.size();
    }
    @Override
    public Filter getFilter() {
        return mFilter;
    }
    public class ViewHolder extends RecyclerView.ViewHolder {
        public final View mView;
        public final TextView mIdView;
        public final TextView mContentView;
        public QuizObject mItem;
        public ViewHolder(View view) {
            super(view);
            mView = view;
            mIdView = (TextView) view.findViewById(R.id.id);
            mContentView = (TextView) view.findViewById(R.id.content);
        }
        @Override
        public String toString() {
            return super.toString() + " '" + mContentView.getText() + "'";
        }
    }
    public class CustomFilter extends Filter {
        private SimpleItemRecyclerViewAdapter mAdapter;
        private CustomFilter(SimpleItemRecyclerViewAdapter mAdapter) {
            super();
            this.mAdapter = mAdapter;
        }
        @Override
        protected FilterResults performFiltering(CharSequence constraint) {
            filteredList.clear();
            final FilterResults results = new FilterResults();
            if (constraint.length() == 0) {
                filteredList.addAll(dictionaryWords);
            } else {
                final String filterPattern = constraint.toString().toLowerCase().trim();
                for (final QuizObject mWords : dictionaryWords) {
                    if (mWords.getWord().toLowerCase().startsWith(filterPattern)) {
                        filteredList.add(mWords);
                    }
                }
            }
            System.out.println("Count Number " + filteredList.size());
            results.values = filteredList;
            results.count = filteredList.size();
            return results;
        }
        @Override
        protected void publishResults(CharSequence constraint, FilterResults results) {
            System.out.println("Count Number 2 " + ((List<QuizObject>) results.values).size());
            this.mAdapter.notifyDataSetChanged();
        }
    }
}

You can see that we implement our own Filter by implementing the Filterable Interface and at the same time will extends with Filter class with our own CustomFilter class.

The List object passed as data source to the RecyclerView Adapter wrapper an entity class named QuizObject. We will create a new java class and will name it QuizObject.java. Open the file, copy and paste the code below inside the file.

public class QuizObject {
    private int id;
    private String word;
    private String meaning;
    public QuizObject(int id, String word) {
        this.id = id;
        this.word = word;
    }
    public QuizObject(int id, String word, String meaning) {
        this.id = id;
        this.word = word;
        this.meaning = meaning;
    }
    public int getId() {
        return id;
    }
    public String getWord() {
        return word;
    }
    public String getMeaning() {
        return meaning;
    }
}

The final code for the MainActivity class is as shown.

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.RecyclerView;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.EditText;
import android.widget.Filter;
import android.widget.Filterable;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.List;
public class MainActivity extends AppCompatActivity {
    private static final String TAG = "MainActivity";
    // List of all dictionary words
    private List<QuizObject> dictionaryWords;
    private List<QuizObject> filteredList;
    // RecycleView adapter object
    private SimpleItemRecyclerViewAdapter mAdapter;
    // Search edit box
    private EditText searchBox;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        dictionaryWords = getListItemData();
        filteredList = new ArrayList<QuizObject>();
        filteredList.addAll(dictionaryWords);
        searchBox = (EditText)findViewById(R.id.search_box);
        RecyclerView recyclerView = (RecyclerView)findViewById(R.id.item_list);
        recyclerView.addItemDecoration(new SimpleDividerItemDecoration(this));
        assert recyclerView != null;
        mAdapter = new SimpleItemRecyclerViewAdapter(filteredList);
        recyclerView.setAdapter(mAdapter);
        // search suggestions using the edittext widget
        searchBox.addTextChangedListener(new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence s, int start, int count, int after) {
            }
            @Override
            public void onTextChanged(CharSequence s, int start, int before, int count) {
                mAdapter.getFilter().filter(s.toString());
            }
            @Override
            public void afterTextChanged(Editable s) {
            }
        });
    }
    // create a custom RecycleViewAdapter class
    public class SimpleItemRecyclerViewAdapter extends RecyclerView.Adapter<SimpleItemRecyclerViewAdapter.ViewHolder> implements Filterable {
        private List<QuizObject> mValues;
        private CustomFilter mFilter;
        public SimpleItemRecyclerViewAdapter(List<QuizObject> items) {
            mValues = items;
            mFilter = new CustomFilter(SimpleItemRecyclerViewAdapter.this);
        }
        @Override
        public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_list_content, parent, false);
            return new ViewHolder(view);
        }
        @Override
        public void onBindViewHolder(final ViewHolder holder, int position) {
            holder.mItem = mValues.get(position);
            holder.mIdView.setText(String.valueOf(mValues.get(position).getId()));
            holder.mContentView.setText(mValues.get(position).getWord());
        }
        @Override
        public int getItemCount() {
            return mValues.size();
        }
        @Override
        public Filter getFilter() {
            return mFilter;
        }
        public class ViewHolder extends RecyclerView.ViewHolder {
            public final View mView;
            public final TextView mIdView;
            public final TextView mContentView;
            public QuizObject mItem;
            public ViewHolder(View view) {
                super(view);
                mView = view;
                mIdView = (TextView) view.findViewById(R.id.id);
                mContentView = (TextView) view.findViewById(R.id.content);
            }
            @Override
            public String toString() {
                return super.toString() + " '" + mContentView.getText() + "'";
            }
        }
        public class CustomFilter extends Filter {
            private SimpleItemRecyclerViewAdapter mAdapter;
            private CustomFilter(SimpleItemRecyclerViewAdapter mAdapter) {
                super();
                this.mAdapter = mAdapter;
            }
            @Override
            protected FilterResults performFiltering(CharSequence constraint) {
                filteredList.clear();
                final FilterResults results = new FilterResults();
                if (constraint.length() == 0) {
                    filteredList.addAll(dictionaryWords);
                } else {
                    final String filterPattern = constraint.toString().toLowerCase().trim();
                    for (final QuizObject mWords : dictionaryWords) {
                        if (mWords.getWord().toLowerCase().startsWith(filterPattern)) {
                            filteredList.add(mWords);
                        }
                    }
                }
                System.out.println("Count Number " + filteredList.size());
                results.values = filteredList;
                results.count = filteredList.size();
                return results;
            }
            @Override
            protected void publishResults(CharSequence constraint, FilterResults results) {
                System.out.println("Count Number 2 " + ((List<QuizObject>) results.values).size());
                this.mAdapter.notifyDataSetChanged();
            }
        }
    }
    private List<QuizObject> getListItemData(){
        List<QuizObject> listViewItems = new ArrayList<QuizObject>();
        listViewItems.add(new QuizObject(1, "Apple", "Apple"));
        listViewItems.add(new QuizObject(2, "Orange", "Orange"));
        listViewItems.add(new QuizObject(3, "Banana", "Banana"));
        listViewItems.add(new QuizObject(4, "Grape", "Grape"));
        listViewItems.add(new QuizObject(5, "Mango", "Mango"));
        listViewItems.add(new QuizObject(6, "Pear", "Pear"));
        listViewItems.add(new QuizObject(7, "Pineapple", "Pineapple"));
        listViewItems.add(new QuizObject(8, "Strawberry", "Strawberry"));
        listViewItems.add(new QuizObject(9, "Coconut", "Coconut"));
        listViewItems.add(new QuizObject(10, "Almond", "Almond"));
        return listViewItems;
    }
}

This is the ending of the tutorial. I hope you must have learn one thing. You can suggest a topic that you might like to see me write and publish here.

You can download the code for this tutorial below. If you are having hard time downloading the tutorials, kindly contact me.

Remember to subscribe with your email address so that you will be among the first to receive my new post once it is published.

Please if you love this tutorial, kindly download my new android app – Daily Calculator Tool – in Google Play Store and let me know what you think about it.

OTHER INTERESTING POSTS:

2 Comments

Add a Comment