Android Recyclerview Search Filter Example

Earlier, I explained about Recyclerview and Cardview in details. Now, I am going to explain about Android Recyclerview Search Filter with Example.

SearchView which can be used to filter the data displayed in the RecyclerView. While use Toolbar’s search widget to input the search query.

When the SearchView is used in an ActionBar as an action view for a collapsible menu item, It needs to be set to iconified by default using setIconifiedByDefault(true). This is the default, so nothing needs to be done.

If you want the search field to always be visible, then call setIconifiedByDefault(false).

Android Recyclerview Search Filter

So, Follow the steps to develop the application with Android Recyclerview Search Filter.

1. Creating a New Project

1. Create a New Project in Android Studio.

2. Add project dependencies Retrofit, Recyclerview, and Cardview to App build.gradle.

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'com.android.support:appcompat-v7:26.1.0'
    
    implementation 'com.android.support:design:26.1.0'
    implementation 'com.android.support:cardview-v7:26.1.0'
    //Retrofit
    compile 'com.squareup.retrofit2:retrofit:2.3.0'
    compile 'com.squareup.retrofit2:converter-gson:2.3.0'
    //Glide
    compile 'com.github.bumptech.glide:glide:4.3.1'
}

3. Also, Open AndroidManifest.xml and add  INTERNET permission as we are going to make HTTP calls.

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.velmurugan.recyclerviewsearchfilterexample">

    <uses-permission android:name="android.permission.INTERNET"></uses-permission>

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

Glide – Image loading framework for Android with Example [Part 1]

Expandable Recyclerview For Android With Example


2. Load Data From API to Recyclerview using Retrofit

Also, Check out my Retrofit Android Example to know more about Retrofit.

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:tools="http://schemas.android.com/tools"
    tools:context=".MainActivity">

    <android.support.design.widget.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:theme="@style/AppTheme.AppBarOverlay">

    </android.support.design.widget.AppBarLayout>
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@android:color/white">

        <android.support.v7.widget.RecyclerView
            android:id="@+id/recyclerview"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:padding="5dp">
        </android.support.v7.widget.RecyclerView>
    </LinearLayout>

</android.support.design.widget.CoordinatorLayout>

ApiClient.java

public class ApiClient {
    public static String BASE_URL ="http://35.200.174.74/apis/";
    private static Retrofit retrofit;
    public static Retrofit getClient(){
        if(retrofit == null){
            retrofit = new Retrofit.Builder()
                    .baseUrl(BASE_URL)
                    .addConverterFactory(GsonConverterFactory.create())
                    .build();
        }
        return retrofit;
    }
}

Movie.java

public class Movie {
    @SerializedName("title")
    private String title;

    @SerializedName("image")
    private String imageUrl;

    public Movie(String title, String imageUrl) {
        this.title = title;
        this.imageUrl = imageUrl;
    }

    public String getTitle() {
        return title;
    }

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

    public String getImageUrl() {
        return imageUrl;
    }

    public void setImageUrl(String imageUrl) {
        this.imageUrl = imageUrl;
    }
}

ApiInterface.java

public interface ApiInterface {
    @GET("volley_array.json")
    Call&amp;lt;List&amp;lt;Movie&amp;gt;&amp;gt; getMovies();
}

Also, Add the data to Recyclerview in MainActivity.java,

recyclerView = (RecyclerView) findViewById(R.id.recyclerview);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
movieAdapter = new MovieAdapter();
recyclerView.setAdapter(movieAdapter);

movieList = new ArrayList<>();
ApiInterface apiService = ApiClient.getClient().create(ApiInterface.class);
Call<List<Movie>> call = apiService.getMovies();

call.enqueue(new Callback<List<Movie>>() {
    @Override
    public void onResponse(Call<List<Movie>> call, Response<List<Movie>> response) {
        movieList = response.body();
        Log.d("TAG","Response = "+movieList);
        movieAdapter.setMovieList(getApplicationContext(),movieList);
    }

    @Override
    public void onFailure(Call<List<Movie>> call, Throwable t) {
        Log.d("TAG","Response = "+t.toString());
    }
});

3. Adding Android Recyclerview Search Filter

Early, We added the data to the Recyclerview. Then, I am going to add the Searchview for the data in the Recyclerview.

1. Add new Menu Resource File.

goto File > New > Android Resource File.

menu resource file

Likewise, Enter menu file name. Press Enter. Now,  We created the menu file.

Add the Search Item to the menu file.

