Question

I have 3 AutoCompleteTextViews, and I would like to register 2 String[] adapters on them. Currently, I'm doing this:

atw_from.setAdapter(new ArrayAdapter(ctx, android.r.layout.simple_dropdown_item_1line, stages_adapter));

Let's say my user wants to type "Középmező", he starts to type "Közé" and he will be offered to choose Középmező, until this, it is pretty simple. But what if the user is too lazy to type accents (and a lot of them are lazy), thus he will type Kozepmezo only, then he won't get any offer, since there is no Kozepmezo in my String[]. The thing I want is, if he types in "Koze", he should be offered Középmező, so even if he doesn't uses accents, he will be always offered the actual word with the accents.

Currently, I have a pretty silly solution, I have a String[] with double the size of the original [], the first half contains the words with accents, the second contains the deaccented versions. So now, if he types Közé, he will be offered Középmező, and if he types Koze, he will be offered Kozepmezo. It works because the server can process both versions, but it just looks silly, and I want to solve it.

From what I understand, I should make a full custom adapter. Is that the best approach, or is there any solution included in the SDK? If I should make the custom adapter, could anyone point me in the right direction, on how to do that? :)

EDIT: added my own answer, should work for everyone, cheers for the other answer, which directed me to the good direction!

Was it helpful?

Solution

Okey, after a lot of pain in the arse dealing with this, here's the thing I did at the end. This is NOT a good practise at all, and I may do it wrong, but at least it is working perfectly now.

Simply ctrl+c, ctrl+v the source code of BaseAdapter, and ctrl+c, ctrl+v the source code of ArrayAdapter. Now look at the private inner class, ArrayFilter, especially the performFiltering method. Modify(not override!) it, as much as you want, in my case, I added a lot of .replace("x","y") thingies, for the deaccenting part.

Whatever else I tried, either produced unpredictable force closes(a lot, and completely random ones), or I just couldnt do it, since too much methods/variables are private, instead of protected. I must say, Google should revisit theese codes...

Note: you don't really need to ctrl+c ctrl+v the BaseAdapter codes, since it is a public abstract class, but hey, it is not that much of a code, and this way everything is there, clearly visible for you.

cheers

OTHER TIPS

I would indeed go for a custom adapter, in which you provide your own filter function to match on both the accented and deaccented notations.

An example implementation doing just that, can be found here. Basically you will need to implement the actual filtering in performFiltering - I'm assuming you already have a way to deaccent queries, since you're currently populating your String[] with deaccented versions. You will want to compare the query with and without accents against the entries in your array (which you will want to use with and without accents). In the end you should have at least the following four tests:

accented(query) -> accented(entry)
accented(query) -> deaccented(entry)
deaccented(query) -> accented(entry)
deaccented(query) -> deaccented(entry)

By deaccenting words on-the-fly, you will only have to provide the String[] with accented words, while the filtering logic (in your adapter) will take care of matching against (de)accented words.

Edit: as discussed, below a sample implementation in one of my ongoing projects.

Some pointers:

  1. CustomArrayAdapter is mostly a wrapper class that simplifies common tasks; e.g. interaction with a row wrapper/view holder. Basically all it needs is a constructor and implementation of updateRow (which obviously will get called from the super class' getView method).
  2. CustomRowWrapper should be pretty straightforward.
  3. ArrayUtil and ArrayUtil.FilterFuction take care of the actual filtering. Simply said these act as replacement for a for loop that builds a new list of all items matching some criteria.

public class CARMedicationSuggestionAdapter extends CustomArrayAdapter<CARMedicationInfo, RowWrapper> {

    private List<CARMedicationInfo> mMedications;
    private Filter mFilter;

    /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
     * constructor
     * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

    public CARMedicationSuggestionAdapter(Context context, List<CARMedicationInfo> objects) {
        super(RowWrapper.class, context, R.layout.medication_suggestion_item_layout, objects);
        // keep copy of all items for lookups 
        mMedications = new ArrayList<CARMedicationInfo>(objects);
    }

    /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
     * update row
     * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

    @Override protected void updateRow(RowWrapper wrapper, CARMedicationInfo item) {
        wrapper.getNameTextView().setText(item.toString());
    }

    /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
     * get filter
     * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

    @Override public Filter getFilter() {
        // return if already created
        if (mFilter != null) return mFilter;
        mFilter = new Filter() {
            @Override protected void publishResults(CharSequence constraint, FilterResults results) {
                @SuppressWarnings("unchecked") List<CARMedicationInfo> filtered = (List<CARMedicationInfo>) results.values;
                if (results == null || results.count == 0) return;
                // clear out current suggestions and add all new ones
                clear(); 
                addAll(filtered);
            }

            @Override protected FilterResults performFiltering(final CharSequence constraint) {
                // return empty results for 'null' constraint
                if (constraint == null) return new FilterResults();
                // get all medications that contain the constraint in drug name, trade name or whose string representation start with the constraint
                List<CARMedicationInfo> suggestions = ArrayUtil.filter(mMedications, new ArrayUtil.FilterFunction<CARMedicationInfo>() {
                    @Override public boolean filter(CARMedicationInfo item) {
                        String query = constraint.toString().toLowerCase().trim();
                        return  item.mMedicationDrugName.toLowerCase().contains(query) || 
                                item.mMedicationTradeName.toLowerCase().contains(query) ||
                                item.toString().toLowerCase().startsWith(query); 
                    }
                });
                // set results and size
                FilterResults filterResults = new FilterResults();
                filterResults.values = suggestions;
                filterResults.count = suggestions.size();
                return filterResults;
            }
        };
        return mFilter;
    }

    /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
     * row wrapper
     * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

    static class RowWrapper extends CustomRowWrapper {

        private ImageView mIconImageView;
        private TextView mNameTextView;

        public RowWrapper(View row) {
            super(row);
        }

        public ImageView getIconImageView() {
            if (mIconImageView == null) mIconImageView = (ImageView) mRow.findViewById(R.id.icon_imageview);
            return mIconImageView;
        }

        public TextView getNameTextView() {
            if (mNameTextView == null) mNameTextView = (TextView) mRow.findViewById(R.id.name_textview);
            return mNameTextView;
        }

    }

}

take a look at this thread remove-accents-from-string and android Normalizer class

[EDIT] or you could try merge-adapter with both arrays

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top