Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Mobile / Android

Model Validation Support in Android Binding

4.25/5 (6 votes)
14 Jan 2011GPL33 min read 49.6K  
Introduction to View Model Validation support in Android Binding

Introduction

Android Binding also supports Model Validation through @Annotation syntax. You may also add custom validation rules in a few easy steps. The following article is based on the sample application on the market.

or search:

  • Android Binding in market

Please download the sample app and get a feel of how it works. The source code is also available. Check the source code page -> Demos folder for it.

This is how the sample looks like:

Image 1

The above shows the validation result, with custom error messages. This article will explain about this.

About Android Binding

Image 2

Android Binding is a framework that supports declarative view model binding. You may read this for more information.

Initial View Model

Let's start with a simple Registration view model:

Java
public class RegistrationViewModel{ 
    public final Observable<CharSequence> Login;
    public final Observable<CharSequence> Password;
    public final Observable<CharSequence> ConfirmPassword;
    public final Comamnd Submit;
} 

Remember, in Android Binding, it is expected the Observables are public fields, it might be a good practice to decorate it as final so no latter remapping will occur.

Add Validation Rules

OK, the login and password are obviously mandatory. It is called *Required* in Android Binding. All the validation classes are sitting in the package:

Java
com.gueei.android.binding.validation.validators; 

Remember, if you found the validators are not enough, you can always make a custom one, which we will cover later in the post.

So, here's how we instruct the Android Binding that those fields are Required:

Java
public class RegistrationViewModel{
    @Required
    public final Observable<CharSequence> Login;
    @Required
    public final Observable<CharSequence> Password;
    @Required
    public final Observable<CharSequence> ConfirmPassword;
} 

That's it! We just need to put an *@Required* annotation attribute to the required field.

Validate

Validation is done by calling the static method:

Java
ModelValidator.validate(model); 

You might want to validate whenever the user 'submits' the form. The above line of code will return an object of class: *ValidationResult* which contains whether the validation is success, if not, the error messages associated with it. If you don't like (yes, you shouldn't) the predefined messages, you may put in your own:

Java
...
@Required(ErrorMessage="You must put the login name!")
public final Observable<CharSequence> Login;
... 

More Validation Rules

The Confirm Password is something you want to make sure your user didn't mistype a password. It must be the same as the Password field. Here's how we do it:

Java
@Required
@EqualsTo(Observable="Password")
public final Observable<CharSequence> ConfirmPassword;

Simple! We tell the Framework that confirm password must be equalsto another observable, that is, the Password field. One more thing, the attributes are 'stackable', that means, one Observable property can have multiple validation rules applied on it. Just like the above example, that means:

The ConfirmPassword is required and it must equals to Password

Ok, we are done with password, how about the user name? We only want to accept alphabets, numbers and, underscore; and it must be at least 3 characters to most 8 characters. Simplest way to check everything like this, is using Regular Expression.

Java
@Required(ErrorMessage="You must put the login name!")
@RegexMatch(Pattern="^[A-Za-z0-9_]{3,8}$")
public final Observable<CharSequence> Login;

Custom Validation

Since Java does not support Inheritance on @interface, working with custom validation is a little bit tricky. To simplify the explanation, I'd start with the implementation of @Required annotation:

Java
@Retention(RetentionPolicy.RUNTIME) 
public @interface Required{
    public Class<?> Validator() default RequiredValidator.class;
    public String ErrorMessage() default "%fieldname% is a required field"; 
    public class RequiredValidator extends ValidatorBase<Required> { 
        @Override
        public Class getAcceptedAnnotation() {
            return Required.class;
        }
        @Override
        protected String doFormatErrorMessage(Required parameters,
                String fieldName) {
            return parameters.ErrorMessage().replace("%fieldname%", fieldName);
        }
        @Override
        protected boolean doValidate(Object value, Required parameters,
                Object model) {
            if (value==null) return false;
            if (Boolean.FALSE.equals(value)) return false;
            if (value instanceof CharSequence){
               if (((CharSequence) value).length() == 0) return false;
            }
            return true;
        }
    } 
} 

We MUST declare the interface's retention as @Retention(RetentionPolicy.RUNTIME)or else the attribute will not be available in Runtime.

The field public Class<?> Validator() is a must, the ModelValidator only works with annotation with this field. It needs to return a class, by default, and should not change, that will be the Validator class.

Everything else in the interface is optional, they will then passed to the validator as "parameters" (or options).

Java
public class RequiredValidator extends ValidatorBase<Required>

declares the validator, which must be a subclass of ValidatorBase<?>, where the <?> you should put the @interface you are accepting.

Three methods need to be implemented:

  • getAcceptedAnnotation() returns the type of @interface this Validator is accepting.
  • doFormatErrorMessage() returns the formatted error message, alternative error message is suggested to define in respective @interface, although it is not a must.
  • doValidate() actually validates the input value. Here, notice the first parameter *Object value* is the value, but not the observable object. The whole model is also passed in as parameter.

About the Author and Project

History

  • 14th January, 2011: Initial post

License

This article, along with any associated source code and files, is licensed under The GNU General Public License (GPLv3)