<menu 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"
    tools:context=".ui.MainActivity">
    <item
        android:id="@+id/action_search"
        android:icon="@drawable/ic_launcher_background"
        android:orderInCategory="100"
        android:title="@string/action_search"
        app:showAsAction="always"
        app:actionViewClass="android.support.v7.widget.SearchView" />
</menu>

2. In Addition, Inflate Searchview menu to the MainActivity. Add setOnQueryTextListener to the Searchview to get search String.Then,  based on the request string change the Recyclerview adapter.

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    getMenuInflater().inflate(R.menu.main_menu, menu);

    SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE);
    searchView = (SearchView) menu.findItem(R.id.action_search)
            .getActionView();
    searchView.setSearchableInfo(searchManager
            .getSearchableInfo(getComponentName()));
    searchView.setMaxWidth(Integer.MAX_VALUE);

    searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
        @Override
        public boolean onQueryTextSubmit(String query) {
            movieAdapter.getFilter().filter(query);
            return false;
        }

        @Override
        public boolean onQueryTextChange(String query) {
            movieAdapter.getFilter().filter(query);
            return false;
        }
    });
    return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == R.id.action_search) {
return true;
}

return super.onOptionsItemSelected(item);
}

Filterable

Android provide the Filterable class to filter the data by a filter (condition). Usually, the getFilter() method have to override in the adapter class in which the filter condition is provided to search through a list.

Public methods

abstract Filter –  getFilter() – Returns a filter that can be used to constrain data with a filtering pattern.

@Override
public Filter getFilter() {
    return new Filter() {
        @Override
        protected FilterResults performFiltering(CharSequence charSequence) {
            String charString = charSequence.toString();
            if (charString.isEmpty()) {
                movieListFiltered = movieList;
            } else {
                List<Movie> filteredList = new ArrayList<>();
                for (Movie movie : movieList) {
           if (movie.getTitle().toLowerCase().contains(charString.toLowerCase())) {
                        filteredList.add(movie);
                    }
                }
                movieListFiltered = filteredList;
            }

            FilterResults filterResults = new FilterResults();
            filterResults.values = movieListFiltered;
            return filterResults;
        }

        @Override
        protected void publishResults(CharSequence charSequence, FilterResults filterResults) {
            movieListFiltered = (ArrayList<Movie>) filterResults.values;

            notifyDataSetChanged();
        }
    };

Final MovieAdapter.java

public class MovieAdapter extends RecyclerView.Adapter<MovieAdapter.MyViewHolder> implements Filterable {

    private List<Movie> movieList;
    private List<Movie> movieListFiltered;
    private Context context;

    public void setMovieList(Context context,final List<Movie> movieList){
        this.context = context;
        if(this.movieList == null){
            this.movieList = movieList;
            this.movieListFiltered = movieList;
            notifyItemChanged(0, movieListFiltered.size());
        } else {
            final DiffUtil.DiffResult result = DiffUtil.calculateDiff(new DiffUtil.Callback() {
                @Override
                public int getOldListSize() {
                    return MovieAdapter.this.movieList.size();
                }

                @Override
                public int getNewListSize() {
                    return movieList.size();
                }

                @Override
                public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
                    return MovieAdapter.this.movieList.get(oldItemPosition).getTitle() == movieList.get(newItemPosition).getTitle();
                }

                @Override
                public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {

                    Movie newMovie = MovieAdapter.this.movieList.get(oldItemPosition);

                    Movie oldMovie = movieList.get(newItemPosition);

                    return newMovie.getTitle() == oldMovie.getTitle() ;
                }
            });
            this.movieList = movieList;
            this.movieListFiltered = movieList;
            result.dispatchUpdatesTo(this);
        }
    }

    @Override
    public MovieAdapter.MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {

        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.movielist_adapter,parent,false);
        return new MyViewHolder(view);
    }

    @Override
    public void onBindViewHolder(MovieAdapter.MyViewHolder holder, int position) {
        holder.title.setText(movieListFiltered.get(position).getTitle());
        Glide.with(context).load(movieList.get(position).getImageUrl()).apply(RequestOptions.centerCropTransform()).into(holder.image);
    }

    @Override
    public int getItemCount() {

        if(movieList != null){
            return movieListFiltered.size();
        } else {
            return 0;
        }
    }

    @Override
    public Filter getFilter() {
        return new Filter() {
            @Override
            protected FilterResults performFiltering(CharSequence charSequence) {
                String charString = charSequence.toString();
                if (charString.isEmpty()) {
                    movieListFiltered = movieList;
                } else {
                    List<Movie> filteredList = new ArrayList<>();
                    for (Movie movie : movieList) {
               if (movie.getTitle().toLowerCase().contains(charString.toLowerCase())) {
                            filteredList.add(movie);
                        }
                    }
                    movieListFiltered = filteredList;
                }

                FilterResults filterResults = new FilterResults();
                filterResults.values = movieListFiltered;
                return filterResults;
            }

            @Override
            protected void publishResults(CharSequence charSequence, FilterResults filterResults) {
                movieListFiltered = (ArrayList<Movie>) filterResults.values;

                notifyDataSetChanged();
            }
        };
    }

    public class MyViewHolder extends RecyclerView.ViewHolder {

        TextView title;
        ImageView image;

        public MyViewHolder(View view) {
            super(view);
            title = (TextView) view.findViewById(R.id.title);
            image = (ImageView)view.findViewById(R.id.image);
        }
    }
}

