Best Practice: Input Validation (Android) Best Practice: Input Validation (Android) android android

Best Practice: Input Validation (Android)


This java class implements a TextWatcher to "watch" your edit text, watching any changes done to the text:

public abstract class TextValidator implements TextWatcher {    private final TextView textView;    public TextValidator(TextView textView) {        this.textView = textView;    }    public abstract void validate(TextView textView, String text);    @Override    final public void afterTextChanged(Editable s) {        String text = textView.getText().toString();        validate(textView, text);    }    @Override    final public void     beforeTextChanged(CharSequence s, int start, int count, int after) {         /* Needs to be implemented, but we are not using it. */     }    @Override    final public void     onTextChanged(CharSequence s, int start, int before, int count) {          /* Needs to be implemented, but we are not using it. */        }}

And in your EditText, you can set that text watcher to its listener

editText.addTextChangedListener(new TextValidator(editText) {    @Override public void validate(TextView textView, String text) {       /* Insert your validation rules here */    }});


One approach (which I am using) is you should have a helper for validating inputs such as:

  1. Nullity (or emptiness)
  2. Dates
  3. Passwords
  4. Emails
  5. Numerical values
  6. and others

here's an exerpt from my ValidationHelper class:

public class InputValidatorHelper {    public boolean isValidEmail(String string){        final String EMAIL_PATTERN = "^[_A-Za-z0-9-\\+]+(\\.[_A-Za-z0-9-]+)*@[A-Za-z0-9-]+(\\.[A-Za-z0-9]+)*(\\.[A-Za-z]{2,})$";        Pattern pattern = Pattern.compile(EMAIL_PATTERN);        Matcher matcher = pattern.matcher(string);        return matcher.matches();    }    public boolean isValidPassword(String string, boolean allowSpecialChars){        String PATTERN;        if(allowSpecialChars){            //PATTERN = "((?=.*\\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[@#$%]).{6,20})";            PATTERN = "^[a-zA-Z@#$%]\\w{5,19}$";        }else{            //PATTERN = "((?=.*\\d)(?=.*[a-z])(?=.*[A-Z]).{6,20})";            PATTERN = "^[a-zA-Z]\\w{5,19}$";        }        Pattern pattern = Pattern.compile(PATTERN);        Matcher matcher = pattern.matcher(string);        return matcher.matches();    }    public boolean isNullOrEmpty(String string){        return TextUtils.isEmpty(string);    }    public boolean isNumeric(String string){        return TextUtils.isDigitsOnly(string);    }    //Add more validators here if necessary}

Now the way I use this class is this:

InputValidatorHelper inputValidatorHelper = new InputValidatorHelper();StringBuilder errMsg = new StringBuilder("Unable to save. Please fix the following errors and try again.\n");//Validate and Saveboolean allowSave = true;if (user.getEmail() == null && !inputValidatorHelper.isValidEmail(user_email)) {    errMsg.append("- Invalid email address.\n");    allowSave = false;}if (inputValidatorHelper.isNullOrEmpty(user_first_name)) {    errMsg.append("- First name should not be empty.\n");    allowSave = false;}if(allowSave){    //Proceed with your save logic here}

You can call your validation by using TextWatcher which is attached via EditText#addTextChangedListener

example:

txtName.addTextChangedListener(new TextWatcher() {    @Override    public void beforeTextChanged(CharSequence s, int start, int count, int after) {        //Do nothing    }    @Override    public void onTextChanged(CharSequence s, int start, int before, int count) {    }    @Override    public void afterTextChanged(Editable s) {        validate();    }});