26

I have a numberDecimal EditText which I want to validate using a regular expression. In validation what I want is:

  1. Before the decimal point, the maximum digit I want to enter is three and the digit should not start with zero like 2,23,342, etc.

  2. After the decimal point, the maximum digit I want to enter is one like .1, .3, .6, etc.

So the number that I allow the user to enter is like 2.1, 32.5, 444.8, 564.9, etc.

But in my code, what happens is:

  1. It allows the user to enter more than a three digit number before the decimal point like 3456, 4444, 5555 and after that it doesn't allow me to enter a decimal point after that.

  2. It allows me to enter 0 before the decimal point as the start of the digit.

So why does this happen, is anything wrong in the regular expression I have used? If anyone knows, please help me to solve this.

Code I have used:

weightEditText.addTextChangedListener(new TextWatcher() 
{           
    @Override
    public void onTextChanged(CharSequence s, int start, int before, int count) {               
    }           
    @Override
    public void beforeTextChanged(CharSequence s, int start, int count, int after) {            
    }           
    @Override
    public void afterTextChanged(Editable s) 
    {
        Pattern mPattern = Pattern.compile("^([1-9][0-9]{0,2})?(\\.[0-9]?)?$");

        Matcher matcher = mPattern.matcher(s.toString());               
        if(!matcher.find())
        {
            weightEditText.setText(); // Don't know what to place                   
        }
    }
});
0xCursor
  • 2,242
  • 4
  • 15
  • 33
AndroidDev
  • 4,521
  • 24
  • 78
  • 126
  • See http://stackoverflow.com/a/2811058/1160207, and http://stackoverflow.com/a/4174811/1160207. It will help you. – Never Quit Apr 26 '12 at 06:32

5 Answers5

26

There's never any point in examining dest alone in an InputFilter; that's what's already present in the field. Change the regular expression match to be against source and it would be appropriate if you only wanted to check that certain characters were accepted into the field. However, you want to check field formatting, not just filter the input on a character-by-character basis. This is much more complex.

Every time the user makes a change to the contents of tempEditText, the system calls your filter's filter method before the change is actually made. It passes the current field contents and the proposed change (which can be insert/append, delete, or replace). The change is represented by a source CharSequence source (the characters—if any—to be added to the field), range start and end indexes within the source (the range is not necessarily all of source), a Spanned dest (the current field contents before the change) and range dstart and dend indexes within dest that are proposed to be replaced by the indicated source range.

The job of filter is to modify the change (if necessary) and return a CharSequence to use (in its entirety) in place of source (or null to go ahead and use source). Rather than checking dest as you are now doing, you will need to check whether the change will result in an acceptable field. To do this, you will need more complex logic. (Note, in particular, that the new character(s) may be intended for insert somewhere other than at the end; also, filter will be called when the user is deleting characters as well as adding them.)

