Reputation: 1382
I'm using DiffUtil in my RecyclerView to displays a list from a database using the Room component. I would like to add a search function in the Appbar, that will filter out the existing items in the list as the user is typing.
My app currently has a search icon in the action bar, when you click the search icon it will expand across the Appbar and allow the user to search the database and return a new list. This mehtod involves querying the database each time.
Search Menu, This is where the parameters for the search widget are set.
<?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)
}
})
}
My 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) {
...
}
I would like to avoid querying the database every time for a search result, I want to use onQueryTextChange so it goes through the existing list and updates the list as the user is entering their query.
Upvotes: 1
Views: 1080
Reputation: 374
just implement Filterable and override getFilter Method
and make your filter object then return this object at getFilter Method
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>?)
}
}}
and from your fragmnet or activity just call 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)
}
Upvotes: 2