使用SearchView过滤RecyclerView / ListAdapter

时间:2019-10-10 09:06:49

标签: kotlin android-recyclerview filtering listadapter

我正在尝试为RecyclerView实现过滤器。我使用数据绑定,并且我的适配器是ListAdapter子类,如下所示

class BookAdapter(private val clickListener: ClickHandler) :
    ListAdapter<Book, BookAdapter.ViewHolder>(BooksDiffCallback()) {

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        return ViewHolder.from(parent)
    }

    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        holder.bind(getItem(position)!!, clickListener)
    }

    class ViewHolder private constructor(val binding: BookItemBinding) :
        RecyclerView.ViewHolder(binding.root) {

        fun bind(
            item: Book,
            clickListener: ClickHandler
        ) {
            binding.book = item
            binding.clickListener = clickListener
            binding.executePendingBindings()
        }

        companion object {
            fun from(parent: ViewGroup): ViewHolder {
                val inflater = LayoutInflater.from(parent.context)
                val binding = BookItemBinding.inflate(inflater, parent, false)
                return ViewHolder(binding)
            }
        }
    }
}

class BooksDiffCallback : DiffUtil.ItemCallback<Book>() {
    override fun areItemsTheSame(oldItem: Book, newItem: Book): Boolean {
        return oldItem.id == newItem.id
    }

    override fun areContentsTheSame(oldItem: Book, newItem: Book): Boolean {
        return oldItem == newItem
    }

}

class ClickHandler(val clickListener: (id: String) -> Unit) {
    fun onClick(item: Book) = clickListener(item.id)
}

根据文档,要添加过滤功能,我需要在适配器中实现Filterable并定义getFilter()方法。这就是我要坚持的地方:在getFilter()的情况下,我根本不知道如何实现ListAdapter。任何帮助将不胜感激。

2 个答案:

答案 0 :(得分:9)

我遇到了类似的问题,并尝试使用与上述Maor Hadad描述的方法类似的方法来解决。它有时起作用,并且在

中引发了投放错误

Filter.publishResult()

方法。所以,我用这种方法解决了。 首先创建一个变量private var unfilteredlist = listOf<BaseDataItem>()和一个方法

fun modifyList(list : List<BaseDataItem>) {
    unfilteredList = list
    submitList(list)
}

fun filter(query: CharSequence?) {
    val list = mutableListOf<BaseDataItem>()

    // perform the data filtering
    if(!query.isNullOrEmpty()) {
        list.addAll(unfilteredList.filter {
            it.*field1*.toLowerCase(Locale.getDefault()).contains(query.toString().toLowerCase(Locale.getDefault())) ||
                    it.*field2*.toLowerCase(Locale.getDefault()).contains(query.toString().toLowerCase(Locale.getDefault())) })
    } else {
        list.addAll(unfilteredList)
    }

    submitList(list)
}
BookAdapter类中为

*field1**field2*(您可以添加更多字段)是您要搜索查询匹配的字段。然后,无论您在原始代码中调用adapter.submitList(List<BaseDataItem>)到哪里,都将其替换为自定义方法adapter.modifyList(List<BaseDataItem>)。 然后像下面这样写searchView.setOnQueryTextListener

searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
    override fun onQueryTextSubmit(query: String): Boolean {

        return false
    }

    override fun onQueryTextChange(newText: String): Boolean {

        (binding.recycler.adapter as ItemAdapter).filter(newText)
        return true
    }
})

别忘了删除Filterable接口及其方法,您不再需要它们

答案 1 :(得分:0)

我找不到获取列表的方法,因此我保存了对列表的引用。 代码示例:

ListAdapter: 实施可过滤:

class ItemAdapter(private val clickListener: ItemListener) :
ListAdapter<ItemAdapter.BaseDataItem, RecyclerView.ViewHolder>(ItemDiffCallBack()), Filterable {

添加变量以供参考:

var mListRef: List<BaseDataItem>? = null
var mFilteredList: List<BaseDataItem>? = null

在您首次提交列表之前,请先保存其引用

    withContext(Dispatchers.Main) {
        if (mListRef == null) {
                mListRef = items
            }
        submitList(items)
    }

过滤器:

override fun getFilter(): Filter {

    return object : Filter() {
        override fun performFiltering(charSequence: CharSequence): FilterResults {

            val charString = charSequence.toString()

            if (charString.isEmpty()) {

                mFilteredList = mListRef
            } else {
                mListRef?.let {
                    val filteredList = arrayListOf<BaseDataItem>()
                    for (baseDataItem in mListRef!!) {
                        if (baseDataItem is BaseDataItem.DataItemWrapper) {
                            if (charString.toLowerCase(Locale.ENGLISH) in baseDataItem.dataItem.Name.toLowerCase(
                                    Locale.ENGLISH
                                )
                            ) {
                                filteredList.add(baseDataItem)
                            }
                        }
                    }

                    mFilteredList = filteredList
                }
            }
            val filterResults = FilterResults()
            filterResults.values = mFilteredList
            return filterResults
        }

        override fun publishResults(
            charSequence: CharSequence,
            filterResults: FilterResults
        ) {
            mFilteredList = filterResults.values as ArrayList<BaseDataItem>
            submitList(mFilteredList)
        }
    }
}

如果您在片段中进行搜索,请添加以下内容:

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setHasOptionsMenu(true)
}

override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
    inflater.inflate(R.menu.menu_main, menu)
    val mSearchMenuItem = menu.findItem(R.id.search)
    val searchView = mSearchMenuItem.actionView as SearchView
    search(searchView)
    super.onCreateOptionsMenu(menu, inflater)
}


private fun search(searchView: SearchView) {

    searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
        override fun onQueryTextSubmit(query: String): Boolean {

            return false
        }

        override fun onQueryTextChange(newText: String): Boolean {

            (binding.recycler.adapter as ItemAdapter).filter.filter(newText)
            return true
        }
    })
}