It may be easier to implement a TextWatcher. In it's beforeTextChanged method, you can record the current contents and in it's afterTextChanged method, you can check (using a regular expression) whether the contents are acceptable and, if not, restore the before-the-change contents. (Make sure, though, that the text before the change was acceptable. If it isn't, substitute something acceptable—like clearing the field. Otherwise your code will go into an infinite loop because the TextWatcher is going to be invoked again when you correct the field contents.)

You also have an error in your regular expression: it allows a leading zero. Here's an improved version that fixes this problem (and removes one set of unnecessary parentheses):

"^([1-9][0-9]{0,2})?(\\.[0-9]?)?$"

(As an aside: you can use \\d instead of [0-9].)

EDIT

Here's my edit of your edit:

weightEditText.addTextChangedListener(new TextWatcher() 
{           
    private static final Pattern sPattern
        = Pattern.compile("^([1-9][0-9]{0,2})?(\\.[0-9]?)?$");

    private CharSequence mText;

    private boolean isValid(CharSequence s) {
        return sPattern.matcher(s).matches();
    }

    @Override
    public void onTextChanged(CharSequence s, int start, int before, int count){
    }           

    @Override
    public void beforeTextChanged(CharSequence s, int start, int count,
            int after){
        mText = isValid(s) ? new CharSequence(s) : "";
    }           

    @Override
    public void afterTextChanged(Editable s) 
    {
        if (!isValid(s))
        {
            weightEditText.setText(mText);
        }
        mText = null;
    }
});
Ted Hopp
  • 232,168
  • 48
  • 399
  • 521
  • I have edited my answer and do it using TextWatcher. But not able to find a clue what to write in beforeTextChange method. can u please look at my Edited answer and tell me what i should do. – AndroidDev Apr 27 '12 at 06:10
  • @Anshuman - First, move your pattern match to a separate method (called, for instance, `isValid`). Then declare a field in your TextWatcher to store the text before the change: `class MyTextWatcher { private CharSequence mText; ... public void beforeTextChanged (CharSequence s, int start, int count, int after) { mText = isValid(s) ? new CharSequence(s) : ""; }`. Finally, if the test fails in `afterTextChanged`, call `weightEditText.setText(mText);`. Also, always set `mText = null;` before exiting `afterTextChanged` since the value is now useless. – Ted Hopp Apr 27 '12 at 06:28
  • @Anshuman - I would also make mPattern a `static final` field; there's no point in compiling it more than once. – Ted Hopp Apr 27 '12 at 06:29
  • Yeh its working....thanks a lot...One last question is it possible to change my pattern in such a way that it also doesn't allow to enter decimal point as a start of the digit. – AndroidDev Apr 27 '12 at 06:36
  • 1
    @Anshuman - Sure: `"^([1-9][0-9]{0,2}(\\.[0-9]?)?)?$"`. (P.S. I updated my answer with sample code. I'm glad you got it working.) – Ted Hopp Apr 27 '12 at 06:38
  • @ Ted Hopp Wow great..its working fine and check for all validation..But i wanna to know actually what the use of ? there...why by removing the ? solve the problem... can u please explain me each of the term.. – AndroidDev Apr 27 '12 at 06:44
  • @Anshuman - I didn't remove it. I moved it to the end of the entire pattern. The top level pattern is: `^(something)?$` - in other words, `something` is optional and an empty string matches. Inside the parentheses, `something` breaks down into two parts: `[1-9][0-9]{0,2}` -- a non-zero digit followed by up to 2 more digits -- and `(\\.[0-9]?)?` -- a decimal point followed by an optional digit, all of which is optional. – Ted Hopp Apr 27 '12 at 06:49
  • Its hanging keyboard. – Dalvinder Singh Jul 23 '18 at 11:28
  • @DalvinderSingh - What's hanging keyboard? Is there anything in logcat? Is something looping? Try setting a breakpoint or two with the debugger to diagnose what's happening. – Ted Hopp Jul 23 '18 at 14:12
2

Maybe my implementation will help anyone:

    etRepeaterCallsign.addTextChangedListener(new TextWatcher() {
        private final Pattern sPattern
                = Pattern.compile("^([A-Z]{0,2})?(\\d)?([A-Z-]{0,5})"); // ^([1-9][0-9]{0,2})?(\\.[0-9]?)?$

        private CharSequence mText;

        private boolean isValid(CharSequence s) {
            return sPattern.matcher(s).matches();
        }

        @Override
        public void beforeTextChanged(CharSequence r, int start, int count,
                                      int after) {
            mText = r.toString();
        }

        @Override
        public void onTextChanged(CharSequence s, int start, int before, int count) {
            bIsEdit = true;
        }
        @Override
        public void afterTextChanged(Editable s) {
            etRepeaterCallsign.removeTextChangedListener(this);

            int iCursorPosition = etRepeaterCallsign.getSelectionStart();
            etRepeaterCallsign.setText("");
            if (isValid(s))
                etRepeaterCallsign.append(s);
            else
                etRepeaterCallsign.append(mText);

            etRepeaterCallsign.setSelection(iCursorPosition);

            etRepeaterCallsign.addTextChangedListener(this);

        }
    });
1

You could just parse the number and check that it is < 1000 and that 10*number is an integer while num is not. It would probably be more readable too.

assylias
  • 321,522
  • 82
  • 660
  • 783
1

I tried your pattern using my code as following. It works perfectly as 2.1, 32.5, 444.8, 564.9 etc.

My code:

public class WebPush extends Activity {  
    EditText editTxt;
    private TextView regresult;  

    protected void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main); 

        editTxt = (EditText) findViewById(R.id.editID);
        regresult = (TextView) findViewById(R.id.txtID);

        String urName = editTxt.getText().toString();
        editTxt.addTextChangedListener(new TextWatcher() {

            @Override
            public void onTextChanged(CharSequence arg0, int arg1, int arg2, int arg3) {
            }

            @Override
            public void beforeTextChanged(CharSequence arg0, int arg1, int arg2,int arg3) {
            }       

            @Override
            public void afterTextChanged(Editable s) {
                if (editTxt.getText().toString().matches("(^([0-9]{0,3})?)(\\.[0-9]{0,1})?$"))
                {
                    regresult.setText("");
                }
                else
                {
                    regresult.setText("invalid number");
                }
            }
        });
    }
}
Allan Pereira
  • 2,572
  • 4
  • 21
  • 28
