73

我正在使用带有 LiveData 的 Android MVVM 架构。我有一个像这样的对象

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 中使用诸如字符串之类的主要值对我来说很重要。

4

7 回答 7

63

我认为android没有为此推荐任何最佳实践。我建议您使用使用更清洁和更少样板代码的方法。

如果您正在使用 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 观察者,而无需对现有代码进行少量更改。希望能帮助到你

于 2018-01-10T18:40:16.103 回答
26

使用 MVVM 和 LiveData 时,您可以将对象重新绑定到布局,以便触发 UI 上的所有更改。

给定“用户”是MutableLiveData<User>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 = it //<- re-binding user
})

您的布局文件不应更改:

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

...

<TextView
        android:id="@+id/firstname"
        android:text="@{user.firstname}"
        />
于 2018-09-12T04:07:40.320 回答
22

如果您使用 Kotlin 和 LiveData,我可以为您提供 2 种方法 - 有和没有扩展功能:

无扩展功能

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

相同,但有扩展名

// 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
}
于 2020-01-29T12:33:11.333 回答
4

来自reddit - @cedrickc 的回答

向 MutableLiveData 添加扩展函数:

fun <T> MutableLiveData<T>.modifyValue(transform: T.() -> T) {
   this.value = this.value?.run(transform)
}
于 2021-01-17T14:26:47.523 回答
3

我如何确保在用户对象更改中的某些提交时观察者得到通知?顺便说一句,将这些数据保存在单独的对象中并且不要在我的 ViewModel 中使用诸如字符串之类的主要值对我来说很重要。

您可以使用 androidx.lifecyle.Transformation 类来监控各个字段。

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

您照常更新用户,并监听名字/姓氏以监控这些字段的变化。

于 2020-07-02T04:15:11.107 回答
0

2021 年简单易行的解决方案(也适用于StateFlow):

这里的关键点是,.value必须更改为不同的内容(哈希码),以便绑定 ui 将得到更新。

//Your model
data class Student(val name: String)

//Your ViewModel class
fun onStudentClick() {
    val newName = "Kate"    //change field/property

    _student.value = _student.value.copy(name = newName)    //must be different content (hash code)!!
}

而已。

PS:是的,我知道它copy()会返回一个不同的对象(引用),但是内容是一样的,它在这里不起作用。您必须更改内容。

于 2021-10-02T16:56:30.200 回答
0

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
});
于 2018-01-10T15:48:26.760 回答