How to modify android SearchableSpinner to search for multiple words in any order


I am making an android app with Java that uses the SearchableSpinner from com.github.miteshpithadiya:SearchableSpinner:master.

I would like to modify it so it also can search for multiple words in the same string, in any order. For example, if I searched for the words "red grapefruit" in a list of foods, the Spinner would be reduced to these options:

  • "Grapefruit, pink or red, all areas, raw"
  • "Grapefruit, pink or red, california or arizona, raw"
  • "Grapefruit, pink or red, florida, raw"

Here is the xml:

<com.toptoche.searchablespinnerlibrary.SearchableSpinner android:id="@+id/spinner_view" android:layout_width="match_parent" android:layout_height="50dp" app:layout_constraintTop_toBottomOf="@+id/screenText" app:layout_constraintBottom_toTopOf="@+id/button" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" />

And in MainActivity, I added a custom ArrayAdapter to the SearchableSpinner (I put "getApplicationContext()" instead of "this" because it's in another block):

SearchableSpinner searchableSpinner = findViewById(R.id.spinner_view); String[] dropDownArray = foodsMap.values().toArray(new String[0]); ArrayAdapter<String> adapter = new ArrayAdapter<>(getApplicationContext(), android.R.layout.simple_spinner_item, dropDownArray) { @Override public Filter getFilter() { return new android.widget.Filter() { @Override protected FilterResults performFiltering(CharSequence constraint) { FilterResults results = new FilterResults(); List<String> suggestions = new ArrayList<>(); if (constraint == null || constraint.length() == 0) { suggestions.addAll(items); } else { String[] queryWords = constraint.toString().toLowerCase().split(" "); for (String item : items) { String lowerItem = item.toLowerCase(); boolean allWordsMatch = true; for (String word : queryWords) { if (!lowerItem.contains(word)) { allWordsMatch = false; break; } } if (allWordsMatch) { suggestions.add(item); } } } results.values = suggestions; results.count = suggestions.size(); return results; } @Override protected void publishResults(CharSequence constraint, FilterResults results) { clear(); if (results.count > 0) { addAll((List<String>) results.values); notifyDataSetChanged(); } } }; } }; adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); searchableSpinner.setAdapter(adapter);

But it does not work. It completely empties when I enter "red" followed by a space.

Is there something else I need to override or add?

1
Jan 29 at 6:46 AM
User AvatarIan McInnes
#java#android#searchable-dropdown

Accepted Answer

Thw way you are doing it is almost right, there is 1 or 2 issues only due to which it is not working properly.

  1. Here when the user types a trailing space like example ("red ") then split(" ") can create empty tokens meaning so your all words must match loop rejects everything. You can fix it by trim() and also splitting on whitespace "\\s+" also & ignore empty words also.

  2. Do not filter from the adapter’s current items which you are keep clearing or adding.. Try to always filter from a separate original list that should never change.

example how to do this will match multiple words in any order & will not empty out just because the user typed a space, try if this workss

SearchableSpinner searchableSpinner = findViewById(R.id.spinner_view);

final List<String> allItems = new ArrayList<>(foodsMap.values()); // master list (never mutate)

ArrayAdapter<String> adapter = new ArrayAdapter<>(
        this,
        android.R.layout.simple_spinner_item,
        new ArrayList<>(allItems)
) {
    @Override
    public Filter getFilter() {
        return new Filter() {
            @Override
            protected FilterResults performFiltering(CharSequence constraint) {
                FilterResults results = new FilterResults();
                List<String> out = new ArrayList<>();

                String q = constraint == null ? "" : constraint.toString()
                        .toLowerCase(java.util.Locale.ROOT)
                        .trim();

                if (q.isEmpty()) {
                    out.addAll(allItems);
                } else {
                    String[] words = q.split("\\s+"); // handles trailing/multiple spaces
                    for (String item : allItems) {
                        String s = item.toLowerCase(java.util.Locale.ROOT);
                        boolean ok = true;
                        for (String w : words) {
                            if (w.isEmpty()) continue;
                            if (!s.contains(w)) { ok = false; break; }
                        }
                        if (ok) out.add(item);
                    }
                }

                results.values = out;
                results.count = out.size();
                return results;
            }

            @Override
            protected void publishResults(CharSequence constraint, FilterResults results) {
                clear();
                //noinspection unchecked
                addAll((List<String>) results.values);
                notifyDataSetChanged();
            }
        };
    }
};

adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
searchableSpinner.setAdapter(adapter);
User AvatarAbdul_Basit
Jan 29 at 7:53 AM
0