Saturday 4 June 2011

Android Custom List View Filter

This example demonstrates how to create a search filter for your custom list view i.e. listview created by using base adapter.

 TestFilterListView is the activity which is having the code for creating listview and search filter option.


import java.util.ArrayList;
import java.util.List;

import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewStub;
import android.widget.BaseAdapter;
import android.widget.EditText;
import android.widget.FrameLayout;
import android.widget.ListView;
import android.widget.TextView;


public class TestFilterListView extends Activity {
    FrameLayout historyContainer;
    ViewStub viewStub;
   
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.history_container);
        historyContainer = (FrameLayout) findViewById(R.id.historyContainerLayout);
        EditText filterEditText = (EditText) findViewById(R.id.filter_text);
        filterEditText.addTextChangedListener(new TextWatcher() {
           
            @Override
            public void onTextChanged(CharSequence s, int start, int before, int count) {
               
                historyContainer.removeAllViews();
                final List<String> tempHistoryList = new ArrayList<String>();
                tempHistoryList.addAll(historyList);
                for(String data : historyList) {
                    if(data.indexOf((s.toString())) == -1) {
                        tempHistoryList.remove(data);
                    }
                }
                viewStub = new ViewStub(TestFilterListView.this, R.layout.history_schedule);
                viewStub.setOnInflateListener(new ViewStub.OnInflateListener()
                {
                    public void onInflate(ViewStub stub, View inflated)
                    {
                       
                        setUIElements(inflated, tempHistoryList);
                    }
                });
                historyContainer.addView(viewStub);
                viewStub.inflate();
               
            }
           
            @Override
            public void beforeTextChanged(CharSequence s, int start, int count,
                    int after) {
                // TODO Auto-generated method stub
               
            }
           
            @Override
            public void afterTextChanged(Editable s) {
                // TODO Auto-generated method stub
               
            }
        });
       
        setViewStub();
    }


    /********************************************************************************************************/
    private void setViewStub()
    {
        historyList.add("first");
        historyList.add("second");
        historyList.add("third");
        historyList.add("fourth");
        historyList.add("fifth");
        historyList.add("sixth");
        historyList.add("seventh");
       
       
        viewStub = new ViewStub(TestFilterListView.this, R.layout.history_schedule);
        viewStub.setOnInflateListener(new ViewStub.OnInflateListener()
        {
            public void onInflate(ViewStub stub, View inflated)
            {
               
                setUIElements(inflated, historyList);
            }
        });
        historyContainer.addView(viewStub);
        viewStub.inflate();
    }
   
    /********************************************************************************************************/
    final List<String> historyList = new ArrayList<String>();
    String displayName = "";
    ListView historyListView;
    private void setUIElements(View v, List<String> historyLists)
    {
       
        if (v != null)
        {
            historyScheduleData.clear();
            //historyList.clear();
           
            historyScheduleData.addAll(historyLists);
            historyListView = (ListView) findViewById(R.id.historylist);
            historyListView.setAdapter(new BeatListAdapter(this));
           
            registerForContextMenu(historyListView);
           

        }
    }
   
    /********************************************************************************************************/
    private static class BeatListAdapter extends BaseAdapter {
        private LayoutInflater mInflater;

        public BeatListAdapter(Context context) {
            mInflater = LayoutInflater.from(context);

        }

        public int getCount() {
            return historyScheduleData.size();
        }

        public Object getItem(int position) {
            return position;
        }

        public long getItemId(int position) {
            return position;
        }

        public View getView(int position, View convertView, ViewGroup parent) {
            ViewHolder holder;
            if (convertView == null) {
                convertView = mInflater.inflate(R.layout.history_list_view, null);
                holder = new ViewHolder();
                holder.historyData = (TextView) convertView
                        .findViewById(R.id.historytext);
               
                convertView.setTag(holder);
            } else {
                holder = (ViewHolder) convertView.getTag();
            }

            holder.historyData.setText(historyScheduleData.get(position));

            return convertView;
        }

        static class ViewHolder {

            TextView historyData;
        }
    }
   
    private static final List<String> historyScheduleData = new ArrayList<String>();
   
   
}
 








Here in the code you can clearly see this is a list view which is created using base adapter hence you can modify it in whichever way you want i.e. custom listview

  Here, historyList is the main list which is holding the data. historyList  will be filtered by pressing key in edit text,  onTextChanged is the listener method of textwatcher interface which gets called when we change the text of edit text here filterEditText.


List with filtered data will be in your tempHistoryList which is also using the same adapter view hence even after modification of list same adapter will be recreated.




Here is the code of layouts that has been used.

history_container.xml



<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent" android:layout_height="fill_parent"
    android:orientation="vertical">
    <EditText android:layout_width="fill_parent" android:layout_height="wrap_content"
        android:id="@+id/filter_text" />
    <FrameLayout android:layout_width="fill_parent"
        android:layout_height="fill_parent" android:id="@+id/historyContainerLayout" />
   
   
</LinearLayout>

This layout is the main container means the main layout which will hold your listview and framelayout etc you may add others as per your requirement.


history_list_view.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_height="wrap_content" android:gravity="left|center"
    android:layout_width="wrap_content" android:paddingBottom="5px"
    android:paddingTop="5px" android:paddingLeft="5px">
    <TextView android:text="@+id/historytext" android:id="@+id/historytext"
        android:layout_width="wrap_content" android:layout_height="wrap_content"
        android:layout_marginLeft="10px" android:textColor="#0099CC"></TextView>
</LinearLayout>

This layout contains all the widgets which should be there in listview in our case it is only textview.We may eaisly add widget to listview as it is in xml so easy to maintain or modify.
Thanks to the powerful architecture of android.


history_schedule.xml


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="fill_parent"
    android:layout_height="fill_parent">
    <ScrollView android:id="@+id/scrollItemInfo"
        android:layout_width="fill_parent" android:layout_height="1000dip">
        <LinearLayout android:orientation="vertical"
            android:layout_width="fill_parent" android:layout_height="fill_parent">
                <ListView android:id="@+id/historylist" android:layout_height="1000dip"
                         android:layout_width="fill_parent">
                 </ListView>
        </LinearLayout>
    </ScrollView>
</LinearLayout>

This layout is having the widgets which should be in frame layout here we have added a listview this is the contents which will be refreshed every time we  press any key in filter edit text.The layouts are separated so that readers may easily customize in their own way.