I'm trying to implement a filter for my RecyclerView
. I use data-binding and my adapter is a ListAdapter
subclass as shown below
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)
}
According to the docs, to add filtering functionality I need to implement Filterable
in my adapter and define getFilter()
method. And this is where I stuck: I simply don't know how to implement getFilter()
in the case of ListAdapter
. Any help will be appreciated.
I had a similar problem and tried solving it using a method similar to the one described by Maor Hadad above. it worked at some times and it raised cast error in the
Filter.publishResult()
method. So, I solved it this way.
Firstly create a variable private var unfilteredlist = listOf<BaseDataItem>()
and a methods
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)
}
in the BookAdapter
class. Where *field1*
and *field2*
(you can add more fields) are the fields you want the search query to match. Then, wherever you call the adapter.submitList(List<BaseDataItem>)
in the original code, replace it with the custom method adapter.modifyList(List<BaseDataItem>)
.
Then write the searchView.setOnQueryTextListener
like the one below
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
}
})
Don't forget to remove the Filterable
interface and its methods, you don't need them anymore
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With