Overview
The best way is to filter the items sent into adapter, but I can't do this because I am using FirestoreRecyclerAdapter
.
I cannot filter via Firestore database operation because I am doing partial text search.
I perform search by by manipulating RecyclerView.Adapter.getItemCount
and RecyclerView.Adapter.getItem
.
Perform search by selectively hiding the view is not recommended, as sometimes you might end up with a blank view which still occupy the space.
Another interesting solution is via SortedList, as shown in this example.
Model
The adapter support 2 types of view
class Note { open val name: String? get() = doc.get("name") as? String}
class Update(val doc: DocumentSnapshot) { open val content: String? get() = doc.get("content") as? String}
Adapter
Calling search
will trigger notifyDataSetChanged
, which trigger getItemCount
and getItem
which will show the appropriate items.
private class LocalAdapter(options: FirestoreRecyclerOptions<Post>, val viewModel: HomeViewModel) : FirestoreRecyclerAdapter<Post, LocalAdapter.ViewHolder>(options) { private var searchText: String = "" // indicate if search mode private val isSearch: Boolean get() = searchText.isNotBlank() // store the list of filtered search items private var searchItems: MutableList<Post> = mutableListOf() override fun getItemViewType(position: Int): Int { return when(val item = getItem(position)) { is Note -> R.layout.home_item_note is Update -> R.layout.home_item_update else -> R.layout.home_item_note } // return super.getItemViewType(position) } override fun getItemCount(): Int { return if (isSearch) { searchItems = mutableListOf() for (position in 0 until super.getItemCount()) { var item = super.getItem(position) when(item) { is Note -> { // will regex be faster? // or store lowercase text(all related fields) as map // or use internal text search engine if (item.name?.toLowerCase()?.contains(searchText) == true) { searchItems.add(item) } } is Update -> { if (item.content?.toLowerCase()?.contains(searchText) == true) { searchItems.add(item) } } } } searchItems.size } else { super.getItemCount() } } override fun getItem(position: Int): Post { return if (!isSearch) { super.getItem(position) } else { searchItems[position] } } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): LocalAdapter.ViewHolder { val view = LayoutInflater.from(parent.context) .inflate(viewType, parent, false) return ViewHolder(view) } override fun onBindViewHolder( holder: LocalAdapter.ViewHolder, position: Int, item: Post ) { // holder.batchNoTextView.text = "#${item.batchNo}" // holder.nameTextView.text = item.name when(item) { is Note -> { bindNote(holder, item) } is Update -> { bindUpdate(holder, item) } } } private fun bindNote(holder: ViewHolder, item: Note) { holder.apply { noteNameTextView.text = item.name } } private fun bindUpdate(holder: ViewHolder, item: Update) { holder.apply { updateContentTextView.text = item.content } } fun search(text: String) { searchText = text notifyDataSetChanged() } inner class ViewHolder(override val containerView: View) : RecyclerView.ViewHolder(containerView), LayoutContainer}
SearchView
Refer to Add SearchView To Android Toolbar to capture search input.