MainActivity.java

public class MainActivity extends AppCompatActivity {

    private SearchView searchView;
    private RecyclerView recyclerView;
    private MovieAdapter movieAdapter;
    private List<Movie> movieList;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        recyclerView = (RecyclerView) findViewById(R.id.recyclerview);
        recyclerView.setLayoutManager(new LinearLayoutManager(this));
        movieAdapter = new MovieAdapter();
        recyclerView.setAdapter(movieAdapter);

        movieList = new ArrayList<>();
        ApiInterface apiService = ApiClient.getClient().create(ApiInterface.class);
        Call<List<Movie>> call = apiService.getMovies();

        call.enqueue(new Callback<List<Movie>>() {
            @Override
            public void onResponse(Call<List<Movie>> call, Response<List<Movie>> response) {
                movieList = response.body();
                Log.d("TAG","Response = "+movieList);
                movieAdapter.setMovieList(getApplicationContext(),movieList);
            }

            @Override
            public void onFailure(Call<List<Movie>> call, Throwable t) {
                Log.d("TAG","Response = "+t.toString());
            }
        });
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.main_menu, menu);

        SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE);
        searchView = (SearchView) menu.findItem(R.id.action_search)
                .getActionView();
        searchView.setSearchableInfo(searchManager
                .getSearchableInfo(getComponentName()));
        searchView.setMaxWidth(Integer.MAX_VALUE);

        searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
            @Override
            public boolean onQueryTextSubmit(String query) {
                movieAdapter.getFilter().filter(query);
                return false;
            }

            @Override
            public boolean onQueryTextChange(String query) {
                movieAdapter.getFilter().filter(query);
                return false;
            }
        });
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        int id = item.getItemId();
        if (id == R.id.action_search) {
            return true;
        }

        return super.onOptionsItemSelected(item);
    }

    @Override
    public void onBackPressed() {
        if (!searchView.isIconified()) {
            searchView.setIconified(true);
            return;
        }
        super.onBackPressed();
    }
}

Screenshot

Android Recyclerview Search Filter exmaple

Android Recyclerview Search Filter download

11 Responses

  1. thanks sir for so good tutorial.
    But sir, i also make a Details Activity. When i click this list item the application has stop.
    So please Help, How to make Details activity in this tutorial base..

  2. Hello SIr,

    while typing in search box, only Title get filter. Image thumbnail remains same. It can’t change according to title.

    Please give me solution to filter with respective image thumbnail

  3. Wonderful blog! I found it while searching on Yahoo News. Do you have any suggestions on how to get listed in Yahoo News? I’ve been trying for a while but I never seem to get there! Appreciate it

  4. Pretty section of content. I just stumbled upon your site and in accession capital to
    assert that I acquire in fact enjoyed account your blog posts.

    Any way I’ll be subscribing to your feeds and even I
    achievement you access consistently rapidly.

  5. Hi.
    I waznt to tell you that I’m using this site ffor a lojg time and you write really good content.
    I shared your newset post on twitter and got a lot of
    posiktive feedbacks.
    Hope you put new content soon.

  6. Hi,
    I ant to tell you thhat I’m visiting this site for some
    time and you write really good content. I shared your last post on tumblr and got
    a lot of good feedbacks.
    Waiting for your next article

  7. Үou have made some good points therе. I ⅼooked ߋn thе internet for moге
    info ɑbout tһe issue and foᥙnd mοst individuals wіll go along with yߋur views on this
    website.

Leave a Reply

Your email address will not be published. Required fields are marked *

Back to Top