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.
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
according to Ayaz Alifov answer you cannot get correct filtered items' count in TextWatcher
, because filtering usually takes longer time than TextWatcher
event listeners.
but i have done a trick with a timerTask
. so the TextWatcher would execute after counting.
editText.addTextChangedListener(
new TextWatcher() {
@Override public void onTextChanged(CharSequence s, int start, int before, int count) { }
@Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { }
private Timer timer=new Timer();
private final long DELAY = 1000; // milliseconds
@Override
public void afterTextChanged(final Editable s) {
timer.cancel();
timer = new Timer();
timer.schedule(
new TimerTask() {
@Override
public void run() {
// adapter.getCount() will give you the correct item's counts
Log.d(TAG, "run: afterTextChanged " + adapter.getCount());
}
},
DELAY
);
}
}
);
Edited: 5/Sep/2019
you can also get items count with the help of setting a registerDataSetObserver
.
adapter.registerDataSetObserver(new DataSetObserver() {
@Override
public void onChanged() {
super.onChanged();
Log.d(TAG, "onChanged: " + adapter.getCount());
}
});
in this way the onChanged()
will call every time text change. But if the suggestion list becomes empty, it will not be called.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With