首页 > 解决方案 > 如何从现有列表中搜索 diffutil 过滤掉结果

问题描述

我在我的 RecyclerView 中使用 DiffUtil 来使用 Room 组件显示来自数据库的列表。我想在 Appbar 中添加一个搜索功能,它会在用户输入时过滤掉列表中的现有项目。

我的应用目前在操作栏中有一个搜索图标,当您单击搜索图标时,它将在整个应用栏展开并允许用户搜索数据库并返回一个新列表。此方法涉及每次查询数据库。

搜索菜单,这是设置搜索小部件参数的地方。

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">
    <item android:id="@+id/my_search"
        android:title="Search"
        android:icon="@drawable/ic_search"
        app:showAsAction="collapseActionView|ifRoom"
        app:actionViewClass="androidx.appcompat.widget.SearchView" />
</menu>

RecyclerViewFragment

    override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
        super.onCreateOptionsMenu(menu, inflater)
        inflater.inflate(R.menu.recycler_view_menu, menu)

        val searchItem = menu.findItem(R.id.my_search)
        val searchView: SearchView = searchItem.actionView as SearchView
        searchView.imeOptions = EditorInfo.IME_ACTION_DONE   
        searchView.setIconifiedByDefault(false)
        searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
            override fun onQueryTextSubmit(query: String): Boolean {
                // This approach queries my database for a new list. 
                viewModel.searchTopic("%$query%")
                submitList()
                return false
            }

            override fun onQueryTextChange(newText: String): Boolean {
                // I would like to use the onTextChange() to filter out results from the list instead of querying a new list from the database. 
                return true
            }

        })
    }


    private fun submitList() {
        viewModel.listDevTopics.observe(viewLifecycleOwner, Observer {
            it?.let {
                rvAdapter.submitList(it)
            }
        })
    }

我的 RecyclerViewAdapter

class RecyclerViewAdapter() : androidx.recyclerview.widget.ListAdapter<Dev,
        RecyclerViewAdapter.ItemViewHolder>(MyDiffCallback()) {


    lateinit var searchList: List<Dev>

    class MyDiffCallback : DiffUtil.ItemCallback<Dev>() {
        
        override fun areItemsTheSame(oldItem: Dev, newItem: Dev): Boolean {
            return oldItem.topic == newItem.topic
        }

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


    class ItemViewHolder(private val view: View) :  RecyclerView.ViewHolder(view) {
         ...
    }


    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ItemViewHolder {
        ...
    }

    override fun onBindViewHolder(holder: ItemViewHolder, position: Int) {
        ...
    }

我想避免每次都查询数据库以获取搜索结果,我想使用 onQueryTextChange 以便它遍历现有列表并在用户输入查询时更新列表。

标签: kotlinandroid-searchandroid-diffutils

解决方案


只需实现 Filterable 并覆盖 getFilter 方法

并制作您的过滤器对象,然后在 getFilter 方法中返回此对象

class JobOrderAdapter(val clickListener: JobOrderListener) : ListAdapter<CJO,
    ViewHolder>(JobOrderDiffCallback()), Filterable {

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder =
    ViewHolder.from(parent)

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

private var list = listOf<CJO>()

fun setData(list: List<CJO>){
    this.list = list
    submitList(list)
}

override fun getFilter(): Filter = customFilter

private val customFilter = object : Filter() {
    override fun performFiltering(constraint: CharSequence?): FilterResults {
        val filteredList = mutableListOf<CJO>()
        if (constraint == null || constraint.isEmpty()) {
            filteredList.addAll(list)
        } else {
            val filterPattern = constraint.toString().toLowerCase().trim()

            for (item in list) {
// here i am searching at custom obj by managerName
                if (item.managerName.toLowerCase().contains(filterPattern)) {
                    filteredList.add(item)
                }
            }
        }
        val results = FilterResults()
        results.values = filteredList
        return results
    }

    override fun publishResults(constraint: CharSequence?, filterResults: FilterResults?) {
        submitList(filterResults?.values as MutableList<CJO>?)
    }
}}

并从您的 Fragmnet 或活动中调用 adapter.filter.filter(yourQueryText)

    override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
    inflater.inflate(R.menu.home_menu, menu)
    val searchByContract = menu.findItem(R.id.search_by_name)
    val searchContractView = searchByContract.actionView as SearchView

    searchContractView.queryHint = "البحث باسم مدير البيع"
    searchContractView.inputType = InputType.TYPE_CLASS_TEXT

    searchContractView.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
        override fun onQueryTextSubmit(query: String): Boolean {
            return false
        }

        override fun onQueryTextChange(newText: String?): Boolean {
            adapter.filter.filter(newText)
            return false
        }
    })

    super.onCreateOptionsMenu(menu, inflater)
}

推荐阅读