LiveData 双向数据绑定,无需公开 MutableLiveData

LiveData two-way databinding WITHOUT exposing MutableLiveData

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

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

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

我通常从我的 ViewModel 公开一个 LiveData getter 和一个单独的 setter,我试图通过使用 InverseMethod() 注释,但这不会真正起作用,因为数据绑定正在寻找 LiveData 本身的 getValue() 的 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);
        }
    }
}

这是如何绑定它

<EditText
   android:id="@+id/input_email"
   android:layout_width="match_parent"
   android:layout_height="wrap_content"
   android:text="@={viewmodel.email}"/>

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

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

我有完全相同的问题,我就是这样找到你的。 我知道这不完全是你要找的东西,但一个选择是从你的 ViewModel 观察 mEmail,并在其中实现你的 setEmail() 代码(当然在设置值本身之后......我不知道如何控制设置我猜你正在寻找的值)

val observer = Observer<String> { setEmail(it)}
fun setEmail(value:String){ //Your code }
init{
    mEmail.observeForever(observer)
}
//Don´t forget to remove the observer

我们被推荐从 ObservableField 切换到 LiveData 进行数据绑定,因为它是 lifecycle-aware。我们还被建议不要公开 MutableLiveData,因为视图模型应该控制分配。

这非常适合 one-way 数据绑定,在这些情况下我只会公开 LiveData。

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

我已经有一段时间忘记了这个问题,但作为一种解决方法,我稍微扩展了 MutableLiveData 并在每次需要控制 setter.[=14 时使用它来代替=]

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

现在使用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
});