Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

Suppose that I have an EditText with an ID of foo. Elsewhere in the same layout resource, I have a Button, and I want to enable the Button only if there is text in the EditText.

I thought that this would work:

        <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}" />

The generated binding class does indeed call setEnabled() on bar based on the length of the text in foo... but only when executeBindings() is called. That does not seem to be called as the user types, so the enabled state of my Button does not change based on the user entering text in the EditText.

I can work around this, but it feels like this should be working. Is there something that I'm missing? Is this a known limitation?

like image 988
CommonsWare Avatar asked Sep 24 '18 21:09

CommonsWare


1 Answers

Update

Just change foo.text.length to foo.text.length().


Old Solution

Some points, perhaps which you missed.

1. Using BaseObservable

If your model extends BaseObservable then you need to make 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);
    }
}

Reason being, when you use text.length then you need to invoke all dependent elements when it is changed. Because text.length is not observable, so you need to manually invoke it. Generated binding class calls setName(value) but it does not change text.length dependent views.

2. Using MutableLiveData

If you use MutableLiveData then it is nessesary to provide LifeCycleOwner

    binding.setLifecycleOwner(this);

3. Using ObservableField (Shortest approach)

Another easy approach is to use ObservableField for foo.text

I said easy because you don't need to make this field Bindable in this case.

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

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

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

I created a sample with 3 of these solutions, all are working!

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

I placed all models inside Activity. I tried to explain well, still you can make comment if you have any confusion.

Output

output image

like image 109
Khemraj Sharma Avatar answered Sep 29 '22 03:09

Khemraj Sharma