Custom getFilter in custom ArrayAdapter in android
Asked Answered
C

7

42

I am having trouble implementing custom getFilter in custom arrayAdapter. Actually I have no idea how to implement it. Tried various codes but still no luck. Here is my custom array adapter.

package com.test.FilterableList.Adapters;

import java.util.ArrayList;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.TextView;

import com.test.FilterableList.Models.ListTO;
import com.test.FilterableList.R;

import android.widget.Filterable;


public class FilterableAdapter extends ArrayAdapter<ListTO> implements Filterable {

    // declaring our ArrayList of items
    public ArrayList<ListTO> objects;

    /* here we must override the constructor for ArrayAdapter
    * the only variable we care about now is ArrayList<Item> objects,
    * because it is the list of objects we want to display.
    */
    public FilterableAdapter(Context context, int textViewResourceId, ArrayList<ListTO> objects) {
        super(context, textViewResourceId, objects);
        this.objects = objects;
    }

    /*
     * we are overriding the getView method here - this is what defines how each
     * list item will look.
     */
    public View getView(int position, View convertView, ViewGroup parent){

        // assign the view we are converting to a local variable
        View v = convertView;

        // first check to see if the view is null. if so, we have to inflate it.
        // to inflate it basically means to render, or show, the view.
        if (v == null) {
            LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            v = inflater.inflate(R.layout.list_item, null);
        }

        /*
         * Recall that the variable position is sent in as an argument to this method.
         * The variable simply refers to the position of the current object in the list. (The ArrayAdapter
         * iterates through the list we sent it)
         *
         * Therefore, i refers to the current Item object.
         */
        ListTO i = objects.get(position);

        if (i != null) {

            // This is how you obtain a reference to the TextViews.
            // These TextViews are created in the XML files we defined.

            TextView tt = (TextView) v.findViewById(R.id.list_name);
            if (tt != null){
                tt.setText(i.FileName);
            }



        }

        // the view must be returned to our activity
        return v;

    }
}

And Here is the ListTO class.

package com.test.FilterableList.Models;

public class ListTO {

    public int Id;
    public String FileName;
    public String FileUri;

    public ListTO(int id, String fileName, String fileUri) {

        Id = id;
        FileName = fileName;
        FileUri = fileUri;

    }

}

And here is the layout.

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:background="@drawable/blacklikenbackground"
    tools:context=".AllListActivity" >

    <EditText
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="Search"
        android:id="@+id/inputSearch"
        />


    <ListView
        android:id="@+id/test_list"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
    </ListView>

</LinearLayout>

Here the search keyword comes from "inputSearch" EditText.

Here's the text changed listener.

 inputSearch.addTextChangedListener(new TextWatcher() {

                    @Override
                    public void onTextChanged(CharSequence cs, int arg1, int arg2, int arg3) {
                        // When user changed the Text
                      //  Toast.makeText(getActivity(), cs.toString(), Toast.LENGTH_LONG).show();
                        m_adapter.getFilter().filter(cs);
                    }

                    @Override
                    public void beforeTextChanged(CharSequence arg0, int arg1, int arg2,
                                                  int arg3) {
                        // TODO Auto-generated method stub

                    }

                    @Override
                    public void afterTextChanged(Editable arg0) {
                        // TODO Auto-generated method stub
                    }
                });

Thanks.

Cormack answered 1/10, 2013 at 18:0 Comment(4)
what exactly do you want to achieve? Where will the filter data come from and what do you want the filter to do?Danie
I'd also suggest that you take a look at this excellent answer: https://mcmap.net/q/391708/-how-to-implement-getfilter-with-custom-adapter-that-extends-baseadapter - it may be what you are looking for.Danie
@Danie Sorry the layout has been added to the question. Actually the Search keyword comes from the "inputSearch" EditText. I want to implement auto suggest like feature used in google; But here from the list view.Cormack
ArrayAdapter abstract all that functionality, you just need to override toString() on ListTO. See its implementation grepcode.com/file/repository.grepcode.com/java/ext/… @chandu tangudu answer is the correct oneTheroid
F
47

