如何使用 Kotlin 在 ListAdapter 中使用 Filterable?
How to use Filterable in ListAdapter using Kotlin?
我会使用 SearchView
来过滤我的 RecyclerView
,在 Whosebug 和其他网站上我发现了将 Filterable
与 Java 和 RecyclerView.Adapter
而我正在使用 ListAdapter
..
所以我试图自己制作自定义过滤器,但是当我尝试过滤适配器时,我在 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) }
}
如果我错了,请纠正我,但我会说这里有错误:
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)
}
我在下面列出的代码中存在一些缺陷。
currentList
持有列表中的当前项目,而不是完整的项目列表。即,如果您有 10 件物品,并且在过滤后您得到 3 件物品,那么 currentList
将持有 3 件物品而不是 10 件。所以你不能使用 currentList
来过滤列表。相反,你坚持 CompleteList
并在这个上应用过滤器。
你不应该调用 notifyDataSetChanged()
这只会破坏 DiffUtils
的全部目的,而是调用 #submitList
我以为你有一个完整列表的引用作为全局变量,但你从来没有给它赋值,它总是空的。
我做了一个工作示例来说明。请尝试使用您的代码添加下面的基本代码。我使用 type as 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
来过滤我的 RecyclerView
,在 Whosebug 和其他网站上我发现了将 Filterable
与 Java 和 RecyclerView.Adapter
而我正在使用 ListAdapter
..
所以我试图自己制作自定义过滤器,但是当我尝试过滤适配器时,我在 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) }
}
如果我错了,请纠正我,但我会说这里有错误:
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)
}
我在下面列出的代码中存在一些缺陷。
currentList
持有列表中的当前项目,而不是完整的项目列表。即,如果您有 10 件物品,并且在过滤后您得到 3 件物品,那么currentList
将持有 3 件物品而不是 10 件。所以你不能使用currentList
来过滤列表。相反,你坚持CompleteList
并在这个上应用过滤器。你不应该调用
notifyDataSetChanged()
这只会破坏DiffUtils
的全部目的,而是调用#submitList
我以为你有一个完整列表的引用作为全局变量,但你从来没有给它赋值,它总是空的。
我做了一个工作示例来说明。请尝试使用您的代码添加下面的基本代码。我使用 type as 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) }
}