【问题标题】:How to use Filterable in ListAdapter using Kotlin?如何使用 Kotlin 在 ListAdapter 中使用 Filterable?
【发布时间】:2021-05-03 03:21:05
【问题描述】:

我会使用SearchView 来过滤我的RecyclerView,在stackoverflow 和其他网站上,我发现只是在我使用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&lt;List&lt;Articolo&gt;&gt;) 获取的

这是我的片段中的代码:

   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 代码中使用。

标签: android kotlin android-recyclerview listadapter


【解决方案1】:

我在下面列出的代码中的一些缺陷。

  1. currentList 持有当前列表中的项目,而不是完整的项目列表。即,如果您有 10 个项目,并且在过滤后您得到 3 个项目,那么 currentList 将持有 3 个项目而不是 10 个。所以你不能使用currentList 来过滤列表。相反,你坚持CompleteList 并在此应用过滤器。

  2. 你不应该打电话给notifyDataSetChanged(),这违背了拥有DiffUtils的全部目的,而是你打电话给#submitList

  3. 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)
        }

【讨论】:

    猜你喜欢
    • 2021-03-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-11-20
    相关资源
    最近更新 更多