Обновление LiveData при изменении поля объекта

Я использую архитектуру Android MVVM с LiveData. У меня есть такой объект

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

    public String getFirstName() {
        return firstName;
    }

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

    public String getLastName() {
        return lastName;
    }

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

И моя модель выглядит так:

public class InfoViewModel extends AndroidViewModel {
    MutableLiveData<User> user = new MutableLiveData<>();

    public InfoViewModel(@NonNull Application application) {
        super(application);
        User user = new User();
        user.setFirstName("Alireza");
        user.setLastName("Ahmadi");

        this.user.setValue(user);
    }

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

    public void change(){
        user.getValue().setFirstName(user.getValue().getFirstName() + " A ");
    }
}

Как я могу убедиться, что некоторые зарегистрированные в пользовательских объектах изменения наблюдателей получают уведомление? Кстати, мне важно хранить эти данные в отдельном объекте и не использовать первичные значения, такие как строки в моей модели ViewModel.

Ответ 1

Я не думаю, что есть какая-то лучшая практика, рекомендованная андроидом для этого. Я предлагаю вам использовать подход, который использует более чистый и менее шаблонный код.

Если вы используете привязку данных к файлам android вместе с LiveData, вы можете использовать следующий подход:

Ваш объект POJO будет выглядеть примерно так.

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);
    }
}

Итак, у вас уже будет класс, который уведомляет о каждом изменении его свойства. Таким образом, вы можете просто использовать этот обратный вызов изменения свойств в своей MutableLiveData, чтобы уведомить своего наблюдателя. Вы можете создать пользовательскую MutableLiveData для этого

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());

        }
    };


}

Затем все, что вам нужно сделать, это использовать эту CustomMutableLiveData вместо MutableLiveData в вашей модели просмотра

public class InfoViewModel extends AndroidViewModel {

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

Таким образом, делая это, вы можете уведомить обозревателя просмотра и LiveData с небольшим изменением существующего кода. Надеюсь, что это поможет.

Ответ 2

При использовании MVVM и LiveData вы можете повторно привязать объект к макету, чтобы он вызвал все изменения в пользовательском интерфейсе.

Данный "пользователь" является MutableLiveData<User> в 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()
    }
}

Файл активности/фрагмента:

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 = viewModel.user //<- re-binding
})

Ваш файл макета не должен меняться:

<data>
    <variable
        name="user"
        type="com.project.model.User" />
</data>

...

<TextView
        android:id="@+id/firstname"
        android:text="@{user.firstname}"
        />

Ответ 3

Чтобы ваш наблюдатель получил уведомление, вы должны использовать setValue, если вы это сделаете user.getValue().setFirstName(user.getValue().getFirstName() + " A "); ваш наблюдатель не будет уведомлен!

Просмотр модели

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

Активность/фрагмент

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

Где-то в вашей деятельности/фрагменте

Это вызовет наблюдателя:

mModel.getUser().setValue(user);

Если вы хотите обновить только одно поле от объекта вместо обновления всего объекта, у вас должны быть кратные 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
});

Ответ 4

Я отправил тот же вопрос, и я получил самое простое решение. Найдите мой ответ здесь