LiveData双向数据绑定,不公开MutableLiveData

Su-*_*ang 6 android android-databinding android-livedata android-architecture-components

我正在尝试在EditText上使用双向数据绑定,如果我将该字段公开为MutableLiveData,则效果很好,因为通常在网上找到的示例中都可以看到它。

但是,有充分的理由不公开MutableLiveData,并且这些理由并不是神奇的无效,因为我决定使用数据绑定库。

编辑:这里的主要动机是MyViewModel应该保持对设置数据的控制(这是为什么不建议MutableLiveData直接公开它的原因),在设置方法中,我可以执行任何必要的检查或转换,然后仅调用setValueLiveData。

我通常会LiveData从ViewModel中公开一个getter和一个单独的setter,我试图通过使用InverseMethod()批注来实现双向数据绑定,但这并不能真正起作用,因为数据绑定正在寻找getValue()LiveData本身的InverseMethod。

这是一个简单的示例:

public class MyViewModel extends ViewModel {

    private MutableLiveData<String> mEmail = new MutableLiveData<>();

    // @InverseMethod("setEmail")    ### THIS DOESN'T WORK
    public LiveData<String> getEmail() {
        return mEmail;
    }

    // ### I WANT DATA-BINDING TO USE THIS METHOD
    public void setEmail(String email) {
        if (mEmail.getValue() != email) {
            mEmail.setValue(email);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

这就是想要绑定它的方式

<EditText
   android:id="@+id/input_email"
   android:layout_width="match_parent"
   android:layout_height="wrap_content"
   android:text="@={viewmodel.email}"/>
Run Code Online (Sandbox Code Playgroud)

到目前为止,唯一可行的解​​决方法是使用单向数据绑定在EditText上设置文本,然后附加TextWatcher并从此处调用我的ViewModel.setter。

编辑:
第二种解决方法是扩展MutableLiveData,然后在重写中进行检查和转换setValue...这是要编写的很多样板文件。

Nic*_*ell 8

我们被建议从 ObservableField 切换到 LiveData 进行数据绑定,因为它是生命周期感知的。我们还被建议不要公开 MutableLiveData,因为视图模型应该控制分配。

这对于单向数据绑定非常有效,在这些情况下,我只会公开 LiveData。

我们想要使用双向数据绑定,根据定义将赋值从视图模型移动到 UI,所以我认为在这种情况下公开 MutableLiveData 是正确的。我这么说是因为我们是故意这样做的,因为我们希望我们的 UI 能够分配值,以便我们有更清晰的视图。


Su-*_*ang 3

我已经忘记这个问题有一段时间了,但作为一种解决方法,我MutableLiveData稍微扩展了一下,并在每次需要控制设置器时使用它。

import androidx.core.util.Consumer;
import androidx.lifecycle.MutableLiveData;

public class DelegatedMutableLiveData<T> extends MutableLiveData<T> {
    private Delegate<T> mDelegate;

    public interface Delegate<T> {
        void onSet(T value, Consumer<T> setter);
    }

    public DelegatedMutableLiveData(Delegate<T> delegate) {
        mDelegate = delegate;
    }

    public DelegatedMutableLiveData(T value, Delegate<T> delegate) {
        super(value);
        mDelegate = delegate;
    }

    @Override
    public void setValue(T value) {
        if (mDelegate != null) {
            mDelegate.onSet(value, super::setValue);
        } else {
            super.setValue(value);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

现在使用DelegatedMutableLiveData如下:

private final DelegatedMutableLiveData<Integer> mMyLiveData = new DelegatedMutableLiveData<>((value, setter) -> {
    // do your checks and change value if necessary
    setter.accept(value); // or don't accept if you don't want to change the current value
});
Run Code Online (Sandbox Code Playgroud)