Android ListView - Search/Filter

android search filter

In this example, we'll see how to search filter an android listview.

We'll use a SearchView component.

GRADLE FILES

We'll edit the app level build.gadle adding some dependencies.

build.gradle

  • Our ListView will comprise cardviews so we add cardview dependency.
    apply plugin: 'com.android.application'

    android {
        compileSdkVersion 23
        buildToolsVersion "23.0.2"

        defaultConfig {
            applicationId "com.tutorials.hp.lvfilter"
            minSdkVersion 15
            targetSdkVersion 23
            versionCode 1
            versionName "1.0"
        }
        buildTypes {
            release {
                minifyEnabled false
                proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            }
        }
    }

    dependencies {
        compile fileTree(dir: 'libs', include: ['*.jar'])
        testCompile 'junit:junit:4.12'
        compile 'com.android.support:appcompat-v7:23.2.1'
        compile 'com.android.support:design:23.2.1'
        compile 'com.android.support:cardview-v7:23.2.1'
    }

LAYOUTS

We'll have these three layouts in our project:

1. activity_main.xml

    <?xml version="1.0" encoding="utf-8"?>
    <android.support.design.widget.CoordinatorLayout 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"
        android:fitsSystemWindows="true"
        tools:context="com.tutorials.hp.lvfilter.MainActivity">

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

            <android.support.v7.widget.Toolbar
                android:id="@+id/toolbar"
                android:layout_width="match_parent"
                android:layout_height="?attr/actionBarSize"
                android:background="?attr/colorPrimary"
                app:popupTheme="@style/AppTheme.PopupOverlay" />

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

        <include layout="@layout/content_main" />

        <android.support.design.widget.FloatingActionButton
            android:id="@+id/fab"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="bottom|end"
            android:layout_margin="@dimen/fab_margin"
            android:src="@android:drawable/ic_dialog_email" />

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

2. content_main.xml

  • This layout will hold our ListView and SearchView.
    <?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"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:paddingBottom="@dimen/activity_vertical_margin"
        android:paddingLeft="@dimen/activity_horizontal_margin"
        android:paddingRight="@dimen/activity_horizontal_margin"
        android:paddingTop="@dimen/activity_vertical_margin"
        app:layout_behavior="@string/appbar_scrolling_view_behavior"
        tools:context="com.tutorials.hp.lvfilter.MainActivity"
        tools:showIn="@layout/activity_main">

        <android.support.v7.widget.SearchView
            android:id="@+id/mSearch"
            android:layout_width="match_parent"
            android:layout_height="50dp"

            app:defaultQueryHint="Search.."

            />
        <ListView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:id="@+id/lv"
            android:layout_below="@+id/mSearch"
            android:layout_alignParentLeft="true"
            android:layout_alignParentStart="true" />
    </RelativeLayout>

3. model.xml

  • This layout will define the model for our custom rows.
  • Our ListView will have images and text.
    <?xml version="1.0" encoding="utf-8"?>
    <android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
        android:orientation="horizontal" android:layout_width="match_parent"
        xmlns:card_view="http://schemas.android.com/apk/res-auto"
        android:layout_margin="10dp"
        card_view:cardCornerRadius="10dp"
        card_view:cardElevation="10dp"

        android:layout_height="wrap_content">

        <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent">

            <ImageView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:id="@+id/movieImage"
                android:padding="10dp"
                android:src="@drawable/ghost" />

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:textAppearance="?android:attr/textAppearanceLarge"
                android:text="Name"
                android:id="@+id/nameTxt"
                android:padding="10dp"
                android:textColor="@color/colorAccent"
                android:layout_below="@+id/movieImage"
                android:layout_alignParentLeft="true"
                 />

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:textAppearance="?android:attr/textAppearanceLarge"
                android:text=" John Doe a former FBI Agent and now Physics teacher .is wrongly accussed of murdering an innocent child.He makes it his business to find the bad guys who did taht.He convinces hacker Aram to join him.....
                "
                android:id="@+id/descTxt"
                android:padding="10dp"
                android:layout_below="@+id/nameTxt"
                android:layout_alignParentLeft="true"
                />

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:textAppearance="?android:attr/textAppearanceMedium"
                android:text="TV Show"
                android:id="@+id/posTxt"
                android:padding="10dp"

                android:layout_below="@+id/movieImage"
                android:layout_alignParentRight="true" />

            <CheckBox
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:id="@+id/chk"
                android:layout_alignParentRight="true"
                />

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

