Android

Android Filter Recyclerview by using SearchView in ToolBar or actionbar

In this tutorial, we will learn how can we filter the recyclerview item by using the Search view in toolbar or action bar? Recyclerview is the major used component in building the android application. And if the data is very huge then we can not find the specific item by scroll. So we need to filter by name while entering the text on search view. SearchView is an awesome component that provides the search with animated expand and collapse action.

Here is the recommended video which I have implemented search view in this app. Please check this video for better understanding.

Let’s integrate this feature in the sample project. Very first thing we need to add relevant dependencies in your build.gradle file.

 compile 'com.android.support:recyclerview-v7:26.0.1'
 compile 'com.android.support:cardview-v7:26.0.1'
 compile 'com.android.support:design:26.0.1'

Now I need to create the menu_main.xml of item search inside the res/menu folder.

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">
    <item android:id="@+id/action_search"
        android:title="@string/app_name"
        app:actionViewClass="android.support.v7.widget.SearchView"
        android:icon="@android:drawable/ic_menu_search"
        app:showAsAction="always|collapseActionView"
        />
</menu>

Let’s load the data on recyclerview from a database. I am not going into the deep to load the data from the database. here I suppose that I have fetched the data of the table and loaded into List. As everyone knows that we need to bind the recyclerview item by an adapter. Let’s create the adapter and resource layout to inflate the data on view.

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <android.support.v7.widget.CardView
        app:cardCornerRadius="0dp"
        android:layout_margin="3dp"
        app:cardUseCompatPadding="true"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <RelativeLayout
            android:padding="8dp"
            android:id="@+id/relative"
            android:layout_width="match_parent"
            android:layout_height="match_parent">

            <ImageView
                android:src="@drawable/movie_icon"
                android:id="@+id/imageView"
                android:layout_width="match_parent"
                android:layout_height="200dp" />

            <RelativeLayout
                android:id="@+id/info"
                android:padding="5dp"
                android:layout_below="@+id/imageView"
                android:layout_width="match_parent"
                android:layout_height="wrap_content">

                <TextView
                    android:id="@+id/name"
                    android:textSize="15sp"
                    android:textStyle="bold"
                    android:text="@string/app_name"
                    android:ellipsize="end"
                    android:singleLine="true"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content" />

                <TextView
                    android:textSize="13sp"
                    android:id="@+id/releasyear"
                    android:text="@string/app_name"
                    android:layout_below="@+id/name"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content" />

                <ImageView
                    android:layout_below="@+id/releasyear"
                    android:id="@+id/star"
                    android:src="@drawable/ic_stars_yellow_900_24dp"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content" />

                <TextView
                    android:id="@+id/rating"
                    android:layout_below="@+id/releasyear"
                    android:layout_toRightOf="@+id/star"
                    android:layout_centerVertical="true"
                    android:layout_marginTop="3dp"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content" />

            </RelativeLayout>

        </RelativeLayout>

    </android.support.v7.widget.CardView>

</RelativeLayout>

Let’s use this layout for my adapter.

public class ImDbAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {

    private List<IMBD> itemModels;
    private Context context;
    private onItemClickListener listener;

    public ImDbAdapter(Context context, List<IMBD> imbdList, Fragment fragment) {
        this.itemModels = imbdList;
        this.context = context;
        this.listener = (onItemClickListener) fragment;
    }

    @Override
    public int getItemCount() {
        return itemModels.size();
    }


    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
        View itemView = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.item_layout, viewGroup, false);
        return new ItemViewHolder(itemView);
    }

    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
        IMBD model = itemModels.get(position);
        initializeViews(model, holder, position);
    }


    private void initializeViews(final IMBD model, final RecyclerView.ViewHolder holder, final int position) {
        ((ItemViewHolder)holder).name.setText(model.getName());
        ((ItemViewHolder)holder).releasyear.setText("Release Year: "+model.getYearofrelease());
        ((ItemViewHolder)holder).rating.setText(""+model.getRating());
        ((ItemViewHolder)holder).relative.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                listener.itemDetailClick(model);
            }
        });
        String imageUrl = model.getImageurl();
        if (imageUrl != null && !imageUrl.isEmpty()){
            Picasso.with(context)
                    .load(imageUrl)
                    .placeholder(R.drawable.movie_icon)
                    .error(R.drawable.movie_icon)
                    .into(((ItemViewHolder)holder).imageView);
        }
    }


    public static class ItemViewHolder extends RecyclerView.ViewHolder {

        @BindView(R.id.name)
        TextView name;
        @BindView(R.id.releasyear)
        TextView releasyear;
        @BindView(R.id.rating)
        TextView rating;
        @BindView(R.id.imageView)
        ImageView imageView;
        @BindView(R.id.relative)
        RelativeLayout relative;

        public ItemViewHolder(View itemView) {
            super(itemView);
            ButterKnife.bind(this, itemView);
        }
    }

    public interface onItemClickListener{
        public void itemDetailClick(IMBD imbd);
    }

    public void setSearchResult(List<IMBD> result) {
        itemModels = result;
        notifyDataSetChanged();
    }
}

Here I am using the fragment So we need to enable the menu option in onCreate() of Fragment and override the onCreateOptionMenu() to allow searches to filter the recycled view data by using SearchView.

