2009-12-28

I really like Android's EditText

In the last couple of days I enjoyed doing a little bit coding for Android at home. With a J2ME background I was really amazed by the richness of the Android APIs. You have a lot of the Java Standard Edition APIs plus a lot of useful and well thought out APIs for UI and phone related stuff.

I quickly realized that a lot of functionality I thought I have to implement myself is already there. For example I really like the preferences API. On J2ME I had to implement everything myself. Starting from the preferences UI to the preference store.

Also surprising for me was how rich the widgets really are. Let's take the EditText widget. It not only looks really good when rendered on the screen it also has a lot of functionality you can easily use.

For my first application I have a form to enter geocoordinates. They are represented as text like "50:24,123".

After the basics worked I wanted to make the UI a little bit more user friendly by helping the user to enter only valid coordinates.

First I wanted to limit the usable characters to "0123456789:,.-". And it turned out to be quite simple:

NumberKeyListener keyListener = new NumberKeyListener() {

        public int getInputType() {
        return InputType.TYPE_CLASS_NUMBER;
        }

        @Override
        protected char[] getAcceptedChars() {
        return new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '.', ':', '-', ',' };
        }
    };

    lonEditText.setKeyListener(keyListener);

But this still doesn't prevent someone from entering something like "1:1:1:1" which is obviously not valid. So I introduced an input filter:

    InputFilter[] filters = new InputFilter[1];

    filters[0] = new InputFilter() {

        public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {
        if (end > start) {
            String destTxt = dest.toString();
            String resultingTxt = destTxt.substring(0, dstart) + source.subSequence(start, end) + destTxt.substring(dend);

            if (!resultingTxt.matches("[0123456789]*[\\:]?[0123456789]*[,.]?[0123456789]*")) {

            if (source instanceof Spanned) {
                SpannableString sp = new SpannableString("");
                return sp;
            } else {
                return "";
            }

            }
            
        }

        return null;
        }

    };

lonEditText.setFilters(filters);

(Please note that this is just an example and the code isn't production quality.)

That was easy and a lot better. Unfortunately you can still enter invalid coordinates like "999:99.123".

First I thought of getting all the magic into the regexp but first I don't like regexp-magic so much and second I wanted to see how I can give the user some feedback about the validity of the entered data.

I decided to implement a TextWatcher and validate the data in "afterTextChanged".

There I just tried to parse the coordinate and in case of an error I wanted to inform the user. My first approach was to use a Toast but it turned out that EditText already has everything I needed.

The code looked like this:

    if(coordinateValid){
        lonEditText.setError(null);
    } else {
        lonEditText.setError("Coordinate is invalid.");
    }

This is not the actual source but it shows what I wanted to show: Use setError to indicate a problem with the text contained in that EditText.

When the invalid field has focus the error is displayed. If it's not focused there is an icon showing that something is wrong with the content of this field.

That was easy and it's more than I had expected. Thanks Android!

5 comments:

  1. This comment has been removed by the author.

    ReplyDelete
  2. OMG This has taken me SOOOO long to find. Exactly the way to do it! Thankyou VERY much for posting this. So long input filters!

    ReplyDelete
  3. How do i customize font size in set Error?

    ReplyDelete
  4. I haven't tried it yet but I guess if it's possible then http://developer.android.com/guide/topics/ui/themes.html should be the answer. Otherwise you could have a look at the AOSP sources to get the ultimate answer.

    ReplyDelete
  5. Could you update with the missing code for "999:99.123". and afterTextChanged

    ReplyDelete