I would like to change the default filtering in AutoCompleteTextView
. The default filtering finds all strings that startsWith the given token. My project requires that the filtering should find all strings that contains the given token.
Is it possible?
I found a solution for that, thanks to Google and searching for two days. As @torque203 suggested, I've implemented my own custom Adapter. First define a new XML file to custom Item in the adapter:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceMedium"
android:text="Medium Text"
android:paddingLeft="8dp"
android:paddingRight="8dp"
android:paddingTop="16dp"
android:paddingBottom="16dp"
android:id="@+id/lbl_name" />
</RelativeLayout>
Create new class for your Names:
public class Names {
public String name;
}
public class NamesAdapter extends ArrayAdapter<Names> {
Context context;
int resource, textViewResourceId;
List<Names> items, tempItems, suggestions;
public NamesAdapter(Context context, int resource, int textViewResourceId, List<Names> items) {
super(context, resource, textViewResourceId, items);
this.context = context;
this.resource = resource;
this.textViewResourceId = textViewResourceId;
this.items = items;
tempItems = new ArrayList<Names>(items); // this makes the difference.
suggestions = new ArrayList<Names>();
}
@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(R.layout.autocomplete_item, parent, false);
}
Names names = items.get(position);
if (names != null) {
TextView lblName = (TextView) view.findViewById(R.id.lbl_name);
if (lblName != null)
lblName.setText(names.name);
}
return view;
}
@Override
public Filter getFilter() {
return nameFilter;
}
/**
* Custom Filter implementation for custom suggestions we provide.
*/
Filter nameFilter = new Filter() {
@Override
public CharSequence convertResultToString(Object resultValue) {
String str = ((Names) resultValue).name;
return str;
}
@Override
protected FilterResults performFiltering(CharSequence constraint) {
if (constraint != null) {
suggestions.clear();
for (Names names : tempItems) {
if (names.name.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<Names> filterList = (ArrayList<Names>) results.values;
if (results != null && results.count > 0) {
clear();
for (Names names : filterList) {
add(names);
notifyDataSetChanged();
}
}
}
};
}
....
List<Names> namesList = //your names list;
NamesAdapter namesAdapter = new NamesAdapter(
SearchActivity.this,
R.layout.activity_search,
R.id.lbl_name,
namesList
);
//set adapter into listStudent
autoCompleteTextView.setAdapter(namesAdapter);
autoCompleteTextView.showDropDown();
...
Here is a simpler, Kotlin version of what @Caffe Latte posted.
You don't need the custom layout file, just use the default android.R.layout.simple_list_item_1
.
Provide any class to this adapter, including plain ol' Strings. It will simply use toString()
to determine the display text.
import android.content.Context
import android.widget.ArrayAdapter
import android.widget.Filter
import androidx.annotation.IdRes
import androidx.annotation.LayoutRes
import java.util.*
class AutoCompleteAdapter(
context: Context,
@LayoutRes resource: Int,
@IdRes textViewResourceId: Int = 0,
internal var items: List<Any> = listOf()
)
: ArrayAdapter<Any>(context, resource, textViewResourceId, items) {
internal var tempItems: MutableList<Any> = mutableListOf()
internal var suggestions: MutableList<Any> = mutableListOf()
/**
* Custom Filter implementation for custom suggestions we provide.
*/
private var filter: Filter = object : Filter() {
override fun performFiltering(constraint: CharSequence?): FilterResults {
return if (constraint != null) {
suggestions.clear()
tempItems.forEach {
if (it.toString().toLowerCase(Locale.getDefault()).contains(constraint.toString().toLowerCase(Locale.getDefault()))) {
suggestions.add(it)
}
}
val filterResults = FilterResults()
filterResults.values = suggestions
filterResults.count = suggestions.size
filterResults
} else {
FilterResults()
}
}
override fun publishResults(constraint: CharSequence?, results: FilterResults) {
val filterList = results.values as? List<Any>
if (results.count > 0) {
clear()
filterList?.forEach {
add(it)
}.also {
notifyDataSetChanged()
}
}
}
}
init {
tempItems = items.toMutableList()
suggestions = ArrayList()
}
override fun getFilter(): Filter {
return filter
}
}
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