Search code examples
androidarraylistfilteringandroid-adapterautocompletetextview

Get filtered array size in AutoCompleteTextview


I am working on the project in which user can search data. For that, I have implemented AutoCompleteTextView.

 autoComplete.setAdapter(new ArrayAdapter<String>(CheckRiskActivity.this,
                                        R.layout.auto_text_row, druglist));
                                autoComplete.setThreshold(1);
//druglist is my arraylist

Text change listener is as below:

autoComplete.addTextChangedListener(new TextWatcher() {

        @Override
        public void afterTextChanged(Editable s) {
            // here I want to get the size of filtered array list every time when the user adds any character.
        }

        @Override
        public void beforeTextChanged(CharSequence s, int start,
                                      int count, int after) {
        }

        @Override
        public void onTextChanged(CharSequence s, int start,
                                  int before, int count) {
        }
    });

Explanation: If my initial array size is 100 and if the user types 'a', then I want to get the size of filtered array.

Note: I have tried autoComplete.getAdapter().getCount(); but it gives the actual result after adding one more character.


Solution

  • You cannot get correct filtered items' count in TextWatcher, because filtering usually takes longer time than TextWatcher event listeners. Therefore you get incorrect autoComplete.getAdapter().getCount() in afterTextChanged(). I would recommend to use custom listener which will be called every time when filtered items are changed.

    I will provide 2 similar approaches: using separate classes and using only 1 class.

    APPROACH 1: Your adapter should look like:

    import android.content.Context;
    import android.widget.ArrayAdapter;
    import android.widget.Filter;
    import java.util.ArrayList;
    import java.util.List;
    
    public class AutoCompleteAdapter extends ArrayAdapter
    {
        private List<String>    tempItems;
        private List<String>    suggestions;
        private FilterListeners filterListeners;
    
        public AutoCompleteAdapter(Context context, int resource, List<String> items)
        {
            super(context, resource, 0, items);
    
            tempItems = new ArrayList<>(items);
            suggestions = new ArrayList<>();
        }
    
        public void setFilterListeners(FilterListeners filterFinishedListener)
        {
            filterListeners = filterFinishedListener;
        }
    
        @Override
        public Filter getFilter()
        {
            return nameFilter;
        }
    
        Filter nameFilter = new Filter()
        {
            @Override
            protected FilterResults performFiltering(CharSequence constraint)
            {
                if (constraint != null)
                {
                    suggestions.clear();
                    for (String names : tempItems)
                    {
                        if (names.toLowerCase().startsWith(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 (filterListeners != null && filterList!= null)
                    filterListeners.filteringFinished(filterList.size());
    
                if (results != null && results.count > 0)
                {
                    clear();
                    for (String item : filterList)
                    {
                        add(item);
                        notifyDataSetChanged();
                    }
                }
            }
        };
    }
    

    An interface which is used to inform you when filtering will be finished:

    public interface FilterListeners
    {
        void filteringFinished(int filteredItemsCount);
    }
    

    And you can use it:

    import android.app.Activity;
    import android.os.Bundle;
    import android.util.Log;
    import android.widget.AutoCompleteTextView;
    
    import java.util.ArrayList;
    import java.util.List;
    
    public class MainActivity extends Activity implements FilterListeners
    {
        @Override
        protected void onCreate(Bundle savedInstanceState)
        {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            AutoCompleteTextView autoComplete = (AutoCompleteTextView) findViewById(R.id.autoComplete);
            autoComplete.setThreshold(1);
    
            List<String> stringList = new ArrayList<>();
            stringList.add("Black");
            stringList.add("White");
            stringList.add("Yellow");
            stringList.add("Blue");
            stringList.add("Brown");
    
    
            final AutoCompleteAdapter adapter = new AutoCompleteAdapter(this, android.R.layout.simple_list_item_1, stringList);
            adapter.setFilterListeners(this);
            autoComplete.setAdapter(adapter);
        }
    
        @Override
        public void filteringFinished(int filteredItemsCount)
        {
            Log.i("LOG_TAG", " filteringFinished  count = " + filteredItemsCount);
        }
    }
    

    APPROACH 2:

        import android.app.Activity;
        import android.os.Bundle;
        import android.util.Log;
        import android.widget.ArrayAdapter;
        import android.widget.AutoCompleteTextView;
        import android.widget.Filter;
        import java.util.ArrayList;
        import java.util.List;
    
        public class MainActivity extends Activity
        {
            @Override
            protected void onCreate(Bundle savedInstanceState)
            {
                super.onCreate(savedInstanceState);
                setContentView(R.layout.activity_main);
    
                final AutoCompleteTextView autoComplete = (AutoCompleteTextView) findViewById(R.id.autoComplete);
                autoComplete.setThreshold(1);
    
                final List<String> stringList = new ArrayList<>();
                stringList.add("Black");
                stringList.add("White");
                stringList.add("Yellow");
                stringList.add("Blue");
                stringList.add("Brown");
    
                final ArrayAdapter arrayAdapter = new ArrayAdapter(this, android.R.layout.simple_list_item_1, stringList)
                {
                    private List<String> tempItems   = stringList;
                    private List<String> suggestions = new ArrayList<>();
    
                    @Override
                    public Filter getFilter()
                    {
                        return nameFilter;
                    }
    
                    Filter nameFilter = new Filter()
                    {
                        @Override
                        protected FilterResults performFiltering(CharSequence constraint)
                        {
                            if (constraint != null)
                            {
                                suggestions.clear();
                                for (String names : tempItems)
                                {
                                    if (names.toLowerCase().startsWith(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;
                        filteringFinished(filterList.size());
    
                        if (results != null && results.count > 0)
                        {
                            clear();
                            for (String item : filterList)
                            {
                                add(item);
                                notifyDataSetChanged();
                            }
                        }
                    }
                };
            };
    
            autoComplete.setAdapter(arrayAdapter);
        }
    
        private void filteringFinished(int filteredItemsCount)
        {
            Log.i("LOG_TAG", " filteringFinished  count = " + filteredItemsCount);
        }
    }
    

    filteringFinished() method will be called when you enter something to an autocomplete input field and it gets filtered.

    UPDATE (Trie Search):

    I have created a Github project with a simple example of using Trie search algorithm to increase autocomplete performance very much.

    https://github.com/saqada/android-AutoCompleteWithTrie