Logo Icon Logo
A Crowd-sourced Cookbook on Writing Great Android® Apps
GitHub logo Twitter logo OReilly Book Cover Art

Constraining EditText Values with Attributes and the TextWatcher Interface

Author: Daniel Fowler
Published? true
FormatLanguage: WikiFormat

Problem:

There is a need to limit the range and type of values being input.

Solution:

Use appropriate attributes on the EditText Views in the layout XML and enhance it by implementing the TextWatcher interface.

Discussion:

When an App needs input from a user sometimes only a specific type of value is required; maybe a whole number, a decimal number, a number between two values or words that are capitalized. When defining an EditText in a layout attributes such as android:inputType can be used to constrain what the user is able to type. This automatically reduces the amount of code required later on because there are fewer checks to perform on the data that was entered. The TextWatcher interface is also useful for restricting values. In the following example an EditText only allows a value between 0 and 100, for example to represent a percentage. There is no code required to check the value because it is all done as the user types. Here a simple layout has one EditText.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:orientation="vertical"
              android:layout_width="fill_parent"
              android:layout_height="fill_parent">
  <EditText android:layout_width="fill_parent" 
            android:layout_height="wrap_content"
            android:id="@+id/percent" 
            android:text="0"
            android:maxLength="3"
            android:inputType="number"/>
</LinearLayout>

It is given a starting value of zero with android:text="0", the number of characters that can be typed has been limited to three with android:maxLength="3" because the largest number we need, 100, only has three digits. Finally the user is restricted to only positive numbers with android:inputType="number". It is a good idea to review the attributes that Android views support, as defining them in the XML layout can reduce the amount of code to write.

Within the Activity (or AppCompatActivity) class an inner class is used to implement the TextWatcher interface. The afterTextChanged() method is defined and will be called when the text changes as the user types. In this method the value being typed is checked to see if it is greater than 100. If so it is set to 100. There is no need to check for values below zero because they cannot be entered (because of the XML attributes). The try catch is required for when all the numbers are deleted, in which case the test for values above 100 would exception (trying to parse an empty string). TextWatcher also has beforeTextChanged() and onTextChanged() implementations but they are empty here.

class CheckPercentage implements TextWatcher{
    public void afterTextChanged(Editable s) {
        try {
            Log.d("Percentage", "input: " + s);
            if(Integer.parseInt(s.toString())>100)
                s.replace(0, s.length(), "100");
        }
        catch(NumberFormatException nfe){}
    }
    public void beforeTextChanged(CharSequence s, int start, int count, int after) {
        // Not used, details on text just before it changed
	// used to track in detail changes made to text, e.g. implement an undo
    }
    public void onTextChanged(CharSequence s, int start, int before, int count) {
        // Not used, details on text at the point change made
    }
}

In onCreate() the TextWatcher is connected to the EditText using its addTextChangedListener() method.

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    ((EditText) findViewById(R.id.percent)).addTextChangedListener(new CheckPercentage());
}

Note that it is fine to change the EditText value in afterTextChanged() as its internal Editable class is passed in. However, it cannot be changed by altering the CharSequence passed into beforeTextChanged() and onTextChanged(). For further details on the attributes supported by EditText see the Android documentation on the TextView, from which EditText is subclassed.

Also remember that changing the value in the EditText causes the afterTextChanged() method to be called again. Care must be taken to ensure that the code using a TextWatcher does not result in endless looping.

See Also:

[1]

[2]

[3]

Download:

The source code for this project can be downloaded from http://tekeye.uk/android/examples/download/percentage.zip.
marquessbr 2015-08-10 10:00:01.215 nice! this help-me a lot! thank's
marksyntax 2013-02-05 05:26:46.777 <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent"> <EditText android:layout_width="fill_parent" android:layout_height="wrap_content" android:id="@+id/percent" android:text="0" android:maxLength="3" android:inputType="number"/> </LinearLayout> IM GETTING THIS ERROR -- >[I18N] Hardcoded string "0", should use @string resource PLEASE HELP