Recyclerview with search filter poster

How To Setup Recyclerview With Search Filter In Android ?

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

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

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

1. Creating New Project

  1. First of all, Create New Project in Android Studio.
  2. Add project dependencies Retrofit, Recyclerview, and Cardviewto 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>

2. Load Data From API to Recyclerview using Retrofit

Again, 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://velmm.com/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<List<Movie>> 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 a new Menu Resource File.

goto File > New > Android Resource File.

create menu file

Likewise, Enter the menu file name. Press Enter. Now, the menu file got Created.

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 the Searchview menu to the MainActivity. Add setOnQueryTextListener to the Searchview to get the 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);
    }

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

@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();
        }

That’s it.

You can download this example from GITHUB.


Comments

Leave a Reply

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


Latest Posts