Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android: Data Binding Text not Updating

I want the user to enter their name in an EditText and display Hello {name} inside a TextView. When I set the text of the TextView to be the observable property (user.firstName), the text updates. Even if I set the text to the toString() method of the model, it still works fine. However, if I instead use viewModel.hello, the text doesn't update. What am I missing?

Model:

public class User extends BaseObservable {
    public String firstName;
    public String lastName;

    public User(String firstName, String lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    }

    @Bindable
    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
        notifyPropertyChanged(BR.firstName);
    }

    @Bindable
    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
        notifyPropertyChanged(BR.lastName);
    }

    @Override
    public String toString() {
        return getFirstName() + " " + getLastName();
    }
}

ViewModel:

public class ViewModel {
    public  User user;

    public ViewModel() {
        user = new User("", "");
    }

    public String hello() {
        return "Hello " + user.toString();
    }

    public TextWatcher userNameTextWatcher() {
        return new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {

            }

            @Override
            public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {

            }

            @Override
            public void afterTextChanged(Editable editable) {
                if (editable.toString().length() == 0) {
                    user.setFirstName("");
                    user.setLastName("");
                    user.notifyChange();
                    return;
                }
                String[] name = editable.toString().split(" ");
                if (name.length == 1) {
                    user.setFirstName(name[0]);
                    user.setLastName("");
                    user.notifyChange();
                    return;
                }
                String firstName = name[0] + " ";
                for (int i = 0; i < name.length - 2; i++) {
                    firstName = firstName + " " + name[i] + " ";
                }
                user.setFirstName(firstName);
                user.setLastName(name[name.length - 1]);
                user.notifyChange();
            }
        };
    }
}

Layout:

<data>
    <variable
        name="viewModel"
        type=".ViewModel"/>
</data>

<TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        tools:text="@{viewModel.hello}"/>

    <EditText
        android:id="@+id/editText"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@+id/textView"
        android:layout_marginTop="8dp"
        app:addEditTextWatcher="@{viewModel.userNameTextWatcher}"/>
like image 568
SunnySydeUp Avatar asked Jun 27 '16 04:06

SunnySydeUp


1 Answers

One problem is that your ViewModel is not observable, so changes to hello() won't be reflected in the model. Since it is a single field that is observable, you can also use an ObservableField for the hello string, but I'll demonstrate it with an Observable ViewModel.

public class ViewModel extends BaseObservable {
    public final User user;

    public ViewModel() {
        user = new User("", "");
        user.addOnPropertyChangedCallback(new OnPropertyChangedCallback {
            @Override
            public void onPropertyChanged(Observable sender, int property) {
                if (property == BR.firstName || property == BR.lastName) {
                    notifyPropertyChanged(ViewModel.this, BR.hello);
                }
            }
        });
    }

    @Bindable
    public String hello() {
        return "Hello " + user.toString();
    }

    public TextWatcher userNameTextWatcher() {
        return new TextWatcher() { ... }
    };
}

}

But I think it would be much easier to listen to changes to User directly in the XML and not worry about making ViewModel Observable:

 <data>
     <variable
         name="viewModel"
         type=".ViewModel"/>
 </data>

 <TextView
    android:id="@+id/textView"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    tools:text="@{@string/hello(viewModel.user.firstName, viewModel.user.lastName)}"/>

<EditText
    android:id="@+id/editText"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_below="@+id/textView"
    android:layout_marginTop="8dp"
    app:addEditTextWatcher="@{viewModel.userNameTextWatcher}"/>

And you'd have a string resource:

<string name="hello">Hello %1$s %2$s</string>
like image 55
George Mount Avatar answered Sep 27 '22 18:09

George Mount