How to take user experience to the next level by writing a validate as you go TextWatcher for date entry fields.
Android’s EditText and companion class TextWatcher have been around since API 1.0. Both classes are quite powerful, and yet I am surprised there aren’t more standard out-of-the-box validators. For example, a common occurrence is using a TextWatcher to validate a date while the user enters it. There is an android:inputType “date” attribute, but this only influences which keyboard is displayed.
The example project below shows how I typically validate user input using a TextWatcher. It requires a date in the format MM/YYYY and will auto enter the forward slash at the appropriate time. You can follow along or download and import the entire project directly into Eclipse.
1. Create a new Android project targeting Android 2.3 or better.
2. In the /res/layout folder I have added a single EditText widget to a relative layout. Setting the input type attribute will restrict the keys available on the soft keyboard presented when the field receives focus.
activity_main.xml
3. In the /src/MainActivity.java file I implemented the custom TextWatcher. The majority of the code is focused on chunking up the date into a year and a month and validating the individual parts. It’s important to reset the displayed error messaging by calling setError(null) any time the watcher gets invoked and no error condition is hit.
MainActivity.java
package com.authorwjf.datevalidatingedittext;
import java.util.Calendar;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
import android.widget.EditText;
import android.app.Activity;
public class MainActivity extends Activity {
private EditText mDateEntryField;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mDateEntryField = (EditText) findViewById(R.id.date_entry_field);
mDateEntryField.addTextChangedListener(mDateEntryWatcher);
}
private TextWatcher mDateEntryWatcher = new TextWatcher() {
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
String working = s.toString();
boolean isValid = true;
if (working.length()==2 && before ==0) {
if (Integer.parseInt(working) < 1 || Integer.parseInt(working)>12) {
isValid = false;
} else {
working+=”/”;
mDateEntryField.setText(working);
mDateEntryField.setSelection(working.length());
}
}
else if (working.length()==7 && before ==0) {
String enteredYear = working.substring(3);
int currentYear = Calendar.getInstance().get(Calendar.YEAR);
if (Integer.parseInt(enteredYear) < currentYear) {
isValid = false;
}
} else if (working.length()!=7) {
isValid = false;
}
if (!isValid) {
mDateEntryField.setError(“Enter a valid date: MM/YYYY”);
} else {
mDateEntryField.setError(null);
}
}
@Override
public void afterTextChanged(Editable s) {}
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
};
}
There you have it. When you run the APK on a device or an emulator, you will be prompted to enter a date, and the results will be validated on-the-fly as you type.
It is a powerful feedback mechanism to the user, and the technique could easily be applied to much more than just dates. I don’t think anyone will disagree that there is little room left for round-trip validation on the mobile front. TextWatcher is a great way to take your user experience to the next level.