Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android Jetpack: RecyclerView is not updating when LiveData is set

So I have a simple implementation to display a list of users in a RecyclerView, and the list is queried in a ViewModel as LiveData.

The problem is that the UI is not updated to show the latest list - called users - even when the list is observed. I just set up a list of demo users for now.

Here's my ViewModel:

class MainViewModel : ViewModel() {

    private val demoData = listOf(
            User(userName = "Bob", favoriteColor = "Green"),
            User(userName = "Jim", favoriteColor = "Red"),
            User(userName = "Park", favoriteColor = "Blue"),
            User(userName = "Tom", favoriteColor = "Yellow"),
            User(userName = "Lee", favoriteColor = "Black"),
            User(userName = "Xiu", favoriteColor = "Gray")
    )

    private val _users = MutableLiveData<List<User>>()
    val users: LiveData<List<User>>
        get() = _users

    init {
        _users.value = listOf()
    }

    fun loadUsers() {
        _users.value = demoData.toMutableList().apply { shuffle() }
    }
}

And my ViewModel's attached Fragment:

// ...

override fun onActivityCreated(savedInstanceState: Bundle?) {
    super.onActivityCreated(savedInstanceState)
    viewModel = ViewModelProviders.of(this).get(MainViewModel::class.java)

    viewModel.users.observe(this, Observer {
        mAdapter.notifyDataSetChanged()
    })

    mAdapter = UsersAdapter(viewModel.users.value!!)

    mainRV = view?.findViewById<RecyclerView>(R.id.rv_main)?.apply {
        adapter = mAdapter
        layoutManager = LinearLayoutManager(view?.context)
    }

    viewModel.loadUsers()
}

P.S. the UsersAdapter is a usual RecyclerView.Adapter.

I have made sure to call setValue on my users list to call the Observer, thus I am not sure what is missing here. Is my adapter wrongly setup?

like image 382
Fawwaz Yusran Avatar asked Oct 28 '22 10:10

Fawwaz Yusran


1 Answers

fun loadUsers() {
    _users.value = demoData.toMutableList().apply { shuffle() }
}

toMutableList() creates a new list with the data, see the source code:

public fun <T> Collection<T>.toMutableList(): MutableList<T> {
    return ArrayList(this)
}

So instead of getting the initial value and never updating your adapter, you should update the list in the adapter and show that.

viewModel.users.observe(this, Observer { users ->
    mAdapter.updateData(users)
})

Where if you are not using ListAdapter, then you can define this method like this:

class MyAdapter: RecyclerView.Adapter<ViewHolder>(
   private var list: List<User> = Collections.emptyList()
) {
    ...

    fun updateData(users: List<User>) {
        this.users = users
        notifyDataSetChanged()
    }
}

You can also use ListAdapter and submitList and you'll also get animations.

like image 57
EpicPandaForce Avatar answered Nov 08 '22 11:11

EpicPandaForce