How to Implement Search/Filter for RecyclerView

Jul 24, 2020

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.

❤️ Is this article helpful?

Buy me a coffee ☕ or support my work via PayPal to keep this space 🖖 and ad-free.

Do send some 💖 to @d_luaz or share this article.

✨ By Desmond Lua

A dream boy who enjoys making apps, travelling and making youtube videos. Follow me on @d_luaz

👶 Apps I built

Travelopy - discover travel places in Malaysia, Singapore, Taiwan, Japan.