I am using one of Android Jetpack Component ViewModel + Live data in my project it works fine for me when using normal data such as string and Int but when it comes to arrayList it won't observe anything
Here's my code
class MainActivityModel : ViewModel() {
private var dataObservable = MutableLiveData<ArrayList<Int>>()
init {
dataObservable.value = arrayListOf(1,2,3,4,5)
}
fun getInt(): LiveData<ArrayList<Int>> = dataObservable
fun addInt(i:Int) {
dataObservable.value!![i] = dataObservable.value!![i].plus(1)
}
}
A LiveData won't broadcast updates to observers unless its value is completely reassigned with a new value. This does not reassign the value:
dataObservable.value!![i] = dataObservable.value!![i].plus(1)
What it does is retain the existing array, but add an array element. LiveData doesn't notify its observables about that. The actual array object has to be reassigned.
If you want to reassign a new array value and notify all observers, reassign a new array altogether, like this:
dataObservable.value = dataObservable.value!![i].plus(1)
Assigning dataObservable.value will call the LiveData setValue() method, and notify observers of the new value passed to setValue() .
If you modify your complex observable object outside of main thread you need to use postValue
dataObservable?.value[i] += 1
dataObservable.apply {
postValue(value)
}
Related
I have MVVM fragment with this simple requirement.
The fragment have a EntryText field, and I want to populate with a value calculated in the view model,
when the viewmodel is created I need to call a Room request and get a value, then that value should be present to the user, and the user can change that value.
Also I'm soring that value in the State Handle
var parcelaDesde = state.get<String>("parcelaDesde")?:"0"
set(value) {
field = value
state.set("parcelaDesde", value)
}
I resolved with:
*creating a public methos in viewmodel that retrieve info from Room and update local member field, also update a MutableLiveData that is observed in Fragment
fun updateParcelaDesde(default: Int) {
val num = parcelaDesde.toIntOrNull()
num?.let {
if (it == default) {
viewModelScope.launch(Dispatchers.IO) {
parcelaDesde = filialcruzaRepository.getMinNroParcela(filial.value?.id!!)?.toString()?:"0"
Log.d(TAG, "updateParcelaDesde: $parcelaDesde")
isLoadingDesde.postValue(false)
}
}
}
}
on the Fragment, I just observe the liveData and update the UI when te loading is completed.
viewModel.isLoadingDesde.observe(viewLifecycleOwner) {
binding.etParcelaDesde.setText( viewModel.parcelaDesde.trim() )
}
Is this the correct way ?
How to do this if I want to us dataBinding?
Best Regards
I am using live data with room database and my activity observes live data provided from room database.
#Query("SELECT * FROM BUS WHERE BUS_CATEGORY = :busCategory")
LiveData<List<Bus>> getLiveBuses( String busCategory);
ViewModels gets LiveData via Dao(Data Access Object) and activity observes this live data.
Now it works fine. But when busCategory changes i can't modify this live data to get buses for newly selected busCategory.
So how can i observe this same liveData where query parameters is changeable?
I suggest you to to use viewModel. I did the query and observe changes using MutableLiveData.
First step
val mutableBusCategory: MutableLiveData<String> = MutableLiveData()
Setter for mutablelivedata
fun searchByCategory(param: String) {
mutableBusCategory.value = param
}
observable to observe the change
val busObservable: LiveData<Bus> = Transformations.switchMap(mutableBusCategory) { param->
repository.getLiveBuses(param)
}
and final step to observe the live data
busObservable.observe(this, Observer {
//your logic for list})
and to trigger mutablelivedata
searchByCategory(//categoryName)
I don't think this is a reasonable expectation. It would make more sense to fire off a new query and subscribe to that.
I have a method as follow in the repository class which returns list of countries either from local database or from network as LiveData:
fun loadCountries(): LiveData<Resource<List<Country>>> {
return object : NetworkBoundResource<List<Country>, List<CountryResponse>>() {
...
}.asLiveData()
}
In ViewModel, I have a LiveData that keeps the returning LiveData:
class CountryViewModel : ViewModel() {
val countryListResource: LiveData<Resource<List<Country>>> = countryRepository.loadCountries()
fun refresh() {
// How to assign new LiveData returned by countryRepository.loadCountries() here?
// SwitchMap needs other LiveData to be used.
}
}
User should be able to refresh data and that's where my problem arises. I need to inject new LiveData returned by countryRepository.loadCountries() into countryListResource and I don't know how to achieve this?
If I do countryListResource = countryRepository.loadCountries() (which I cannot since it is val), the observer needs to stop observing previous one and start observing the latest one.
If I use swichMap on countryListResource, I need another MutableLiveData to trigger a new call from the repository, as shown here.
Is there any other way to achieve this?
The solution is very simple.
First in your repository class create mutable say obj1 and immutable say obj2 live data objects of type country list.
Obj2 is initialised with obj1.Then loadCountry() function should update only the obj1.
Your view model should observe the obj2 using observeForever().
Then view model also do create two obj same as repo say obj3 and obj4 resp. Observe the obj4 in your activity. Obj3 is getting updated using postValue() in observeForever() and which in-turns update obj4(initialised with obj3).
Hope this helps you.
Example:
Repository
// obj1
private val _countryLiveData : MutableLiveData<List<Country>>
// obj2
val countryLiveData : LiveData<List<Country>>
get() = _countryLiveData
fun loadCountries() {
val list = fetchCountryList()
_countryLiveData.postValue(list)
}
ViewModel
// obj3
private val _countryLiveData : MutableLiveData<List<Country>>
// obj4 live data to be observed in activity
val countryLiveData : LiveData<List<Country>>
get() = _countryLiveData
init {
countryRepository.countryLiveData.observeForever { countryList ->
this._countryLiveData.postValue(countryList)
}
}
fun refresh() {
countryRepository.loadCountries()
}
I have a MutableLiveData of type ArrayDeque.
I am trying to observe it.
It works when I first assign a value to it (create the ArrayDeque) but what I am trying to do is observe changes to the contents i.e. when new entries are added or when entries are removed.
var moveHistory = MutableLiveData<ArrayDeque<Move>>()
..
moveHistory.value = ArrayDeque<Move>() <<--- this fires
moveHistory.value?.addFirst(MontanaMoveStandard(from, to)) <<- this doesn't fire
this is my observe code:
moveHistory.observe(this, Observer {
moveHistory -> undoButton?.isEnabled = moveHistory.size > 0
})
This what I did in the end.
I made my own class MoveHistory which extends MutableLiveData and perform the actions on that. I added to it the methods I use (addFirst and pollFirst) and then reassign value to itself as suggested by Luksprog
This is the class I ended up with:
class MoveHistory(): Serializable, MutableLiveData<ArrayDeque<Move>>(){
init {
value = ArrayDeque<Move>()
}
fun addFirst(move: Move) {
value?.addFirst(move)
value = value
}
fun pollFirst(): Move {
var move = value?.pollFirst()
value = value
return move!!
}
}
Now all I have to do is register my observer on an instance of that class and use the methods to add and remove.
I'm using LiveData and ViewModel from the architecture components in my app.
I have a list of items that is paginated, I load more as the user scrolls down. The result of the query is stored in a
MutableLiveData<List<SearchResult>>
When I do the initial load and set the variable to a new list, it triggers a callback on the binding adapter that loads the data into the recyclerview.
However, when I load the 2nd page and I add the additional items to the list, the callback is not triggered. However, if I replace the list with a new list containing both the old and new items, the callback triggers.
Is it possible to have LiveData notify its observers when the backing list is updated, not only when the LiveData object is updated?
This does not work (ignoring the null checks):
val results = MutableLiveData<MutableList<SearchResult>>()
/* later */
results.value.addAll(newResults)
This works:
val results = MutableLiveData<MutableList<SearchResult>>()
/* later */
val list = mutableListOf<SearchResult>()
list.addAll(results.value)
list.addAll(newResults)
results.value = list
I think the extension is a bit nicer.
operator fun <T> MutableLiveData<ArrayList<T>>.plusAssign(values: List<T>) {
val value = this.value ?: arrayListOf()
value.addAll(values)
this.value = value
}
Usage:
list += anotherList;
According to MutableLiveData, you need to use postValue or setValue in order to trigger the observers.