Livedata Update on Object Field Change

LiveData update on object field change

I don't think there is any best practice as such recommended by android for this. I would suggest you to use the approach which uses cleaner & less boilerplate code.

If you are using android data binding along with LiveData you can go with the following approach:

Your POJO object would look something like this

public class User extends BaseObservable {
private String firstName;
private String lastName;

@Bindable
public String getFirstName() {
return firstName;
}

public void setFirstName(String firstName) {
this.firstName = firstName;
notifyPropertyChanged(BR.firstName);
}

@Bindable
public String getLastName() {
return lastName;
}

public void setLastName(String lastName) {
this.lastName = lastName;
notifyPropertyChanged(BR.lastName);
}
}

So you would be already having a class which notifies whenever its property changes. So you can just make use of this property change callback in your MutableLiveData to notify its observer. You can create a custom MutableLiveData for this

public class CustomMutableLiveData<T extends BaseObservable>
extends MutableLiveData<T> {

@Override
public void setValue(T value) {
super.setValue(value);

//listen to property changes
value.addOnPropertyChangedCallback(callback);
}

Observable.OnPropertyChangedCallback callback = new Observable.OnPropertyChangedCallback() {
@Override
public void onPropertyChanged(Observable sender, int propertyId) {

//Trigger LiveData observer on change of any property in object
setValue(getValue());

}
};

}

Then all you need to do is use this CustomMutableLiveData instead of MutableLiveData in your View Model

public class InfoViewModel extends AndroidViewModel {

CustomMutableLiveData<User> user = new CustomMutableLiveData<>();
-----
-----

So by doing this you can notify both view & LiveData observer with little change to existing code. Hope it helps

LiveData update values when field of object change

Try this:

You need to make your model class extend BaseObservable.

public class Object extends BaseObservable {
private String name;
private float internalvalue;
private float in1;
private float out1;
private float out2;

public Object(String name, float internalvalue) {
this.name = name;
this.internalvalue = internalvalue;
}

public float getOut1() {
return this.out1;
}

public void setOut1(float out1) {
this.out1 = out1;
notifyChange();
}

public float getOut2() {
return this.out2;
}

public void setOut2(float out2) {
this.out2 = out2;
notifyChange();
}

public void setIn1(float in1) {
this.in1 = in1;
}

private void performSomething(float internalvalue, float in1) {
SubClassSingleton.performSomething(internalvalue, in1, new SubClassSingletonListener() {
@Override
public void onSuccess(float out1, float out2) {
setOut1(out1);
setOut2(out2);
}
});
}
}

And in View, you cannot bind float directly to view. It has to be string, so bind like this:

<layout ...
<data>
<variable
name="viewModel"
type="app.example.MainViewModel"/>
</data>
<LinearLayout
...>
<EditText
android:text='@={""+viewModel.obj.in1}'
.../>
<TextView
android:text='@{""+viewModel.obj.out1}'
.../>
<TextView
android:text='@{""+viewModel.obj.out2}'
.../>
</LinearLayout>
</layout>

Changes to LiveData of composite objects

The fact is that the onChanged method of the LiveData's Observer is only called when the containing value of the LiveData is being set using setValue() or postValue() method. There is no mechanism to observe fields inside an object that is held by a LiveData. As a result, by changing the value of fields inside object-A, the observer shouldn't be notified.

On the other hand, as you know, the methods that are provided by Room to query on a db are able to return LiveData<SomeType> instead of SomeType. Under the hood, Room creates and registers a ContentObserver on your query's table(s) to get aware of changes in it. So, every time a change occurs on the data, Room gets notified using the mentioned ContentObserver, then fetches the query result again and post it on the LiveData.

How to update LiveData value?

Finally I found the solution after the struggle of 3 days,

For updating the value of LiveData<PagedList> we can use MediatorLiveData, as follows, inside your ViewModel class:

public LiveData<PagedList<RecipeListPojo>> liveData;
private MediatorLiveData<PagedList<RecipeListPojo>> mediatorLiveData;

public RecipeListViewModel(@NonNull Application application) {
super(application);
mediatorLiveData = new MediatorLiveData<>();
}

public MediatorLiveData<PagedList<RecipeListPojo>> init(RecipeDao recipeDao, RecipeFrom recipeFrom, String orderBy) {
liveData = new LivePagedListBuilder(recipeDao.getAllRecipesList(simpleSQLiteQuery), 6).build();

mediatorLiveData.addSource(liveData, new Observer<PagedList<RecipeListPojo>>() {
@Override
public void onChanged(@Nullable PagedList<RecipeListPojo> recipeListPojos) {
mediatorLiveData.setValue(recipeListPojos);
}
});
return mediatorLiveData;
}

Further details can be found here about MediatorLiveData

How can i update a field of a ListItem (LiveData)

LiveData is just a container holding a reference to your data and notifying possible observer of changes. So as far as LiveData is concerned this is the best you can do.

However LiveData does't support molecular changes so every change may results in the entire list invalidating and redrawn. It is strongly suggested that you use a different method if you have lot of changes in data (like real-time applications).

LiveData - How to observe changes to List inside object?

MutableLiveData will only notify view when it value changes, e.g. when you place other value which is different from an old one. That's why user.value = user.value!!.copy(pets = newList) works.

MutableLiveData cannot know when one of the fields was changed, when they're simple basic types/classes.

But you can make pets a mutable state, in this case live data will be able to notify about changes. Define it like val pets = mutableStateListOf<String>().


I personally not a big fan of live data, and code with value!! looks not what I'd like to see in my project. So I'll tell you about compose way of doing it, in case your project will allow you to use it. You need to define both pets as a mutable state list of strings, and user as a mutable state of user.

I suggest you read about compose states in the documentation carefully.

Also note that in my code I'm defining user with delegation, and pets without delegation. You can use delegation only in view model, and inside state holders you cannot, othervise it'll become plain objects at the end.

@Composable
fun TestView() {
val viewModel = viewModel<TestViewModel>()
Column {
// TextField and Button that calls viewModel.addPet(petName)
var i by remember { mutableStateOf(0) }

Button(onClick = { viewModel.addPet("pet ${i++}") }) {
Text("add new pet")
}

LazyColumn {
items(viewModel.user.pets) { pet ->
Text(text = pet)
}
}
}
}

class User {
val pets = mutableStateListOf<String>()
}

class TestViewModel: ViewModel() {
val user by mutableStateOf(User())

fun addPet(petName: String) {
user.pets.add(petName)
}
}


Related Topics



Leave a reply



Submit