如何使用自定义适配器为 AutoCompleteTextView 制作自定义过滤器?
How to make custom Filter for AutoCompleteTextView using custom Adapter?
我的布局中有一个 AutoCompleteTextView
,它将从 API 获取数据,数据 returns 两个参数,描述和代码,AutoCompleteText
必须可针对代码和描述进行过滤。
为了查看代码和描述,我创建了自定义 ArrayAdapter
并且我正在尝试实施 Filterable
方法来设置自定义过滤器,该过滤器将检查代码和描述属性。
问题是,一旦我尝试在 AutoCompleteTextView
中写入,我的应用程序因在 publishResults
开始时抛出异常而崩溃:java.lang.UnsupportedOperationException
那么我怎样才能为我的 ArrayAdapter 实现自定义过滤器?
这是我的适配器代码:
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()
}
}
}
}
下面是我在 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<*>?) {
}
}
根据需要完整的堆栈跟踪:
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.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)
它在 ArrayAdapter
的文档中说:
@throws UnsupportedOperationException if the underlying data collection is immutable
因此,如果您想使用 clear()
,您应该将 MutableList
传递给 ArrayAdapter
,方法是用一个构造 FornitoriAdapter
,或者将其转换为Collection<T>.toMutableList()
.
我的布局中有一个 AutoCompleteTextView
,它将从 API 获取数据,数据 returns 两个参数,描述和代码,AutoCompleteText
必须可针对代码和描述进行过滤。
为了查看代码和描述,我创建了自定义 ArrayAdapter
并且我正在尝试实施 Filterable
方法来设置自定义过滤器,该过滤器将检查代码和描述属性。
问题是,一旦我尝试在 AutoCompleteTextView
中写入,我的应用程序因在 publishResults
开始时抛出异常而崩溃:java.lang.UnsupportedOperationException
那么我怎样才能为我的 ArrayAdapter 实现自定义过滤器?
这是我的适配器代码:
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()
}
}
}
}
下面是我在 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<*>?) {
}
}
根据需要完整的堆栈跟踪:
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.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)
它在 ArrayAdapter
的文档中说:
@throws UnsupportedOperationException if the underlying data collection is immutable
因此,如果您想使用 clear()
,您应该将 MutableList
传递给 ArrayAdapter
,方法是用一个构造 FornitoriAdapter
,或者将其转换为Collection<T>.toMutableList()
.