I used MVP design pattern with Dagger2 dependency injection in this sample project. If you want to get the detail of MVP design pattern in Java then Please check this post MVP architectural design pattern with RxJava. Here is the detail about the Dagger2 Dependency Injection.

public class ImdbListFragment extends Fragment implements ImdbListContract.View, ImDbAdapter.onItemClickListener, SearchView.OnQueryTextListener{

    @BindView(R.id.recyclerView)
    RecyclerView recyclerView;

    @Inject
    ImdbListContract.Presenter mPresenter;
    private ImDbAdapter mImDbAdapter;
    private List<IMBD> mListImDb;

    public static ImdbListFragment newInstance() {
        return new ImdbListFragment();
    }

    @Override
    public void onResume() {
        super.onResume();
        mPresenter.subscribe();
    }

    @Override
    public void onPause() {
        super.onPause();
        mPresenter.unSubscribe();
    }

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);

        // do things if you want to create only first time when activity created.
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        injectDependency();
        mPresenter.attachView(this);
        setHasOptionsMenu(true);
    }

    private void injectDependency() {
        ApplicationComponent applicationComponent = ((MainApplication) getActivity().getApplication()).getApplicationComponent();
        ImdbListComponent imdbListComponent = DaggerImdbListComponent.builder()
                .applicationComponent(applicationComponent)
                .imdbListModule(new ImdbListModule())
                .build();
        imdbListComponent.inject(this);

    }

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        View root = inflater.inflate(R.layout.fragment_imdb_list, container, false);
        ButterKnife.bind(this, root);
        if (mPresenter != null) {
            if (mPresenter.getCountDb() > 0) {
                mPresenter.loadImDbDb();
            } else {
                Log.v("", "Error in loading.");
            }
        }

        return root;
    }

    @Override
    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
        inflater.inflate(R.menu.menu_main, menu);

        final MenuItem item = menu.findItem(R.id.action_search);
        final SearchView searchView = (SearchView) MenuItemCompat.getActionView(item);
        searchView.setOnQueryTextListener(this);

        item.setOnActionExpandListener( new MenuItem.OnActionExpandListener() {

            @Override
            public boolean onMenuItemActionExpand(MenuItem item) {

                return true;
            }

            @Override
            public boolean onMenuItemActionCollapse(MenuItem item) {
                // Do something when collapsed
                mImDbAdapter.setSearchResult(mListImDb);
                return true; // Return true to collapse action view

            }
        });
    }

    @Override
    public boolean onQueryTextSubmit(String query) {
        return false;
    }

    @Override
    public boolean onQueryTextChange(String newText) {
        final List<IMBD> filteredModelList = filter(mListImDb, newText);
        mImDbAdapter.setSearchResult(filteredModelList);
        return true;
    }

    private List<IMBD> filter(List<IMBD> models, String query) {
        query = query.toLowerCase();
        final List<IMBD> filteredModelList = new ArrayList<>();
        for (IMBD model : models) {
            final String text = model.getName().toLowerCase();
            if (text.contains(query)) {
                filteredModelList.add(model);
            }
        }
        return filteredModelList;
    }

    @Override
    public void onImDbOk(List<IMBD> imbdList) {
        mListImDb = imbdList;
        mImDbAdapter = new ImDbAdapter(getActivity(), imbdList, this);
        recyclerView.setLayoutManager(new GridLayoutManager(getActivity(), 2));
        recyclerView.setAdapter(mImDbAdapter);
    }

    @Override
    public void showLoadErrorMessage(String errorMsg) {
        Toast.makeText(getActivity(), errorMsg, Toast.LENGTH_LONG).show();
    }

    @Override
    public void showEmptyView(boolean isShow) {
        // show error view if any
    }

    @Override
    public void itemDetailClick(IMBD imbd) {
        String  imbdDetailLink = imbd.getDetailLink();
       
    }
}

Wrapping Up: This is the great implementation of RecylerView item filter by using the SearchView in ToolBar. Let’s use this feature in your app to make your application more awesome and powerful.

Here is the Github link for full source code.

Please do subscribe your email to get the every newsletter from this blog and if you feel that this post helps you then do not forget to share and comment below.

You can learn all articles of MobologicPlus from the android app, which is available to download below.

[appbox googleplay  mobi.androapp.mobologicplus.c7929]

Happy Coding 🙂

5 2 votes
Article Rating

Recent Posts

Hide your production API key or any sensitive data in Android

Hi everyone, In this article, we are going to learn how to hide the production… Read More

2 years ago

How to handle the localisation or multi language support in android with examples?

Hello everyone, Today in this article, we are going to learn about localisation to support… Read More

2 years ago

How to convert any callback to Coroutines and use them in Kotlin Android?

Hello everyone, In this article, we are going to learn something to handle the callback… Read More

2 years ago

Request Permission Launcher with Kotlin in Android

In this article, we are learning about the run time permissions for request permission launchers.… Read More

2 years ago

Implement the SMS User Consent API and SMS Retriever API in Android

Hello everyone. In my last tutorial, we learned about the Jetpack Compose introduction and about applying the… Read More

3 years ago

Jetpack Compose Coroutine flow with LiveData/ViewModel in Android

Hello everyone, In this article, we are going to learn about the Jetpack Compose with… Read More

3 years ago