1

In a simple Android app that used to use an ArrayList and an ArrayAdapter to fill an AutoCompleteTextView, I have now replaced the ArrayAdapter with a custom adapter. The reason is that I need to strip whitespace and some other characters from the entered text before searching the list (i.e., entering "O.M." should be normalised to "OM" and then the suggestions beginning with "OM" should be displayed.

I tried to do this in the following commit on GitHub: https://github.com/Natureshadow/MirWTFApp/commit/260a4deaee449cb63d3af3c446c94466b90f736c

The custom Adapter has a getFilter method that should return a custom Filter doing the normalisation, but getFilter() is never called.

What can I do to find out why it is not called, and how can I make the app use the custom Filter?

StackOverflow has some questions that describe a similar issue, but all these questions include changing the handling of the ArrayList data within the custom adapter, which I did not do.

Dominik George
  • 517
  • 2
  • 13
  • how to make your life harder and implement `Filter` stuff at all? where do data for your `AutoCompleteTextView` come from? what is your data model? – pskink Nov 16 '15 at 13:32
  • @tinysunlight wellm there is a link to GitHub… – Dominik George Nov 16 '15 at 22:37
  • @pskink because the data set contains normalised strings, so the input needs to be normalised as well before searching. – Dominik George Nov 16 '15 at 22:38
  • see [this](http://stackoverflow.com/a/19860624/2252830), pay special attention on `FilterQueryProvider`, small and easy, no need for custom adapter, custom filter and stuff like that – pskink Nov 16 '15 at 22:49

2 Answers2

1

The solution ended up being a custom ArrayAdapter subclass which prepends a filter before the ArrayFilter even sees it.

The change to the main application is minimal, it just needs to instantiate the newly-christened WtfArrayAdapter and pass its constructor an extra argument: an object reference to the object containing the normalisation method (in the current äpp’s design, that’s the MainActivity class, a future refactor will do better):

-        ArrayAdapter acronymKeys = new ArrayAdapter(this,
-          android.R.layout.simple_list_item_1, sorted);
+        ArrayAdapter acronymKeys = new WtfArrayAdapter(this,
+          android.R.layout.simple_list_item_1, sorted, this);

The custom WtfArrayAdapter must be able to call methods from ArrayFilter (which is ArrayAdapter’s internal private class), so it needs to belong to package android.widget;. I’ll spare you the full imports, comments, etc. and will reproduce the important parts below:

public class WtfArrayAdapter<T> extends ArrayAdapter<T> {

New members for: - the pre-filter normalisation method’s object - our internal private class’ object - the parent’s internal private class’ object (ArrayAdapter.mFilter is private, unfortunately)

    private MainActivity sParent;
    private WtfArrayFilter sFilter;
    private Filter pFilter;

The extended constructor (we need to implement only the one we’re actually calling): - call the inherited constructor - store away the parent’s mFilter value - instantiate our own WtfArrayFilter - store away the pre-filter normalisation method’s object, as WtfArrayFilter will need it later

    public WtfArrayAdapter(Context context, @LayoutRes int resource,
      @NonNull T[] objects, MainActivity parent) {
        super(context, resource, 0, Arrays.asList(objects));
        sParent = parent;
        sFilter = new WtfArrayFilter();
        pFilter = super.getFilter();
    }

Override ArrayAdapter’s getFilter() method to always return our new WtfArrayFilter:

    public Filter getFilter() {
        return sFilter;
    }

Implementation of WtfArrayFilter as Filter subclass just like ArrayFilter does, and forwarding all boring (i.e. unchanged) calls to the inherited ArrayFilter:

    private class WtfArrayFilter extends Filter {
        protected void publishResults(CharSequence constraint,
          FilterResults results) {
            pFilter.publishResults(constraint, results);
        }

We only need to change one method to do our own filtering before calling the inherited ArrayFilter’s filtering method:

        protected FilterResults performFiltering(CharSequence prefix) {
            return pFilter.performFiltering(prefix == null ? null :
              sParent.normaliseAcronym(prefix.toString()));
        }
    }
}

Improvements welcome, I’m still a Java™ beginner.

mirabilos
  • 4,473
  • 2
  • 42
  • 65
0
    ArrayList<YourModel> listFromActivity= null;
    ArrayList<YourModel> mainList;

    public MyConstructor(Context context, ArrayList<YourModel> listFromActivity) {
        this.context = context;
        this.listFromActivity= listFromActivity;
        mainList= new ArrayList<>();
        mainList.addAll(listFromActivity);

    }    

public void filter(String charText) {
            charText = charText.toLowerCase(Locale.getDefault());
            mainList.clear();
            if (charText.length() == 0) {
                mainList.addAll(arraylist);
            } else {
                for (YourObject wp : arraylist) {
                    if (wp.getName().toLowerCase(Locale.getDefault()).contains(charText) ||
                            wp.getKey().contains(charText)) {
                        mainList.add(wp);
                    }
                }
            }
            notifyDataSetChanged();
        }

and in activity or fragment when you use this adapter

edittext.addTextChangedListener(new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence s, int start, int count, int after) {

            }

            @Override
            public void onTextChanged(CharSequence s, int start, int before, int count) {

            }

            @Override
            public void afterTextChanged(Editable s) {
                String text = mSearch.getText().toString().toLowerCase(Locale.getDefault());
                adaper.filter(text);
            }
        });
Kastriot Dreshaj
  • 1,101
  • 6
  • 16
  • Where does the mainList variable come from? – Dominik George Nov 16 '15 at 22:37
  • from constructor you pass your list of items on your constructor from activity/fragment – Kastriot Dreshaj Nov 17 '15 at 07:49
  • I still don't get it. The class does not have a member named mainList. If this is where I passed all the elements available, and I clear it, then how do I restore it later? I somehow do not get any of the context here… Then, what will notifyDataSetChanged() do? How does it find the mainList (again, I think mainList does not exist in ArrayAdapter…) – Dominik George Dec 09 '15 at 22:04
  • i added constructor now you can see where mainList came see my edited answer – Kastriot Dreshaj Dec 10 '15 at 08:27
  • Yeah, but how does notifyDataSetChanged know I put something in some class member called mainList? – Dominik George Dec 10 '15 at 15:43