如何让数据绑定表达式对同一布局中 EditText 的更改做出反应?

How Can I Have a Data Binding Expression React To Changes In an EditText In the Same Layout?

假设我有一个 ID 为 fooEditText。在同一布局资源的其他地方,我有一个 Button,我只想在 EditText.

中有文本时启用 Button

我认为这行得通:

        <Button
            android:id="@+id/bar"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:onClick="@{listener}"
            android:text="Um, hi!"
            android:enabled="@{foo.text.length > 0}" />

生成的绑定 class 确实根据 foo 中文本的长度在 bar 上调用了 setEnabled()... 但仅当 executeBindings() 叫做。这似乎不会在用户键入时被调用,因此我的 Button 的启用状态不会根据用户在 EditText.

中输入文本而改变

我可以解决这个问题,但感觉应该可行。有什么我想念的吗?这是已知的限制吗?

更新

只需将 foo.text.length 更改为 foo.text.length()


旧解决方案

有些要点,也许你错过了。

1。使用 BaseObservable

如果你的 model extends BaseObservable 那么你需要 food.text @Bindable.

public class UserBaseObservable extends BaseObservable {
    private String name;

    @Bindable
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
        notifyPropertyChanged(BR.name);
    }
}

原因 是,当您使用 text.length 时,您需要在更改时调用所有依赖元素。因为 text.length 是不可观察的,所以你需要手动调用它。生成的绑定 class 调用 setName(value) 但它不会更改 text.length 依赖视图。

2。使用 MutableLiveData

如果您使用 MutableLiveData 则必须提供 LifeCycleOwner

    binding.setLifecycleOwner(this);

3。使用 ObservableField(最短方法)

另一种简单的方法是将 ObservableField 用于 foo.text

我说简单因为在这种情况下你不需要制作这个字段Bindable

public class UserObservableField {
    private ObservableField<String> name = new ObservableField<>();

    public ObservableField<String> getName() {
        return name;
    }

    public void setName(ObservableField<String> name) {
        this.name = name;
    }
}

我用其中的 3 个解决方案创建了一个示例,所有解决方案都有效!

activity_login.xml

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    >

    <data>

        <variable
            name="userObservableField"
            type="com.innovanathinklabs.sample.activities.LoginActivity.UserObservableField"/>

        <variable
            name="userMutable"
            type="com.innovanathinklabs.sample.activities.LoginActivity.UserMutable"/>

        <variable
            name="userBaseObservable"
            type="com.innovanathinklabs.sample.activities.LoginActivity.UserBaseObservable"/>

    </data>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:padding="20dp">

        <EditText
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:hint="Enter your name"
            android:text="@={userObservableField.name}"/>

        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:enabled="@{userObservableField.name.length()>0}"
            android:text="Proceed"/>

        <EditText
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:hint="Enter your name"
            android:text="@={userMutable.name}"
            />

        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:enabled="@{userMutable.name.length()>0}"
            android:text="Proceed"/>

        <EditText
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:hint="Enter your name"
            android:text="@={userBaseObservable.name}"/>

        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:enabled="@{userBaseObservable.name.length()>0}"
            android:text="Proceed"/>

    </LinearLayout>
</layout>

LoginActivity.java

public class LoginActivity extends AppCompatActivity {
    ActivityLoginBinding binding;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        binding = DataBindingUtil.setContentView(this, R.layout.activity_login);
        binding.setLifecycleOwner(this); // necessary for LiveData to work
        binding.setUserObservableField(new UserObservableField());
        binding.setUserBaseObservable(new UserBaseObservable());
        binding.setUserMutable(new UserMutable());
    }

    public static class UserObservableField {
        private ObservableField<String> name = new ObservableField<>();

        public ObservableField<String> getName() {
            return name;
        }

        public void setName(ObservableField<String> name) {
            this.name = name;
        }
    }


    public static class UserBaseObservable extends BaseObservable {
        private String name;

        // necessary for updating button
        @Bindable
        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
            notifyPropertyChanged(BR.name);
        }
    }


    public static class UserMutable {
        private MutableLiveData<String> name = new MutableLiveData<>();

        public MutableLiveData<String> getName() {
            return name;
        }

        public void setName(MutableLiveData<String> name) {
            this.name = name;
        }
    }
}

我把所有模型都放在里面 Activity。我尽量解释清楚了,还有不明白的地方可以评论

输出

我想我还可以在这里添加一些东西,我个人不喜欢公开可变的实时数据。因此,您可以使用 android:afterTextChanged 属性并执行类似 android:afterTextChanged="@{(text) -> viewmodel.onSomethingChanged(text)}" 的操作。这样你就不会公开你的 MutableLiveData 对象。