AutoCompleteTextView not completing words inside parentheses

An answer, provided by @BNK, is correct. However, I would like to give a similar solution, which doesn't require the whole ArrayAdapter class file. Instead, we will just extend that class and override its only 2 methods: getView() and getFilter(). So, define your AutoSuggestAdapter class:

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.Filter;
import android.widget.TextView;

import java.util.ArrayList;
import java.util.List;

public class AutoSuggestAdapter extends ArrayAdapter
{
    private Context      context;
    private int          resource;
    private List<String> items;
    private List<String> tempItems;
    private List<String> suggestions;

    public AutoSuggestAdapter(Context context, int resource, List<String> items)
    {
        super(context, resource, 0, items);

        this.context = context;
        this.resource = resource;
        this.items = items;
        tempItems = new ArrayList<String>(items);
        suggestions = new ArrayList<String>();
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent)
    {
        View view = convertView;
        if (convertView == null)
        {
            LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            view = inflater.inflate(resource, parent, false);
        }

        String item = items.get(position);

        if (item != null && view instanceof TextView)
        {
            ((TextView) view).setText(item);
        }

        return view;
    }

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

    Filter nameFilter = new Filter()
    {
        @Override
        public CharSequence convertResultToString(Object resultValue)
        {
            String str = (String) resultValue;
            return str;
        }

        @Override
        protected FilterResults performFiltering(CharSequence constraint)
        {
            if (constraint != null)
            {
                suggestions.clear();
                for (String names : tempItems)
                {
                    if (names.toLowerCase().contains(constraint.toString().toLowerCase()))
                    {
                        suggestions.add(names);
                    }
                }
                FilterResults filterResults = new FilterResults();
                filterResults.values = suggestions;
                filterResults.count = suggestions.size();
                return filterResults;
            }
            else
            {
                return new FilterResults();
            }
        }

        @Override
        protected void publishResults(CharSequence constraint, FilterResults results)
        {
            List<String> filterList = (ArrayList<String>) results.values;
            if (results != null && results.count > 0)
            {
                clear();
                for (String item : filterList)
                {
                    add(item);
                    notifyDataSetChanged();
                }
            }
        }
    };
}

Define auto complete view in XML for example:

       <AutoCompleteTextView
        android:id="@+id/autoComplete"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:hint="Enter some text ..."/>

And use it:

    AutoCompleteTextView autoComplete = (AutoCompleteTextView) findViewById(R.id.autoComplete);

    List <String> stringList = new ArrayList<String>();
    stringList.add("Black");
    stringList.add("White");
    stringList.add("Yellow");
    stringList.add("Green");
    stringList.add("Blue");
    stringList.add("Brown");
    stringList.add("Orange");
    stringList.add("Pink");
    stringList.add("Violet");
    stringList.add("Cyan");
    stringList.add("LightBlue");

    AutoSuggestAdapter adapter = new AutoSuggestAdapter(this, android.R.layout.simple_list_item_1, stringList);

    autoComplete.setAdapter(adapter);

    // specify the minimum type of characters before drop-down list is shown
    autoComplete.setThreshold(1);

enter image description here


The default implementation of the filter for ArrayAdapter is searching the beginning of words (separated by space), I mean it uses startsWith. You will need to implement an ArrayFilter which uses contains together with startsWith.

Your issue will be solved by the following steps:

  • Download the ArrayAdapter.java file from here
  • Add that file into the project (you can refactor by renaming file to CustomArrayAdapter.java, for example).
  • In the file, you will find a private class ArrayFilter. Then, add valueText.contains(prefixString) and words[k].contains(prefixString) as the following:

                if (valueText.startsWith(prefixString) || valueText.contains(prefixString)) {
                    newValues.add(value);
                } else {
                    final String[] words = valueText.split(" ");
                    final int wordCount = words.length;
                    // Start at index 0, in case valueText starts with space(s)
                    for (int k = 0; k < wordCount; k++) {
                        if (words[k].startsWith(prefixString) || words[k].contains(prefixString)) {
                            newValues.add(value);
                            break;
                        }
                    }
                }
    
  • Use that customized ArrayAdapter for your AutoCompleteTextView

And here is the result screenshot:

AutoCompleteTextView

Hope this helps!


To add to the answer by @Ayaz Alifov and remediate the concerns offered by @Aballano, here is a more complete AutoSuggestAdapter with kotlin code:


import android.content.Context
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ArrayAdapter
import android.widget.Filter
import android.widget.TextView

class AutoSuggestAdapter<T : Any>(context: Context, private val resource: Int, items : List<T> = ArrayList()) :
    ArrayAdapter<T>(context, resource, items) {

    private val items : ArrayList<T> = ArrayList()
    private val suggestions : ArrayList<T> = ArrayList()
    var customLayoutHandler : ((View, T) -> Unit)? = null

    companion object {
        private const val TAG = "AutoSuggestAdapter"
    }

    override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
        lateinit var v : View
        v = if(convertView == null){
            val inflater : LayoutInflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
            inflater.inflate(resource, parent, false)
        } else convertView

        val item : T = suggestions[position]
        if(v is TextView) v.text = item.toString()
        else customLayoutHandler?.invoke(v,item)
        return v
    }

    override fun getFilter() : Filter = itemFilter

    override fun add(item: T?) {
        if(item != null) super.add(item)
        if(item != null && !items.contains(item)) items.add(item)
    }

    override fun addAll(vararg items: T) {
        super.addAll(*items)
        this.items.addAll(items)
    }

    override fun addAll(collection: MutableCollection<out T>) {
        super.addAll(collection)
        this.items.addAll(collection)
    }

    fun clearItems(){
        items.clear()
    }

    inner class ItemFilter() : Filter() {
        override fun performFiltering(constraint: CharSequence?): FilterResults {
            if(constraint != null){
                suggestions.clear()
                items.forEach { item ->
                    if(item.toString().toLowerCase().contains(constraint.toString().toLowerCase())) suggestions.add(item)
                }

                val fResults : FilterResults = FilterResults()
                fResults.values = suggestions
                fResults.count = suggestions.size
                return fResults
            }
            return FilterResults()
        }

        override fun publishResults(constraint: CharSequence?, results: FilterResults?) {
            if(results != null && results.count > 0){
                var filterList : ArrayList<T> = results?.values as ArrayList<T>
                clear()
                filterList.forEach { item -> add(item) }
                notifyDataSetChanged()
            }
        }

        override fun convertResultToString(resultValue: Any?): CharSequence {
            return resultValue?.toString() ?: ""
        }
    }
    private val itemFilter : ItemFilter = ItemFilter()
}

It should work out of the box for Strings or any other primitive type (or objects/classes where you have a valid toString representation). It also allows for you to specify a custom function to handle custom objects where you want to use a non-standard layout (other than just a TextView). It passes the top level view container and the item you want to represent in the dropdown.