LiveData update on object field change

后端 未结 5 1665
渐次进展
渐次进展 2020-11-27 12:49

I\'m using Android MVVM architecture with LiveData. I have an object like this

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


        
相关标签:
5条回答
  • 2020-11-27 12:57

    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

    0 讨论(0)
  • 2020-11-27 13:10

    For your observer get notified you should use setValue if you do this user.getValue().setFirstName(user.getValue().getFirstName() + " A "); your observer will not be notified !

    View Model

    public MutableLiveData<User> getUser() {
        return user;
    }
    

    Activity / Fragment

    mModel = ViewModelProviders.of(this).get(InfoViewModel.class);
    mModel.getUser().observe(this, s -> {
        // User has been modified
    });
    

    Somewhere in your activity / fragment

    This will trigger the observer :

    mModel.getUser().setValue(user);
    

    If you want to update only one field from an object instead of update the whole object you should have multiples MutableLiveData<String>

    // View Model
    private MutableLiveData<String>         firstName;
    private MutableLiveData<String>         lastName;
    
    //Somewhere in your code
    mModel.getFirstName().setValue(user.getValue().getFirstName() + " A ");
    mModel.getFirstName().observe(this, s -> {
        // Firstname has been modified
    });
    
    0 讨论(0)
  • 2020-11-27 13:11

    When using MVVM and LiveData, you can re-bind the object to the layout so it will trigger all changes on the UI.

    Given "user" is a MutableLiveData<User> in the ViewModel

    ViewModel

    class SampleViewModel : ViewModel() {
        val user = MutableLiveData<User>()
    
        fun onChange() {
            user.value.firstname = "New name"
            user.value = user.value // force postValue to notify Observers
            // can also use user.postValue()
        }
    }
    

    Activity/Fragment file:

    viewModel = ViewModelProviders
                .of(this)
                .get(SampleViewModel::class.java)
    
    // when viewModel.user changes, this observer get notified and re-bind
    // the user model with the layout.
    viewModel.user.observe(this, Observer {
        binding.user = it //<- re-binding user
    })
    

    Your layout file shouldn't change:

    <data>
        <variable
            name="user"
            type="com.project.model.User" />
    </data>
    
    ...
    
    <TextView
            android:id="@+id/firstname"
            android:text="@{user.firstname}"
            />
    
    0 讨论(0)
  • 2020-11-27 13:16

    How can I make sure when some filed in user object changes observers get notified? BTW it is important to me to keep this data in the separate object and not use primary values like Strings in my ViewModel.

    You can use androidx.lifecyle.Transformation class to monitor for individual fields.

    val user = MutableLiveData<User>();
    //to monitor for User.Name
    val firstName: LiveData<String>  = Transformations.map {it.firstName}
    val lastName: LiveData<String>  = Transformations.map {it.lastName}
    

    you update user as per normal, and listen for firstname/lastname to monitor for changes in those fields.

    0 讨论(0)
  • 2020-11-27 13:18

    If you are using Kotlin and LiveData, I can offer you 2 ways - with and without extension fucntion:

    Without extension function

    liveData.value = liveData.value?.also { it ->
        // Modify your object here. Data will be auto-updated
        it.name = "Ed Khalturin"
        it.happyNumber = 42
    }
    

    Same, but with extension

    // Extension. CopyPaste it anywhere in your project
    fun <T> MutableLiveData<T>.mutation(actions: (MutableLiveData<T>) -> Unit) {
        actions(this)
        this.value = this.value
    }
    
    // Usage
    liveData.mutation {
        it.value?.name = "Ed Khalturin"
        it.value?.innerClass?.city= "Moscow" // it works with inner class too
    }
    
    0 讨论(0)
提交回复
热议问题