I have an AutoCompleteTextView
in my layout which will get data from an API, the data returns two params, description and code, the AutoCompleteText
has to be filterable for both code and description.
To view both code and description i've made a custom ArrayAdapter
and i was trying to implement the Filterable
method to set the custom filter which will check for both code and description properties.
The issue is that once i try to write in AutoCompleteTextView
my app crash by casting an exception at the start of the publishResults
: java.lang.UnsupportedOperationException
So how could i implement a custom filterable to my ArrayAdapter?
Here is my Adapter code:
class FornitoriAdapter(context: Context, resource: Int, private val fornitori: List<Fornitori>) :
ArrayAdapter<Fornitori>(context, resource, fornitori), Filterable {
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
val row: View
val holder: ViewHolder
if (convertView == null) {
val inflater = LayoutInflater.from(context)
row = inflater.inflate(R.layout.list_item, parent, false)
holder = ViewHolder(row)
row.tag = holder
} else {
row = convertView
holder = row.tag as ViewHolder
}
val fornitore: Fornitori = fornitori[position]
holder.descrText.text = fornitore.desc
holder.codeText.text = fornitore.cod
return row
}
override fun getItem(position: Int): Fornitori {
return fornitori[position]
}
override fun getCount(): Int {
return fornitori.size
}
private class ViewHolder constructor(row: View) {
val descrText: TextView = row.findViewById(R.id.description)
val codeText: TextView = row.findViewById(R.id.code)
}
override fun getFilter(): Filter {
return customFilter
}
private val customFilter = object : Filter() {
override fun convertResultToString(resultValue: Any?): CharSequence {
return (resultValue as Fornitori).desc
}
override fun performFiltering(constraint: CharSequence?): FilterResults {
val filteredList = mutableListOf<Fornitori>()
if (constraint == null || constraint.isEmpty()) {
filteredList.addAll(fornitori)
} else {
for (item in fornitori) {
if (item.cod.equals(constraint.toString(), ignoreCase = true) || item.desc.toLowerCase(
Locale.ROOT
).startsWith(
constraint.toString().toLowerCase(
Locale.ROOT
)
)
) {
filteredList.add(item)
}
}
}
val results = FilterResults()
results.values = filteredList
return results
}
override fun publishResults(constraint: CharSequence?, filterResults: FilterResults?) {
clear()
val newValues: List<Fornitori> = filterResults?.values as List<Fornitori>
for (element in newValues) {
add(element)
}
if (filterResults.count > 0) {
notifyDataSetChanged()
} else {
notifyDataSetInvalidated()
}
}
}
}
And here how i manage it in my Fragment:
autocompleteFornitore = view.findViewById(R.id.autocompleteFornitore)
val fornitori: List<Fornitori> = listOf(Fornitori("001", "GAB"), Fornitori("002", "TEST"))
val adapter = FornitoriAdapter(requireContext(), R.layout.list_item, fornitori)
autocompleteFornitore.setAdapter(adapter)
autocompleteFornitore.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(
parent: AdapterView<*>?,
view: View?,
position: Int,
id: Long
) {
}
override fun onNothingSelected(parent: AdapterView<*>?) {
}
}
As required full stack trace:
2021-03-09 16:05:26.915 31076-31076/it.xxx.xxx E/AndroidRuntime: FATAL EXCEPTION: main
Process: it.xxx.xxx, PID: 31076
java.lang.UnsupportedOperationException
at java.util.AbstractList.remove(AbstractList.java:161)
at java.util.AbstractList$Itr.remove(AbstractList.java:374)
at java.util.AbstractList.removeRange(AbstractList.java:571)
at java.util.AbstractList.clear(AbstractList.java:234)
at android.widget.ArrayAdapter.clear(ArrayAdapter.java:320)
at it.gabtamagnini.visualstock.adapters.FornitoriAdapter$customFilter$1.publishResults(FornitoriAdapter.kt:88)
at android.widget.Filter$ResultsHandler.handleMessage(Filter.java:282)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6518)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807)
It says in the documentation of ArrayAdapter
that:
@throws UnsupportedOperationException if the underlying data collection is immutable
Therefore you should be passing in a MutableList
into the ArrayAdapter
if you want to use clear()
, either by constructing FornitoriAdapter
with one, or converting it with Collection<T>.toMutableList()
.