Search code examples
androidvalidationandroid-databinding

Android Data Binding - EditText SetError


We are using the Kubwa validation library to validate the forms with android data binding library, but the error is not showing in the EditTexts.

ViewModel:

@Override
public void onLoginClick(View view) {

    validator.validateEmail(user.getEmail());
    validator.validatePassword(user.getPassword());

    notifyChange();

    if (validator.isValid()) connector.login(user);
}

@Bindable
@Override
public String getEmailError() {
    return validator.getEmailErrorMessage();
}

@Bindable
@Override
public String getPasswordError() {
    return validator.getPasswordErrorMessage();
}

And my xml:

<EditText
android:id="@+id/email"
android:text="@={viewModel.user.email}"
app:error="@{viewModel.emailError}"/> <!-- As the wiki said -->

When I realized that that doesn't work I created a BindingAdapter and changed the xml attribute, but it isn't working either.

@BindingAdapter("app:errorText")
public static void BindError(EditText view, String error){
    view.setError(error);
}

Any insight is greatly appreciated


Solution

  • I tried out something similar to your example and it worked fine.

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        User user = new User();
        ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
        binding.setUser(user);
    }
    

    and the Observable -- I put the validation in the setName, but yours should be the same. You may prefer to have it in the InverseBindingAdapter instead:

    public class User extends BaseObservable {
        private String name;
    
        @Bindable
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
            notifyPropertyChanged(com.example.gmount.tryoutdamnthing.BR.name);
            notifyPropertyChanged(com.example.gmount.tryoutdamnthing.BR.error);
        }
    
        @Bindable
        public String getError() {
            if (name == null || name.length() < 3) {
                return "Too short!";
            } else {
                return null;
            }
        }
    }
    

    and the layout:

    <layout>
        <data>
            <variable name="user" type="com.example.gmount.tryoutdamnthing.User"/>
        </data>
    
        <FrameLayout
                xmlns:android="http://schemas.android.com/apk/res/android"
                xmlns:app="http://schemas.android.com/apk/res-auto"
                android:id="@+id/activity_main"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
        >
    
            <EditText
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="@={user.name}"
                    app:error="@{user.error}"
            />
    
        </FrameLayout>
    </layout>
    

    My guess is that you didn't use the DataBindingUtil.setContentView() or forgot to set the ViewModel object in the binding. Sometimes this happens because people use Activity.setContentView() after DataBindingUtil.setContentView().

    You can see here that I don't need a BindingAdapter for app:error because the setter's name is the same as the attribute.