JAVA CLASSES

Here are our Java classes:

1. Movie.java

  • Our data object.
  • Will represent a single movies.
    package com.tutorials.hp.lvfilter;

    public class Movie {

        private String name;
        private int image;

        public Movie(String name, int image) {
            this.name = name;
            this.image = image;
        }

        public String getName() {
            return name;
        }

        public int getImage() {
            return image;
        }
    }

2. ItemClickListner

  • An interface.
  • Defines the signature for our OnItemClick() method.
    package com.tutorials.hp.lvfilter;

    import android.view.View;

    public interface ItemClickListener {

        void onItemClick(View v);
    }

3. MyViewHolder.java

  • View holder class.
    package com.tutorials.hp.lvfilter;

    import android.view.View;
    import android.widget.ImageView;
    import android.widget.TextView;

    public class MyViewHolder implements View.OnClickListener {

        ImageView img;
        TextView nameTxt;
        ItemClickListener itemClickListener;

        public MyViewHolder(View v) {
            img= (ImageView) v.findViewById(R.id.movieImage);
            nameTxt= (TextView) v.findViewById(R.id.nameTxt);

            v.setOnClickListener(this);
        }

        @Override
        public void onClick(View v) {
            this.itemClickListener.onItemClick(v);
        }

        public void setItemClickListener(ItemClickListener ic)
        {
            this.itemClickListener=ic;
        }
    }

4. MyAdapter.java

  • Our adapter class.
  • Derives from BaseAdapter.
  • Implements Filterable.
    package com.tutorials.hp.lvfilter;

    import android.content.Context;
    import android.view.LayoutInflater;
    import android.view.View;
    import android.view.ViewGroup;
    import android.widget.BaseAdapter;
    import android.widget.Filter;
    import android.widget.Filterable;
    import android.widget.Toast;

    import java.util.ArrayList;

    public class MyAdapter extends BaseAdapter implements Filterable {

        Context c;
        ArrayList<Movie> movies;
        LayoutInflater inflater;

        ArrayList<Movie> filterList;
        CustomFilter filter;

        public MyAdapter(Context c, ArrayList<Movie> movies) {
            this.c = c;
            this.movies = movies;
            this.filterList=movies;
        }

        //TOTLA NUM OF MOVIES
        @Override
        public int getCount() {
            return movies.size();
        }

        //GET A SINGLE MOVIE
        @Override
        public Object getItem(int position) {
            return movies.get(position);
        }

        //IDENTITDIER
        @Override
        public long getItemId(int position) {
            return position;
        }

        @Override
        public View getView(final int position, View convertView, ViewGroup parent) {

            if(inflater==null)
            {
                inflater= (LayoutInflater) c.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            }

            //PERFORM INFLATION
            if(convertView==null)
            {
                convertView=inflater.inflate(R.layout.model,null);
            }

            //BIND DATA TO VIEWS
            MyViewHolder holder=new MyViewHolder(convertView);
            holder.nameTxt.setText(movies.get(position).getName());
            holder.img.setImageResource(movies.get(position).getImage());

            holder.setItemClickListener(new ItemClickListener() {
                @Override
                public void onItemClick(View v) {
                    Toast.makeText(c,movies.get(position).getName(),Toast.LENGTH_SHORT).show();
                }
            });

            //RETURN A ROW
            return convertView;
        }

        @Override
        public Filter getFilter() {

            if(filter==null)
            {
                filter=new CustomFilter(filterList,this);
            }

            return filter;
        }
    }

