androidkotlinandroid-recyclerviewandroid-livedata

How to update data in a RecyclerView using LiveData and a SearchView


I have a Room database (with dao), a Repository, a ViewModel a RecyclerView and an Adapter interacting with each other.

Right now the RecyclerView just displays all the item in my ReferenceItem table.

I want to add a Search Bar at the top and filter the results in the RecyclerView.

Here is how I implement my RecyclerView and its Adapter in the Fragment:

@SuppressLint("NotifyDataSetChanged")
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        // Init la searchVal
        searchVal = ""

        // Set RecyclerView
        setRecyclerView(view)

        // Observe LiveData of all items
        viewModel.getFilteredReferenceItem(searchVal).observe(viewLifecycleOwner, Observer {

            refAdapter.referenceItems = viewModel.sortReferenceItemList(it)

            refAdapter.notifyDataSetChanged()
        })

The parameter .referenceItems in the refAdapter is what contains the list of item I want to display.

.referenceItems is of type MutableList<AdapterReferenceItem>

The getFilteredReferenceItem() method looks like this in the dao:

@Query(value="SELECT * FROM reference_items WHERE reference_item_name LIKE '%' || :searchVal || '%' ORDER BY category ASC, reference_item_name ASC")
fun getFilteredReferenceItem(searchVal : String): LiveData<List<ReferenceItem>>

It returns a LiveData.

The sortReferenceItemList() is to add Headers to certain item group. This is for aesthetic only. BUT, it takes a MutableList as argument and not a LiveData.

Now, I do not understand how the "it" argument work in :

refAdapter.referenceItems = viewModel.sortReferenceItemList(it)

I do not understand how "it" can be a MutableList when all I pass is a LiveData.

Now for the Search Bar, here is what I have so far. I am trying to change the value of searchVal to get the LiveData to update... but nothing happens.

        //SearchView
        searchView = view.findViewById<SearchView>(R.id.search_ref)
        searchView.setOnQueryTextListener(object : OnQueryTextListener,
            SearchView.OnQueryTextListener {
            override fun onQueryTextSubmit(query: String?): Boolean {
                TODO("Not yet implemented")
            }

            @SuppressLint("NotifyDataSetChanged")
            override fun onQueryTextChange(newText: String?): Boolean {

                searchVal = if (newText != null) {
                    newText.toString()
                } else{
                    ""
                }
                refAdapter.notifyDataSetChanged()

                return true
            }
        })

P.S: I am not a developer. I developed this app 3 years ago and never touched Kotlin or Android Studio ever since. But I think this is a nice update to my small personal app that I would love to finish.


Solution

  • well, you seems to recall everything but missed few things here and there.

    refAdapter.referenceItems = viewModel.sortReferenceItemList(it)
    

    The "it" here refers to the list the livedata holds .i.e

    LiveData<List<ReferenceItem>>
    

    You are trying to make a search functionality, here how you can do it in the viewModel

    private val _query = MutableLiveData<String>("")
        val query: LiveData<String> = _query
    
        val filteredItems: LiveData<List<ReferenceItem>> = Transformations.switchMap(_query) { query ->
            dao.getFilteredReferenceItem(query)
        }
    
        fun setSearchQuery(query: String) {
            _query.value = query
        }
    

    In the MainActivity,

    searchView = findViewById(R.id.searchView)
            searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
                override fun onQueryTextSubmit(query: String?): Boolean {
                    query?.let {
                        viewModel.setSearchQuery(it)
                    }
                    return true
                }
    
                override fun onQueryTextChange(newText: String?): Boolean {
                    newText?.let {
                        viewModel.setSearchQuery(it)
                    }
                    return true
                }
            })
    
    viewModel.filteredItems.observe(this) { filteredList ->
    
       // adapter.submitList(filteredList) , its better if you extend your adapter with ListAdapter so that you don't need call notifyDataSetChanged.
                refAdapter.referenceItems = filteredList
                refAdapter.notifyDataSetChanged()
    }