如何使用 Kotlin 在 ListAdapter 中使用 Filterable?
Posted
技术标签:
【中文标题】如何使用 Kotlin 在 ListAdapter 中使用 Filterable?【英文标题】:How to use Filterable in ListAdapter using Kotlin? 【发布时间】:2021-05-03 03:21:05 【问题描述】:我会使用SearchView
来过滤我的RecyclerView
,在*** 和其他网站上,我发现只是在我使用ListAdapter
时将Filterable
与Java 和RecyclerView.Adapter
一起使用的示例。
所以我试图自己制作自定义过滤器,但是当我尝试过滤适配器时,我在publishResults
中的MutableList
上得到一个空值。
我的适配器代码如下所示:
class ArticoliListAdapter : ListAdapter<Articolo, ArticoliListAdapter.ArticoliViewHolder>(ArticoliComparator()), Filterable
private val list = mutableListOf<Articolo>()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ArticoliViewHolder
return ArticoliViewHolder.create(parent)
override fun onBindViewHolder(holder: ArticoliViewHolder, position: Int)
val current = getItem(position)
holder.bind(current)
override fun getItemId(position: Int): Long
val articolo = currentList[position]
return articolo.barcode.hashCode().toLong()
class ArticoliViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView)
private val barcode: TextView = itemView.findViewById(R.id.barcode)
private val qta: TextView = itemView.findViewById(R.id.qta)
private val desc: TextView = itemView.findViewById(R.id.desc)
private val um: TextView = itemView.findViewById(R.id.um)
fun bind(articolo: Articolo?)
barcode.text = articolo?.barcode
qta.text = articolo?.qta?.formatForQta()
um.text = articolo?.um?.toLowerCase(Locale.ITALIAN)
desc.text = if(articolo?.desc.isNullOrEmpty()) "-" else articolo?.desc
private fun Float.formatForQta(): String
val floatString = this.toString()
val decimalString: String = floatString.substring(floatString.indexOf('.') + 1, floatString.length)
return when (decimalString.toInt() == 0)
true -> this.toInt().toString()
false -> "%.3f".format(this)
companion object
fun create(parent: ViewGroup): ArticoliViewHolder
val view: View = LayoutInflater.from(parent.context)
.inflate(R.layout.item_layout, parent, false)
return ArticoliViewHolder(view)
class ArticoliComparator : DiffUtil.ItemCallback<Articolo>()
override fun areItemsTheSame(oldItem: Articolo, newItem: Articolo): Boolean
return oldItem === newItem
override fun areContentsTheSame(oldItem: Articolo, newItem: Articolo): Boolean
return oldItem.qta == newItem.qta
override fun getFilter(): Filter
return customFilter
private val customFilter = object: Filter()
override fun performFiltering(constraint: CharSequence?): FilterResults
val filteredList = mutableListOf<Articolo>()
if (constraint == null || constraint.isEmpty())
filteredList.addAll(currentList)
else
val filterPattern = constraint.toString().toLowerCase(Locale.ITALIAN).trim it <= ' '
for (item in currentList)
if (item.barcode.toLowerCase(Locale.ITALIAN).contains(filterPattern) || item.desc?.toLowerCase(
Locale.ITALIAN
)!!.contains(filterPattern))
filteredList.add(item)
val results = FilterResults()
results.values = filteredList
return results
override fun publishResults(constraint: CharSequence?, filterResults: FilterResults?)
list.clear()
list.addAll(filterResults?.values as MutableList<Articolo>)
notifyDataSetChanged()
所以我想知道在 Kotlin 中使用 ListAdapter 构建自定义过滤器来过滤我在 recyclerView 中的数据的正确方法是什么。
我在片段中这样调用过滤器:
override fun onQueryTextChange(query: String?): Boolean
adapter.filter.filter(query)
return false
但是当我尝试过滤时没有发生任何事情并且仍然显示所有项目...
RecyclerView
适配器的数据是从我的ViewHolder
设置的,数据是从数据库 (LiveData<List<Articolo>>
) 获取的
这是我的片段中的代码:
articoliViewModel.articoli.observe(viewLifecycleOwner) articoli ->
articoli.let adapter.submitList(it)
【问题讨论】:
这里的currentList
是什么?我没有看到声明
@ADM currentList 这里是 (Mutable)List (currentList 等于 getCurrentList) 到适配器的数据是通过 LiveData 从数据库中获取来设置的
不要使用可过滤的!这是不必要的复杂性和样板代码。只需使用常规过滤方法。里面 onquerytextchanged adapter.submitList(list.filter...)
你看到的那些答案可能是在java上的。我自己曾经通过建议使用可过滤来回答,但这是一个错误。即使您使用 java,也可以创建具有过滤功能的 kotlin 类并在 java 代码中使用。
【参考方案1】:
我在下面列出的代码中的一些缺陷。
currentList
持有当前列表中的项目,而不是完整的项目列表。即,如果您有 10 个项目,并且在过滤后您得到 3 个项目,那么 currentList
将持有 3 个项目而不是 10 个。所以你不能使用currentList
来过滤列表。相反,你坚持CompleteList
并在此应用过滤器。
你不应该打电话给notifyDataSetChanged()
,这违背了拥有DiffUtils
的全部目的,而是你打电话给#submitList
Al 认为您将完整列表引用为全局变量,但您从未为其赋值,它始终为空。
我制作了一个工作示例来说明。请尝试使用您的代码添加下面的基本代码。我使用类型为String
只是为了让示例易于理解,您可以使用您的自定义对象。您还可以修改代码以使其看起来更好,但我认为这足以了解ListAdapter
的工作原理。
class ArticoliListAdapter : ListAdapter<String, ArticoliListAdapter.ArticoliViewHolder>(ArticoliComparator()), Filterable
private var list = mutableListOf<String>()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ArticoliViewHolder
return ArticoliViewHolder.create(parent)
override fun onBindViewHolder(holder: ArticoliViewHolder, position: Int)
val current = getItem(position)
holder.bind(current)
fun setData(list: MutableList<String>?)
this.list = list!!
submitList(list)
class ArticoliViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView)
private val desc: TextView = itemView.findViewById(R.id.txtName)
fun bind(name: String)
desc.text = name.toUpperCase()
companion object
fun create(parent: ViewGroup): ArticoliViewHolder
val view: View = LayoutInflater.from(parent.context)
.inflate(R.layout.item_list, parent, false)
return ArticoliViewHolder(view)
class ArticoliComparator : DiffUtil.ItemCallback<String>()
override fun areItemsTheSame(oldItem: String, newItem: String): Boolean
return oldItem === newItem
override fun areContentsTheSame(oldItem: String, newItem: String): Boolean
return oldItem == newItem
override fun getFilter(): Filter
return customFilter
private val customFilter = object : Filter()
override fun performFiltering(constraint: CharSequence?): FilterResults
val filteredList = mutableListOf<String>()
if (constraint == null || constraint.isEmpty())
filteredList.addAll(list)
else
for (item in list)
if (item.toLowerCase().startsWith(constraint.toString().toLowerCase()))
filteredList.add(item)
val results = FilterResults()
results.values = filteredList
return results
override fun publishResults(constraint: CharSequence?, filterResults: FilterResults?)
submitList(filterResults?.values as MutableList<String>)
当您将数据设置到适配器时,您调用 setData
而不是 submitList
。
articoliViewModel.articoli.observe(viewLifecycleOwner) articoli ->
articoli.let adapter.setData(it)
【讨论】:
在我的 SearchView 中我应该如何调用过滤器? 同理 adapter.getfilter().filter("text"). 好吧,看来我成功了,但是当 searchView 为空时如何重置列表?【参考方案2】:如果我错了,请纠正我,但我会说这里有一个错误:
override fun publishResults(constraint: CharSequence?, filterResults: FilterResults?)
list.clear()
list.addAll(filterResults?.values as MutableList<Articolo>)
notifyDataSetChanged()
我会做以下事情:
override fun publishResults(constraint: CharSequence?, filterResults: FilterResults?)
list.clear()
list.addAll(filterResults?.values as MutableList<Articolo>)
submitList(list)
【讨论】:
以上是关于如何使用 Kotlin 在 ListAdapter 中使用 Filterable?的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 getFilter() 和 ListAdapter 过滤 ListView?
当视图更新 Android Kotlin 时,DiffUtil 不与 ListAdpater 一起使用
如何将自定义 ListAdapter 设置为 appwidget 中的列表视图?