You are having problem, mainly because you are using custom object. If you pass a String or int value to array adapter its know how to filter it. But if you pass custom object default filter implementation have to no idea how to deal with that.

Although it is not clear what you are trying to do in your filter i recommend you following steps.

  1. Proper implementation of ListTO, although it has nothing to do with your goal right now
  2. Implement custom filter
  3. return your filter

Implement custom filter

First thing you have to do is, implements Filterable from your array adapter.

Second, provide implementation of your Filter

Filter myFilter = new Filter() {
        @Override
        protected FilterResults performFiltering(CharSequence constraint) {
         FilterResults filterResults = new FilterResults();   
         ArrayList<ListTO> tempList=new ArrayList<ListTO>();
         //constraint is the result from text you want to filter against. 
         //objects is your data set you will filter from
         if(constraint != null && objects!=null) {
             int length=objects.size();
             int i=0;
                while(i<length){
                    ListTO item=objects.get(i);
                    //do whatever you wanna do here
                    //adding result set output array     

                    tempList.add(item);

                    i++;
                }
                //following two lines is very important
                //as publish result can only take FilterResults objects
                filterResults.values = tempList;
                filterResults.count = tempList.size();
          }
          return filterResults;
      }

      @SuppressWarnings("unchecked")
      @Override
      protected void publishResults(CharSequence contraint, FilterResults results) {
          objects = (ArrayList<ListTO>) results.values;
          if (results.count > 0) {
           notifyDataSetChanged();
          } else {
              notifyDataSetInvalidated();
          }  
      }
     };

Last step,

@Override
     public Filter getFilter() {
        return myFilter;
    }
