cur*_*zen 10 data-binding android
我知道Android的数据绑定库的目的是观察数据并在数据发生变化时自动更新.
问题:数据是否可以观察其他数据?例如,我可以有一个ObservableField"依赖"或"绑定"另一个或一组其他ObservableFields的值吗?目前,我已经手动实现了这一点 - 每当任何"dependee" ObservableField发生变化时,我都会计算相关字段并更新其值.
我的用例是我希望所有"逻辑"都在View之外 - 所以我想把我所有的逻辑放在"数据"类中(ViewModel如果可能的话).我有一个按钮,其状态我想设置为启用/禁用,具体取决于其他几个字段的内容.这个例子说明了我的想法.
我的布局文件看起来像这样
<layout>
<data>
<variable name="register" class="com.example.RegisterViewModel"/>
</data>
<LinearLayout>
<EditText
android:id="@+id/edUsername"
android:text="@{register.username}"/>
<EditText android:id="@+id/edPassword" />
<EditText android:id="@+id/edConfirm" />
<Button android:text="Register" android:enabled="@{register.isValid}" />
</LinearLayout>
</layout>
Run Code Online (Sandbox Code Playgroud)
而且,我的View代码如下:
class RegisterView extends View {
@Override
protected void onFinishInflate() {
RegisterViewBinding binding = DataBindingUtil.bind(this);
RegisterViewModel register = new RegisterViewModel();
binding.setRegister(register);
binding.edPassword.setOnFocusChangeListener(new OnFocusChangeListener(){
@Override public void onFocusChange(View v, boolean hasFocus){
register.updateUsername(edPassword.getText().toString());
}
});
//Similarly for other fields.
}
}
Run Code Online (Sandbox Code Playgroud)
这是我的ViewModel
class RegisterViewModel {
public final ObservableField<String> username = new ObservableField<>();
private final ObservableField<String> password = new ObservableField<>();
private final ObservableField<String> confirmPassword = new ObservableField<>();
public final ObservableBoolean isValid;
//Dependee Observables - isValid depends on all of these
private final ObservableBoolean isUsernameValid = new ObservableBoolean();
private final ObservableBoolean isPasswordValid = new ObservableBoolean();
private final ObservableBoolean arePasswordsSame = new ObservableBoolean();
public RegisterViewModel(){
//Can this binding be made observable so that isValid automatically
//updates whenever isUsernameValid/isPasswordValid/arePasswordsSame change?
isValid = new ObservableBoolean(isUsernameValid.get() &&
isPasswordValid.get() &&
arePasswordsSame.get());
}
public void updateUsername(String name) {
username.set(name);
isUsernameValid.set(ValidationUtils.validateUsername(name));
updateDependents();
}
public void updatePassword(String pwd) {
password.set(pwd);
isPasswordValid.set(ValidationUtils.validatePassword(pwd));
updateDependents();
}
public void updateConfirmPassword(String cnf) {
confirmPassword.set(cnf);
arePasswordsSame.set(password.get().equalsIgnoreCase(cnf.get()));
updateDependents();
}
//Looking to avoid this altogether
private void updateDependents() {
isValid.set(isUsernameValid.get() &&
isPasswordValid.get() &&
arePasswordsSame.get());
}
}
Run Code Online (Sandbox Code Playgroud)
Geo*_*unt 17
ObservableField在Android数据绑定中使用绑定语法对数据进行绑定是不可能的.但是,您可以使用代码绑定它们:
class RegisterViewModel {
public final ObservableField<String> username = new ObservableField<>();
public final ObservableField<String> password = new ObservableField<>();
public final ObservableField<String> confirmPassword = new ObservableField<>();
public final ObservableBoolean isValid = new ObservableBoolean();
private boolean isUsernameValid;
private boolean isPasswordValid;
private boolean arePasswordsSame;
public RegisterViewModel() {
// You can use 3 different callbacks, but I'll use just one here
// with 'if' statements -- it will save allocating 2 Object.
OnPropertyChangedCallback callback = new OnPropertyChangedCallback() {
@Override
public void onPropertyChanged(Observable sender, int propertyId) {
if (sender == username) {
isUsernameValid = ValidationUtils.validateUsername(name);
} else if (sender == password) {
isPasswordValid = ValidationUtils.validatePassword(pwd);
} else if (sender == confirmPassword) {
arePasswordsSame = password.get()
.equalsIgnoreCase(confirmPassword.get());
} else {
// shouldn't get here...
}
isValid.set(isUsernameValid && isPasswordValid && arePasswordsSame);
}
};
username.addOnPropertyChangedCallback(callback);
password.addOnPropertyChangedCallback(callback);
confirmPassword.addOnPropertyChangedCallback(callback);
}
}
Run Code Online (Sandbox Code Playgroud)
在这里,我假设空用户名,密码和confirmPassword无效.看似安全的假设.
我没有看到私人ObservableField的巨大需求.ObservableField被设计为由UI绑定,如果不能,您可以使用其他数据类型.如果您发现它们对使用上述回调的内部绑定很有用,那就去吧.
我建议重新考虑你的方法。数据绑定的主要好处之一是允许更具表现力的视图代码(在本例中为 XML)。虽然您在 XML 和视图模型中实际想要做的工作量之间存在平衡,但您的案例是在视图模型中完成的工作过多的完美示例。在您的代码中,依赖于其他字段的不是可观察字段,而是依赖于其他视图数据的视图数据。observable 字段只是该数据的表示,如果可能,您应该在视图层而不是数据层中创建依赖项。
我建议的方法是从视图层(XML)开始,并假设您没有整体视图模型,而只有附加到视图的数据。例如,您可以从以下内容开始:
<layout>
<LinearLayout>
<EditText android:text="@{username}"/>
<EditText text="@{password}" />
<EditText text="@{confirmPassword}" />
<Button android:text="Register" android:enabled="@{password.equals(confirmPassword) && ...password validation...}" />
</LinearLayout>
</layout>
Run Code Online (Sandbox Code Playgroud)
在这第一步之后,你会很快意识到密码验证逻辑在这里没有意义,因为它不是微不足道的,所以你会去:
<layout>
<import "com.example.ValidationUtils"/>
<LinearLayout>
<EditText android:text="@{username}"/>
<EditText text="@{password}" />
<EditText text="@{confirmPassword}" />
<Button android:text="Register" android:enabled="@{password.equals(confirmPassword) && ValidationUtils.validatePassword(password)}" />
</LinearLayout>
Run Code Online (Sandbox Code Playgroud)
此时,您只需要一个包含用户名、密码和确认密码字段的容器,以便您可以传递它们,因此您只需添加 viewModel 变量。
<layout>
<import "com.example.ValidationUtils"/>
<variable name="viewModel" type="com.example.Register"/>
<LinearLayout>
<EditText android:text="@{viewModel.username}"/>
<EditText text="@{viewModel.password}" />
<EditText text="@{viewModel.confirmPassword}" />
<Button android:text="Register" android:enabled="@{password.equals(confirmPassword) && ValidationUtils.validatePassword(password)}" />
</LinearLayout>
Run Code Online (Sandbox Code Playgroud)
请注意它有多好,您甚至根本不需要查看 Java 代码。
PS:ValidationUtils.validateEntries(username, password, confirmPassword)如果您愿意,您也可以将启用的表达式替换为类似的内容。这更像是一种风格选择;它不会对视图代码的表现力产生太大影响,任何阅读 XML 的人都可以在不查看多个位置的情况下找出视图试图实现的目标。
| 归档时间: |
|
| 查看次数: |
5015 次 |
| 最近记录: |