9

Out of the box, the AutoCompleteTextView widget does not seem to be able to match the input string in the middle of a list value - the matches are always made at the beginning; e.g., entering "ar" matches "argentina", but not "hungary".

How can I search for the text in the middle of the word ? Can anyone give me an idea ?

Thanks in advance !

fedorqui
  • 275,237
  • 103
  • 548
  • 598
Chrishan
  • 4,076
  • 7
  • 48
  • 67
  • I am doing something similar HERE!!! http://stackoverflow.com/questions/12854336/autocompletetextview-backed-by-cursorloader – Etienne Lawlor Oct 29 '12 at 20:10

4 Answers4

10

You would need to write a custom Filter class and implement the performFiltering method yourself. This method takes a CharSequence argument, which you can use to perform whatever String operations you need in order to generate a list of matches from your dataset (in your case, you could use String.contains instead of String.startsWith). The performFiltering function is not run on the UI thread.

You then return your list of matches as a FilterResults object, which contains an Object values (your list of matches, probably an ArrayList) and an int count which is the size of your list of matches.

Finally, implement the publishResults callback method, which returns once the worker thread has generated the list of matches, allowing you to call notifyDataSetChanged on your AutoCompleteTextView's adapter so that it can display the results.

Victor
  • 1,137
  • 1
  • 10
  • 15
  • can you please give me an example ? – Chrishan Jan 30 '12 at 04:22
  • I know it's been a lot of time since this answer, but it's one of the most accurate I found regarding to this subject (and one of the most difficult to find, btw). Thank you! – nKn Jan 10 '14 at 22:07
  • 2
    Victor is right, here is the easy way : simply copy-paste the whole content of android.widget.ArrayAdapter in a new class named CustomArrayAdapter and change the 2 "startsWith" occurrences located in performFiltering and publishResults by "contains". Simple. – Damien Praca Jan 06 '15 at 09:53
6

Old question, but still relevant. Following the guidance of a few other questions implemented a custom adapter using filterable. I made a simple generic adapter that searches with contains. Quick notes on it:

I'm using butterknife, but easy to do the viewHolder with findviewbyid.

The layout R.layout.list_item_simple is a simple layout with the textview R.id.text_view_simple.

The object needs a toString that will be compared.

public class SimpleContainsAutocompleteAdapter <T> extends ArrayAdapter<T> implements Filterable {
    private List <T> listObjects;
    List<T> suggestions = new ArrayList<>();
    private int resource;

    private Filter mFilter = new Filter(){
        @Override
        protected FilterResults performFiltering(CharSequence constraint) {
            FilterResults filterResults = new FilterResults();

            if(constraint != null) {
                suggestions.clear();
                for(T object : listObjects){
                    if(object.toString().toLowerCase().contains(constraint.toString().toLowerCase())){
                        suggestions.add(object);
                    }
                }

                filterResults.values = suggestions;
                filterResults.count = suggestions.size();
            }

            return filterResults;
        }

        @Override
        protected void publishResults(CharSequence contraint, FilterResults results) {
            if(results == null){
                return;
            }

            List<T> filteredList = (List<T>) results.values;
            if(results.count > 0) {
                clear();
                for (T filteredObject : filteredList) {
                    add(filteredObject);
                }
                notifyDataSetChanged();
            }
        }
    };

    public SimpleContainsAutocompleteAdapter(Context context, List<T> listObjects) {
        super(context, R.layout.list_item_simple, listObjects);
        this.listObjects = new ArrayList<>(listObjects);
        this.resource = R.layout.list_item_simple;
    }

    @Override
    public Filter getFilter() {
        return mFilter;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        Object listObject = getItem(position);
        viewHolder holder;
        if(convertView != null) {
            holder = (viewHolder) convertView.getTag();
        }else{
            convertView = LayoutInflater.from(getContext()).inflate(resource, parent, false);
            holder = new viewHolder(convertView);
            convertView.setTag(holder);
        }

        holder.name.setText(listObject.toString());

        return convertView;
    }


    static class viewHolder {
        @InjectView(R.id.text_view_simple) TextView name;

        public viewHolder(View view) {
            ButterKnife.inject(this, view);
        }
    }
}
Ryan C
  • 1,003
  • 1
  • 14
  • 26
3
public class AutoCompleteAdapter <T> extends ArrayAdapter<T> implements Filterable {
    private List <T> listObjects;
    List<T> suggestions = new ArrayList<>();
    private Context context;
    public AutoCompleteAdapter(Context context, List<T> listObjects) {
        super(context, R.layout.list_item_simple, listObjects);
        this.listObjects = new ArrayList<>(listObjects);
        this.context = context;
    }
    private Filter mFilter = new Filter(){
        @Override
        protected FilterResults performFiltering(CharSequence constraint) {
            FilterResults filterResults = new FilterResults();
            if(constraint != null) {
            suggestions.clear();
                for(T object : listObjects){  
                    if(object.toString().toLowerCase().contains(constraint.toString().toLowerCase())){                        suggestions.add(object);
                }
            }

            filterResults.values = suggestions;
            filterResults.count = suggestions.size();
        }
        return filterResults;
    }

    @Override
    protected void publishResults(CharSequence constraint, FilterResults results) {
        if(results == null){
            return;
        }
        List<T> filteredList = (List<T>) results.values;
        if(results.count > 0) {
            clear();
            for (T filteredObject : filteredList) {
                add(filteredObject);
            }
            notifyDataSetChanged();
        }
    }
};
@Override
public Filter getFilter() {
    return mFilter;
}
private static class ViewHolder {
    TextView title;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
    Object listObject = getItem(position);
    final ViewHolder viewHolder; // view lookup cache stored in tag
    if (convertView == null) {
        viewHolder = new ViewHolder();
        LayoutInflater inflater = LayoutInflater.from(getContext());
        convertView = inflater.inflate(R.layout.list_item_simple, parent, false);
        viewHolder.title = (TextView) convertView.findViewById(R.id.title);
        convertView.setTag(viewHolder);
    } else {
        viewHolder = (ViewHolder) convertView.getTag();
    }
    viewHolder.title.setText(listObject.toString());
    return convertView;
}

}

sdelvalle57
  • 877
  • 2
  • 10
  • 16
0

My advice would be to parse the string into a character array. Then iterate each character until the string is found.

For example lets say your search wanted to return all words with "ate" in them and the word list was...

state trait berate late

Your algorithm should go something like this

Take the string and parse it into a character array Loop through the array and find the first "correct character" (in our example its 'a') Once that character is found check the next character, keep checking each character for a match until the value being searched for is complete. If the character is not a match exit the array iteration and go to the next word.