Funicular answered 10/10, 2013 at 16:24 Comment(8)
Would you be able to assist with my issue here: #20524917. I followed the method but the ListView just disappears.Burnaby
@Funicular I have implemented your solution and it works well, However I am having an issue, If I search for a string and then back space back to nothing it doesn't show me ALL the array items.Mesentery
I am also facing the same issue, when I back space nothing works.Caput
@Funicular one thing i dont understand is you are re-assigning objects in publishResults and thus you lose the original list??Publication
Do this:- if (searchquery.length() < 1) { //set original adapter to listview }Greenman
I am using this for an AutoCompleteTextView, but the list of suggestions doesn't appear now :/Moneylender
for a working Kotlin version, see my answer in this post.Domiciliate
The code was none working code, I corrected it for copy&pasteAngulo
D
10

No need to write array adapter. write a toString() method which should return the value of filename.

Like

public class ListTO {

    public int Id;
    public String FileName;
    public String FileUri;

    public ListTO(int id, String fileName, String fileUri) {

        Id = id;
        FileName = fileName;
        FileUri = fileUri;

    }

    public String toString(){
        return FileName
    }

}
Deforce answered 3/4, 2014 at 12:9 Comment(3)
This is a great, and simple solution if you just need a basic search. +1Commutative
This is not just great, this is the no reinvent the wheel correct answerTheroid
Very Good for implementing a Simple SearchAurelio
C
8

You need to override the getFilter() method in the Adapter and provide your own filter. Take a look in this Filterable Example to see an actual implementation.

Add the following getFilter() code to your FilterableAdapter class and fill it with your filtering:

/* (non-Javadoc)
 * @see android.widget.ArrayAdapter#getFilter()
 */
@Override
public Filter getFilter() {
    return new Filter() {

        /* (non-Javadoc)
         * @see android.widget.Filter#performFiltering(java.lang.CharSequence)
         */
        @Override
        protected FilterResults performFiltering(CharSequence constraint) {
            // TODO Auto-generated method stub
            /*
             * Here, you take the constraint and let it run against the array
             * You return the result in the object of FilterResults in a form
             * you can read later in publichResults.
             */
            return null;
        }

        /* (non-Javadoc)
         * @see android.widget.Filter#publishResults(java.lang.CharSequence, android.widget.Filter.FilterResults)
         */
        @Override
        protected void publishResults(CharSequence constraint, FilterResults results) {
            // TODO Auto-generated method stub
            /*
             * Here, you take the result, put it into Adapters array
             * and inform about the the change in data.
             */
        }

    };
}

I've added hints what to do in the comments.

Couplet answered 4/10, 2013 at 6:55 Comment(0)
S
5

try this:

     public class Adptr extends BaseAdapter implements Filterable {
public ArrayList<Model> modelValues;

private Activity activity;
private LayoutInflater layoutinflater;
private List<Model> mOriginalValues;
private int PositionSelected = 0;

public Adptr (ArrayList<Model> modelValues, Activity activity) {
    super();
    this.modelValues = modelValues;
    this.activity = activity;


}

@Override
public int getCount() {

    return modelValues.size();
}

@Override
public Object getItem(int position) {

    return modelValues.get(position);
}

@Override
public long getItemId(int position) {

    return position;
}


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

    layoutinflater = (LayoutInflater)  activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    ViewHolder holder = null;
    Model model = modelValues.get(position);

    if (convertView == null || !(convertView.getTag() instanceof ViewHolder)) {
        convertView = layoutinflater.inflate(R.layout.row_search, null);
        holder = new ViewHolder();
        holder.txtName = (TextView) convertView.findViewById(R.id.row_serch_txt_name);




        convertView.setTag(holder);
        convertView.setTag(R.id.row_serch_txt_name, holder.txtName);

    } else {
        holder = (ViewHolder) convertView.getTag();
    }

    holder.txtArtistName.setText("" + modelValue.get_NAME());




    return convertView;
}

class ViewHolder {
    TextView txtName;


}

@Override
public Filter getFilter() {

    Filter filter = new Filter() {

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

            modelValues = (ArrayList<ModelValueArtist>) results.values; // has

            notifyDataSetChanged();
        }

        @Override
        protected FilterResults performFiltering(CharSequence constraint) {
            FilterResults results = new FilterResults(); // Holds the
                                                            // results of a
                                                            // filtering
                                                            // operation in
                                                            // values
            // List<String> FilteredArrList = new ArrayList<String>();
            List<Model> FilteredArrList = new ArrayList<Model>();

            if (mOriginalValues == null) {
                mOriginalValues = new ArrayList<Model>(modelValues); // saves

            }

            /********
             * 
             * If constraint(CharSequence that is received) is null returns
             * the mOriginalValues(Original) values else does the Filtering
             * and returns FilteredArrList(Filtered)
             * 
             ********/
            if (constraint == null || constraint.length() == 0) {

                // set the Original result to return
                results.count = mOriginalValues.size();
                results.values = mOriginalValues;
            } else {
                Locale locale = Locale.getDefault();
                constraint = constraint.toString().toLowerCase(locale);
                for (int i = 0; i < mOriginalValues.size(); i++) {
                    Model model = mOriginalValues.get(i);

                    String data = model.get_NAME();
                    if (data.toLowerCase(locale).contains(constraint.toString())) {

                        FilteredArrList.add(modelMyMall);
                    }
                }
                // set the Filtered result to return
                results.count = FilteredArrList.size();
                results.values = FilteredArrList;

            }
            return results;
        }
    };
    return filter;
  }

     }
Sharpedged answered 10/10, 2013 at 4:34 Comment(1)
I used this word for word and it didn't work. It was suggested to override getCount() of the ArrayAdapter which just filtered the number of results not the results themselves. I ended up mThis.clear();mThis.addAll(customList); in publishResults()Askins
R
4

You can override toString() in you custom object class and return String object that you want to get filtered out.

@Override
public void toString(){
      return this.toBeFilteredString;
}

And then simply use default built-in filter of arrayAdapter and call it using

adapter.getFilter().filter(Charsequence c);
Recurvate answered 23/5, 2015 at 19:37 Comment(2)
Just what I was looking for! :)Walleyed
Same there! After writing the accepted solution, I looked for something simpler like this... thanks!Shiv
D
4

