user3035850
user3035850

Reputation:

Custom ArrayAdapter getFilter() not being called

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.

Upvotes: 1

Views: 1372

Answers (2)

mirabilos
mirabilos

Reputation: 5317

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.

Upvotes: 0

Kastriot Dreshaj
Kastriot Dreshaj

Reputation: 1121

    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);
            }
        });

Upvotes: 0

Related Questions