5. CustomFilter.java

  • This class is responsible for our custom filtering.
  • Derives from android.widget.Filter.
    package com.tutorials.hp.lvfilter;

    import android.widget.Filter;

    import java.util.ArrayList;

    public class CustomFilter extends Filter {

        ArrayList<Movie> filterList;
        MyAdapter adapter;

        public CustomFilter(ArrayList<Movie> filterList, MyAdapter adapter) {
            this.filterList = filterList;
            this.adapter = adapter;
        }

        //FILTERING
        @Override
        protected FilterResults performFiltering(CharSequence constraint) {

            //RESULTS
            FilterResults results=new FilterResults();

            //VALIDATION
            if(constraint != null && constraint.length()>0)
            {

                //CHANGE TO UPPER FOR CONSISTENCY
                constraint=constraint.toString().toUpperCase();

                ArrayList<Movie> filteredMovies=new ArrayList<>();

                //LOOP THRU FILTER LIST
                for(int i=0;i<filterList.size();i++)
                {
                    //FILTER
                    if(filterList.get(i).getName().toUpperCase().contains(constraint))
                    {
                        filteredMovies.add(filterList.get(i));
                    }
                }

                results.count=filteredMovies.size();
                results.values=filteredMovies;
            }else
            {
                results.count=filterList.size();
               results.values=filterList;
            }

            return results;
        }

        //PUBLISH RESULTS

        @Override
        protected void publishResults(CharSequence constraint, FilterResults results) {

            adapter.movies= (ArrayList<Movie>) results.values;
            adapter.notifyDataSetChanged();

        }
    }

6. MainActivity.java

  • Our launcher activity.
  • References both our ListView and SearchView.
  • Then sets the adapter to ListView.
  • Listens to OnQueryTextChange events on the searchview.
    package com.tutorials.hp.lvfilter;

    import android.os.Bundle;
    import android.support.design.widget.FloatingActionButton;
    import android.support.design.widget.Snackbar;
    import android.support.v7.app.AppCompatActivity;
    import android.support.v7.widget.SearchView;
    import android.support.v7.widget.Toolbar;
    import android.view.View;
    import android.view.Menu;
    import android.view.MenuItem;
    import android.widget.AdapterView;
    import android.widget.ListView;
    import android.widget.Toast;

    import java.util.ArrayList;

    public class MainActivity extends AppCompatActivity {

        SearchView sv;
        ListView lv;
        MyAdapter adapter;

        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
            setSupportActionBar(toolbar);

            FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
            fab.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
                            .setAction("Action", null).show();
                }
            });

            sv= (SearchView) findViewById(R.id.mSearch);
            lv= (ListView) findViewById(R.id.lv);

            adapter=new MyAdapter(this,getMovies());
            lv.setAdapter(adapter);

            sv.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
                @Override
                public boolean onQueryTextSubmit(String query) {
                    return false;
                }

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

        }

        private ArrayList<Movie> getMovies() {
            //COLECTION OF CRIME MOVIES
            ArrayList<Movie> movies=new ArrayList<>();

            Movie movie=new Movie("BlackList",R.drawable.red);

            //ADD ITR TO COLLECTION
            movies.add(movie);

            movie=new Movie("Fruts",R.drawable.fruits);
            movies.add(movie);

            movie=new Movie("Breaking Bad",R.drawable.breaking);
            movies.add(movie);

            movie=new Movie("Crisis",R.drawable.crisis);
            movies.add(movie);

            movie=new Movie("Ghost Rider",R.drawable.rider);
            movies.add(movie);

            movie=new Movie("Star Wars",R.drawable.starwars);
            movies.add(movie);

            movie=new Movie("Shuttle Carrier",R.drawable.shuttlecarrier);
            movies.add(movie);

            movie=new Movie("Men In Black",R.drawable.meninblack);
            movies.add(movie);

            movie=new Movie("Game Of Thrones",R.drawable.thrones);
            movies.add(movie);

            return movies;
        }

    }

How do You Feel after reading this?

According to scientists, we humans have 8 primary innate emotions: joy, acceptance, fear, surprise, sadness, disgust, anger, and anticipation. Feel free to tell us how you feel about this article using these emotes or via the comment section. This feedback helps us gauge our progress.

Help me Grow.

I set myself some growth ambitions I desire to achieve by this year's end regarding this website and my youtube channel. Am halfway. Help me reach them by:




Recommendations


What do You Think


Previous Post Next Post