I searched quite a few answers on SO to get all the snippets needed. So here's a Kotlin version of a custom Filter for my class Registration. This one actually works and all the information you need is in this code.

class RegistrationArrayAdapter(context: Context, private val layoutResId: Int, registrations: List<Registration>) :
    ArrayAdapter<Registration>(context, layoutResId, registrations) {

    private val filter = RegistrationFilter(registrations)

    override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
        val registration = getItem(position)
        val view = convertView ?: LayoutInflater.from(context).inflate(layoutResId, null)
        (view as TextView).text = registration?.mainGuest?.name ?: ""
        return view
    }

    override fun getFilter() = filter

    inner class RegistrationFilter(private val originalList: List<Registration>) : Filter() {

        private val sourceObjects: ArrayList<Registration> = ArrayList()

        init {
            synchronized (this) {
                sourceObjects.addAll(originalList)
            }
        }

        override fun performFiltering(constraint: CharSequence?): FilterResults {
            if (constraint == null) return FilterResults()

            val result = FilterResults()

            if (constraint.isNotEmpty()) {
                val filteredList = ArrayList<Registration>()

                sourceObjects.filterTo(filteredList) { isWithinConstraint(it, constraint) }

                result.count = filteredList.size
                result.values = filteredList

            } else {
                synchronized(this) {
                    result.values = sourceObjects
                    result.count = sourceObjects.size
                }

            }
            return result
        }

        override fun publishResults(constraint: CharSequence?, results: FilterResults) {
            if (results.values == null) return

            @Suppress("UNCHECKED_CAST")
            val filtered = results.values as ArrayList<Registration>

            if (results.count > 0) {
                clear()
                addAll(filtered)
                notifyDataSetChanged()
            } else {
                notifyDataSetInvalidated()
            }

        }


        override fun convertResultToString(resultValue: Any?): CharSequence {
            return (resultValue as Registration).mainGuest.name
        }

        private fun isWithinConstraint(registration: Registration, constraint: CharSequence): Boolean {
            return registration.mainGuest.name.toLowerCase().contains(constraint, true)
        }

    }
}
Domiciliate answered 20/7, 2019 at 10:28 Comment(1)
You are the only life saver!! no other solution work as yours.Microcircuit
H
0

As you said "I want to implement auto suggest like feature used in google; But here from the list view. – Redone"

There is another way to do this by using AutoCompleteTextView

For More details go through AutoCompleteTextView

OutPut will look like this

If enter charachter 'a' it will show the list of words start with 'a'

enter image description here

main.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/listitem_layout"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:background="#e2e2e2"
    android:orientation="vertical" >

    <AutoCompleteTextView
            android:id="@+id/autocomplete"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:layout_margin="10dp"
            android:background="@drawable/text_area"
            android:inputType="text|textNoSuggestions|textMultiLine"
            android:paddingLeft="10dp"
            android:popupBackground="#EFEEEC"
            android:textColor="#333333"
            android:textColorHint="#9c9c9c"
            android:textSize="18sp"
            android:completionThreshold="1" />

</LinearLayout>

auto_textview.xml

<TextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:padding="12dp"
    android:textColor="#333333"
    android:layout_margin="15dp"
    android:textSize="16sp" >
</TextView>

MainActivity.java

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        AutoCompleteTextView seachView = (AutoCompleteTextView) findViewById(R.id.autocomplete);
        seachView.setAdapter(new ArrayAdapter<String>(this,
                R.layout.auto_textview, getHeading()));
    }

    /**
     * @return
     */
    public List<String> getHeading() {
        List<String> list = new ArrayList<String>();
        list.add("Android");
        list.add("Arnold");
        list.add("Blackberry");
        list.add("Blackpearl");
        list.add("Country");
        list.add("Canada");
        list.add("City");
        list.add("Street Address");
        list.add("Objective C");
        return list;
    }

}

Hope this will help you.

Hobble answered 10/10, 2013 at 19:11 Comment(1)
Can you help with my question here: #20524917Burnaby

© 2022 - 2024 — McMap. All rights reserved.