是否可以将一个ObservableField绑定到另一个?

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绑定,如果不能,您可以使用其他数据类型.如果您发现它们对使用上述回调的内部绑定很有用,那就去吧.


Saa*_*ooq 5

我建议重新考虑你的方法。数据绑定的主要好处之一是允许更具表现力的视图代码(在本例中为 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 的人都可以在不查看多个位置的情况下找出视图试图实现的目标。