sush
  • 476
  • 1
  • 7
  • 20
  • But i my case i don't want to show error in separate TextView. Instead i don't allow the user to enter wrong digit in the EditBox. I mean to say that if the digit enter is correct then only it will allow user to type the digit. If it is wrong then it don't allow the user to type. So do u know how that can be done – AndroidDev Apr 26 '12 at 06:37
0

You could try something like this: ^[1-9][0-9]{0,2}(\\.\\d)?$. This should match any number which does not start with 0 (^[1-9]) and is followed by at most two numbers ([0-9]{0,2}). The regex also allows for an optional decimal value ((\\.\\d)?$).

That being said, ideally you should parse the string value to a double or float and make what ever validations you need to make using the actual numerical value.

To parse your string into a double value, you will need to use the Double.parseDouble(String s) like so: double myValue = Double.parseDouble(EditText.getText());

npinti
  • 51,780
  • 5
  • 72
  • 96
  • I used ur pattern. Now it doesn't allow me to type anything in the EditText and one more thing if i used single \ the it show error. It will better if u show me in my example code and please suggest me how can i parse the string value to double because it is stored in Spanned dest variable – AndroidDev Apr 26 '12 at 06:04
  • @Anshuman: I have changed my answer. In Java you need to escape `\` so you need to have them in pairs, as per the change I made. I also included a link to the JavaDoc on how you can parse a String to a double. – npinti Apr 26 '12 at 06:08
  • I have used ur pattern in my code like that Pattern.compile("(^[1-9])([0-9]{0,2})((\\.\\d)?$)");. now what happen is it dont allow me to type any number in the EditBox – AndroidDev Apr 26 '12 at 06:12
  • But when i used this code in simple java project and run in console it works fine and shows error when i enter wrong number. but when i used the same code and try to check the error in EditText input filter method it doesn't work why this happen. – AndroidDev Apr 26 '12 at 06:24
  • @Anshuman: I have tried it myself and it worked for string values such as `1`, `2.1`, `123.2` but failed for `02.1`. – npinti Apr 26 '12 at 06:30
  • @Anshuman: You should use a debugger to see what is actually being passed and when. Other than that, I can't do much. – npinti Apr 26 '12 at 06:34
  • Yeh u r right if i perform this task on EditText.addTextChangedListener and show error in another TextView for wrong number enter it works fine. Its it something to do on calling EditText.setFilter method..U can look at my code. – AndroidDev Apr 26 '12 at 06:40
  • @Anshuman: I do not think that it is a good idea to fire up the Regex each and every time that the user types something in the text box. You should do the validation once the submit button or its equivalent is pressed. Doing the check with every keypress will most likely result in an error as you type. – npinti Apr 26 '12 at 06:59
  • Actually i know but my requirement is that only. I have to check for the validation when the user type in the edit box only. Cant used separate button. Is there any way to solve this out – AndroidDev Apr 26 '12 at 07:07
  • @Anshuman: If you want to validate the data as soon as possible, then, I think you should validate the text field once that the focus is lost, not each and every time the user types in something. – npinti Apr 26 '12 at 07:18
  • But it will let user to type wrong number also..because validation is perform only when the focus is lost from the EditText. – AndroidDev Apr 26 '12 at 07:32
  • @Anshuman: What you could do is that if the focus is lost and the textfield is invalid, display a message as a label or something similar and get the textfield focus again. If you are still having issues, I would recommend you open a new question. – npinti Apr 26 '12 at 07:37