Android LiveData双向数据绑定,无需公开可变LiveData

Android LiveData双向数据绑定,无需公开可变LiveData,android,android-databinding,android-architecture-components,android-livedata,Android,Android Databinding,Android Architecture Components,Android Livedata,我正在尝试在EditText上使用双向数据绑定,如果我像我在网上找到的示例中通常看到的那样,将字段公开为可变的LiveData,则效果很好 然而,有很好的理由不公开MutableLiveData,而且这些理由并不是神奇地无效,因为我决定使用数据绑定库 编辑:这里的主要动机是MyViewModel应该保持对设置数据的控制(这就是为什么不建议直接公开MutableLiveData),在setter中,我可以执行任何必要的检查或转换,然后只需对LiveData调用setValue。 我通常从我的Vie

我正在尝试在EditText上使用双向数据绑定,如果我像我在网上找到的示例中通常看到的那样,将字段公开为可变的LiveData,则效果很好

然而,有很好的理由不公开MutableLiveData,而且这些理由并不是神奇地无效,因为我决定使用数据绑定库

编辑:这里的主要动机是MyViewModel应该保持对设置数据的控制(这就是为什么不建议直接公开
MutableLiveData
),在setter中,我可以执行任何必要的检查或转换,然后只需对LiveData调用
setValue
。 我通常从我的ViewModel中公开一个
LiveData
getter和一个单独的setter,我试图通过使用
InverseMethod()
注释来实现双向数据绑定,但这并不能真正起作用,因为数据绑定正在寻找一种与LiveData本身的
getValue()
相反的方法

下面是一个简单的例子:

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);
        }
    }
}
公共类MyViewModel扩展了ViewModel{
私有MutableLiveData mEmail=新的MutableLiveData();
//@InverseMethod(“setEmail”)####这不起作用
公共LiveData getEmail(){
返回mEmail;
}
//####我希望数据绑定使用此方法
公用电子邮件(字符串电子邮件){
if(mEmail.getValue()!=电子邮件){
mEmail.setValue(电子邮件);
}
}
}
这就是我想把它绑起来的方式

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

到目前为止,唯一有效的解决方法是使用单向数据绑定在EditText上设置文本,然后附加TextWatcher并从那里调用my ViewModel.setter

编辑:

第二种解决方法是扩展MutableLiveData,然后在重写的
setValue
中执行检查和转换。。。要写的样板太多了。

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

val observer=observer{setEmail(it)}
fun setEmail(值:String){//Your code}
初始化{
成员观察者(观察者)
}
//不要忘记移除观察者

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

这非常适合单向数据绑定,在这种情况下,我只公开LiveData


我们希望使用双向数据绑定,根据定义,双向数据绑定将分配从视图模型移动到UI,因此我认为在本例中公开MutableLiveData是正确的。我这样说是因为我们有意这样做,因为我们希望我们的UI能够分配值,这样我们就有了更清晰的视图。

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

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

我也有同样的问题。这正是我想做的,但我不知道。解决方法的开销太大,所以在进行双向数据绑定时,我将公开MutabeLiveData。如果有更好的解决方案就好了。错误消息说:
方法getValue没有反向,您必须向该方法添加@InverseMethod注释,以指示在双向绑定表达式中使用该方法时应使用哪个方法
感谢Gio的反馈,此方法的问题是,所有其他观察者将至少暂时看到无效值。问题仍然是,您将失去对setter的控制。举个例子,你的UI有一个EditText,你想限制什么样的文本是可以接受的。现在你需要另一个TextWatcher来做这件事。
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);
        }
    }
}
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
});