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
});
我正在尝试在 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
});