152

I'm trying to write an app that helps you manage your finances. I'm using an EditText Field where the user can specify an amount of money.

I set the inputType to numberDecimal which works fine, except that this allows people to enter numbers such as 123.122 which is not perfect for money.

Is there a way to limit the number of characters after the decimal point to two?

Michael Currie
  • 13,721
  • 9
  • 42
  • 58
Konstantin Weitz
  • 6,180
  • 8
  • 26
  • 43
  • You could write a regular expression and verify the content of the edit text when it looses focus. – blindstuff Mar 18 '11 at 20:37
  • I found the `InputFilter` interface, it seems to do what i want http://developer.android.com/reference/android/text/method/NumberKeyListener.html#filter%28java.lang.CharSequence,%20int,%20int,%20android.text.Spanned,%20int,%20int%29, but the method `filter` that I have to implement is rather confusing to me. Did somebody write such a Filter already and knows how to use it? – Konstantin Weitz Mar 18 '11 at 21:31
  • Do any of the suggested solutions work for RTL locales? As far as I can tell they will not... – Nick Aug 02 '16 at 16:32

40 Answers40

128

More elegant way would be using a regular expression ( regex ) as follows:

public class DecimalDigitsInputFilter implements InputFilter {

Pattern mPattern;

public DecimalDigitsInputFilter(int digitsBeforeZero,int digitsAfterZero) {
    mPattern=Pattern.compile("[0-9]{0," + (digitsBeforeZero-1) + "}+((\\.[0-9]{0," + (digitsAfterZero-1) + "})?)||(\\.)?");
}

@Override
public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {

        Matcher matcher=mPattern.matcher(dest);       
        if(!matcher.matches())
            return "";
        return null;
    }

}

To use it do:

editText.setFilters(new InputFilter[] {new DecimalDigitsInputFilter(5,2)});
pixel
  • 24,905
  • 36
  • 149
  • 251
Asaf Pinhassi
  • 15,177
  • 12
  • 106
  • 130
  • I only required the decimal places validation, for dollars entered, here is my regex: [0-9]*+((\\.[0-9]{0,1})?)||(\\.)? – Shardul Mar 29 '12 at 19:35
  • 39
    Hi, there are some edge cases still not being handled well. For instance, after I type 2.45, I tend to "move cursor to the front most of the text". I wish to produce text 12.45, it won't allow. – Cheok Yan Cheng Sep 28 '12 at 01:53
  • 4
    it don't allow to change digits before decimal after user have entered 2 digits after decimal point. – Gaurav Singla Nov 17 '13 at 09:14
  • Any solution for the above mentioned issue? If the decimal points are there we cannot edit the value inside or at the beginning of the text. – Gabor Peto Apr 16 '14 at 09:33
  • 11
    Great solution but it is not correct entirely. The matcher should not check dest, it should check the value in the edittext (dest.subSequence(0, dstart) + source.subSequence(start, end) + dest.subSequence(dend, dest.length())) – Mihaela Romanca Apr 24 '14 at 11:22
  • 7
    Mihaela is right, we should be matching against the string that is trying to fill the edittext. I found how to concatenate on another question like this CharSequence match = TextUtils.concat(dest.subSequence(0, dstart), source.subSequence(start, end), dest.subSequence(dend, dest.length())); The regex was causing problems afterwards though so I changed it to this "^\\d{1," + digitsBeforeZero + "}(\\.\\d{0," + digitsAfterZero + "})?$" But you'll have to do validation afterwards too because "1." is valid with that regex but we need it that way so the period can be typed. – dt0 Nov 19 '14 at 01:11
  • 6
    How would you make this work for commas (,) too? Some regions of the world type decimal numbers with commas (ex: 123,45). – Andrew Apr 23 '15 at 22:58
  • @PratikButani Solution from KonstantinWeitz http://stackoverflow.com/a/5368816/72437 works reasonable good for me – Cheok Yan Cheng May 31 '16 at 07:37
  • 1
    @CheokYanCheng Thank you, I think this one also helpful, he has edited this answer. http://stackoverflow.com/a/24632346/1318946 – Pratik Butani May 31 '16 at 07:40
  • How to limit this regex to enter only one dot? Using this pattern it allows you to enter two dots – Igor SKRYL Jan 29 '18 at 16:32
  • 2
    WRONG solution. Doesn't work correctly if you set `digitsBeforeZero` to 1 and `digitsAfterZero` to 3 you won't be able to type 3 digits after decimal point (. dot) – user924 Apr 12 '18 at 21:52
  • HERE fixes for this solution: https://stackoverflow.com/a/24632346/7767664 – user924 Apr 12 '18 at 22:01
  • this poses issues. if a user enters a decimal say 0.25 and first starts by entering a "." the system hangs – Ziggy Feb 27 '20 at 17:18
  • Fair warning to anyone using this: Android Studio 3.6.3 is trying to suggest 'simplifications' that break the regex due to the dynamic values. – Benjamin S May 12 '20 at 15:19
70

Simpler solution without using regex:

import android.text.InputFilter;
import android.text.Spanned;

/**
 * Input filter that limits the number of decimal digits that are allowed to be
 * entered.
 */
public class DecimalDigitsInputFilter implements InputFilter {

  private final int decimalDigits;

  /**
   * Constructor.
   * 
   * @param decimalDigits maximum decimal digits
   */
  public DecimalDigitsInputFilter(int decimalDigits) {
    this.decimalDigits = decimalDigits;
  }

  @Override
  public CharSequence filter(CharSequence source,
      int start,
      int end,
      Spanned dest,
      int dstart,
      int dend) {


    int dotPos = -1;
    int len = dest.length();
    for (int i = 0; i < len; i++) {
      char c = dest.charAt(i);
      if (c == '.' || c == ',') {
        dotPos = i;
        break;
      }
    }
    if (dotPos >= 0) {

      // protects against many dots
      if (source.equals(".") || source.equals(","))
      {
          return "";
      }
      // if the text is entered before the dot
      if (dend <= dotPos) {
        return null;
      }
      if (len - dotPos > decimalDigits) {
        return "";
      }
    }

    return null;
  }

}

To use:

editText.setFilters(new InputFilter[] {new DecimalDigitsInputFilter(2)});
peceps
  • 17,370
  • 11
  • 72
  • 79
41

This implementation of InputFilter solves the problem.

import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.method.DigitsKeyListener;

public class MoneyValueFilter extends DigitsKeyListener {
    public MoneyValueFilter() {
        super(false, true);
    }

    private int digits = 2;

    public void setDigits(int d) {
        digits = d;
    }

    @Override
    public CharSequence filter(CharSequence source, int start, int end,
            Spanned dest, int dstart, int dend) {
        CharSequence out = super.filter(source, start, end, dest, dstart, dend);

        // if changed, replace the source
        if (out != null) {
            source = out;
            start = 0;
            end = out.length();
        }

        int len = end - start;

        // if deleting, source is empty
        // and deleting can't break anything
        if (len == 0) {
            return source;
        }

        int dlen = dest.length();

        // Find the position of the decimal .
        for (int i = 0; i < dstart; i++) {
            if (dest.charAt(i) == '.') {
                // being here means, that a number has
                // been inserted after the dot
                // check if the amount of digits is right
                return (dlen-(i+1) + len > digits) ? 
                    "" :
                    new SpannableStringBuilder(source, start, end);
            }
        }

        for (int i = start; i < end; ++i) {
            if (source.charAt(i) == '.') {
                // being here means, dot has been inserted
                // check if the amount of digits is right
                if ((dlen-dend) + (end-(i + 1)) > digits)
                    return "";
                else
                    break;  // return new SpannableStringBuilder(source, start, end);
            }
        }

        // if the dot is after the inserted part,
        // nothing can break
        return new SpannableStringBuilder(source, start, end);
    }
}
Konstantin Weitz
  • 6,180
  • 8
  • 26
  • 43
  • May I know is there any reason we need to return SpannableStringBuilder instead of null? I test it with null, it works well too. Also, is there any need for us to inherit from DigitsKeyListener? As using android:inputType="numberDecimal" will perform all "0123456789." characters enforcement. – Cheok Yan Cheng Sep 28 '12 at 01:55
35

Here is a sample InputFilter which only allows max 4 digits before the decimal point and max 1 digit after that.

Values that edittext allows: 555.2, 555, .2

Values that edittext blocks: 55555.2, 055.2, 555.42

        InputFilter filter = new InputFilter() {
        final int maxDigitsBeforeDecimalPoint=4;
        final int maxDigitsAfterDecimalPoint=1;

        @Override
        public CharSequence filter(CharSequence source, int start, int end,
                Spanned dest, int dstart, int dend) {
                StringBuilder builder = new StringBuilder(dest);
                builder.replace(dstart, dend, source
                        .subSequence(start, end).toString());
                if (!builder.toString().matches(
                        "(([1-9]{1})([0-9]{0,"+(maxDigitsBeforeDecimalPoint-1)+"})?)?(\\.[0-9]{0,"+maxDigitsAfterDecimalPoint+"})?"

                        )) {
                    if(source.length()==0)
                        return dest.subSequence(dstart, dend);
                    return "";
                }

            return null;

        }
    };

    mEdittext.setFilters(new InputFilter[] { filter });
Favas Kv
  • 2,961
  • 2
  • 28
  • 38
30

I don't like the other solution and I created my own. With this solution you can't enter more than MAX_BEFORE_POINT digit before the point and the decimals can't be more than MAX_DECIMAL.

You just can't type the digit in excess, no other effects! In additional if you write "." it types "0."

  1. Set the EditText in the layout to:

    android:inputType="numberDecimal"

  2. Add the Listener in your onCreate. If you want modify the number of digits before and after the point edit the call to PerfectDecimal(str, NUMBER_BEFORE_POINT, NUMBER_DECIMALS), here is set to 3 and 2

    EditText targetEditText = (EditText)findViewById(R.id.targetEditTextLayoutId);
    
    targetEditText.addTextChangedListener(new TextWatcher() {
      public void onTextChanged(CharSequence arg0, int arg1, int arg2, int arg3) {}
    
      public void beforeTextChanged(CharSequence arg0, int arg1, int arg2, int arg3) {}
    
      public void afterTextChanged(Editable arg0) {
        String str = targetEditText.getText().toString();
        if (str.isEmpty()) return;
        String str2 = PerfectDecimal(str, 3, 2);
    
        if (!str2.equals(str)) {
            targetEditText.setText(str2);
            targetEditText.setSelection(str2.length());
        }
      }
    });
    
  3. Include this Funcion:

    public String PerfectDecimal(String str, int MAX_BEFORE_POINT, int MAX_DECIMAL){
      if(str.charAt(0) == '.') str = "0"+str;
      int max = str.length();
    
      String rFinal = "";
      boolean after = false;
      int i = 0, up = 0, decimal = 0; char t;
      while(i < max){
        t = str.charAt(i);
        if(t != '.' && after == false){
            up++;
            if(up > MAX_BEFORE_POINT) return rFinal;
        }else if(t == '.'){
            after = true;
        }else{
            decimal++;
            if(decimal > MAX_DECIMAL)
                return rFinal;
        }
        rFinal = rFinal + t;
        i++;
      }return rFinal;
    }
    

And it's done!

Kamal
  • 335
  • 3
  • 10
Apoleo
  • 2,045
  • 2
  • 21
  • 37
30

I made some fixes for @Pinhassi solution. It handles some cases:

1.you can move cursor anywhere

2.minus sign handling

3.digitsbefore = 2 and digitsafter = 4 and you enter 12.4545. Then if you want to remove ".", it will not allow.

public class DecimalDigitsInputFilter implements InputFilter {
    private int mDigitsBeforeZero;
    private int mDigitsAfterZero;
    private Pattern mPattern;

    private static final int DIGITS_BEFORE_ZERO_DEFAULT = 100;
    private static final int DIGITS_AFTER_ZERO_DEFAULT = 100;

    public DecimalDigitsInputFilter(Integer digitsBeforeZero, Integer digitsAfterZero) {
    this.mDigitsBeforeZero = (digitsBeforeZero != null ? digitsBeforeZero : DIGITS_BEFORE_ZERO_DEFAULT);
    this.mDigitsAfterZero = (digitsAfterZero != null ? digitsAfterZero : DIGITS_AFTER_ZERO_DEFAULT);
    mPattern = Pattern.compile("-?[0-9]{0," + (mDigitsBeforeZero) + "}+((\\.[0-9]{0," + (mDigitsAfterZero)
        + "})?)||(\\.)?");
    }

    @Override
    public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {
    String replacement = source.subSequence(start, end).toString();
    String newVal = dest.subSequence(0, dstart).toString() + replacement
        + dest.subSequence(dend, dest.length()).toString();
    Matcher matcher = mPattern.matcher(newVal);
    if (matcher.matches())
        return null;

    if (TextUtils.isEmpty(source))
        return dest.subSequence(dstart, dend);
    else
        return "";
    }
}
android_dev
  • 3,886
  • 1
  • 33
  • 52
  • I think it should be perfect solution, made my day. Thank you. – Pratik Butani May 31 '16 at 07:31
  • @android_dev why I can't type negative values (minus)? – user924 Apr 14 '18 at 10:29
  • IF you set `android:inputType="number"` or `android:inputType="numberDecimal"` it won't allow to type minus, this `android:digits="0123456789.-"` doesn't help – user924 Apr 14 '18 at 10:42
  • I tried `android:inputType ="numbersigned"` and now I can type minus, but I can't type `.` dot! `android:digits="0123456789."` doesn't help! – user924 Apr 14 '18 at 10:47
  • finally I found a solution `android:digits="0123456789.-"` and `android:inputType="numberDecimal|numberSigned"`! – user924 Apr 14 '18 at 10:53
20

I achieved this with the help of TextWatcher by the following way

final EditText et = (EditText) findViewById(R.id.EditText1);
int count = -1;
et.addTextChangedListener(new TextWatcher() {
    public void onTextChanged(CharSequence arg0, int arg1, int arg2,int arg3) {             

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

    }

    public void afterTextChanged(Editable arg0) {
        if (arg0.length() > 0) {
            String str = et.getText().toString();
            et.setOnKeyListener(new OnKeyListener() {
                public boolean onKey(View v, int keyCode, KeyEvent event) {
                    if (keyCode == KeyEvent.KEYCODE_DEL) {
                        count--;
                        InputFilter[] fArray = new InputFilter[1];
                        fArray[0] = new InputFilter.LengthFilter(100);
                        et.setFilters(fArray);
                        //change the edittext's maximum length to 100. 
                        //If we didn't change this the edittext's maximum length will
                        //be number of digits we previously entered.
                    }
                    return false;
                }
            });
            char t = str.charAt(arg0.length() - 1);
            if (t == '.') {
                count = 0;
            }
            if (count >= 0) {
                if (count == 2) {                        
                    InputFilter[] fArray = new InputFilter[1];
                    fArray[0] = new InputFilter.LengthFilter(arg0.length());
                    et.setFilters(fArray);
                    //prevent the edittext from accessing digits 
                    //by setting maximum length as total number of digits we typed till now.
                }
                count++;
            }
        }
    }
});

This solution will not allow the user to enter more than two digit after decimal point. Also you can enter any number of digits before decimal point. I hope this will help. Thank you.

Bhargav Rao
  • 50,140
  • 28
  • 121
  • 140
Gunaseelan
  • 14,415
  • 11
  • 80
  • 128
  • Sorry for late info. DOnt forget to initialize `count` with `-1`. Then only this will work correctly. `int count = -1;` – Gunaseelan May 27 '13 at 06:22
  • Gunaseelan - i tried the above code and its working good. But when i delete the typed text and start typing again its typing only one digit, any solution for this..... – Siva K Jun 05 '13 at 13:26
  • @SivaK No way friend. If you delete and then types it will accepts minimum 100 digit. I don't how are you access this `listener`. Any way please take a look an my blog [post](http://v4all123.blogspot.com/2013/05/set-limit-for-fraction-in-decimal.html). You may get an idea. If you can't please let me know. I will help you regards this issue. – Gunaseelan Jun 05 '13 at 13:43
  • i've verified what @SivaK has said. this is clever in any case but i'm going to make some edits to this so that it is fully functional (in my opinion) – MrTristan Feb 10 '15 at 21:15
  • 1
    @Gunaseelan thank you for your solution. But it has some bugs. Eg, when I delete second decimal, it is impossible type it again (I must delete all decimals in order to being able to type it again). Also, after deletion whole entry, some strange limitations occur when type again. – akelec Mar 19 '20 at 11:39
  • did you check this solution for copy pasting from buffer? I think such logic will be broken easily) – user924 Sep 07 '21 at 10:00
  • A lot of bugs in this one. – Prajwal Waingankar Jul 11 '23 at 07:57
18

The InputFilter I came up with allows you to configure the number of digits before and after the decimal place. Additionally, it disallows leading zeroes.

public class DecimalDigitsInputFilter implements InputFilter
{
    Pattern pattern;

    public DecimalDigitsInputFilter(int digitsBeforeDecimal, int digitsAfterDecimal)
    {
        pattern = Pattern.compile("(([1-9]{1}[0-9]{0," + (digitsBeforeDecimal - 1) + "})?||[0]{1})((\\.[0-9]{0," + digitsAfterDecimal + "})?)||(\\.)?");
    }

    @Override public CharSequence filter(CharSequence source, int sourceStart, int sourceEnd, Spanned destination, int destinationStart, int destinationEnd)
    {
        // Remove the string out of destination that is to be replaced.
        String newString = destination.toString().substring(0, destinationStart) + destination.toString().substring(destinationEnd, destination.toString().length());

        // Add the new string in.
        newString = newString.substring(0, destinationStart) + source.toString() + newString.substring(destinationStart, newString.length());

        // Now check if the new string is valid.
        Matcher matcher = pattern.matcher(newString);

        if(matcher.matches())
        {
            // Returning null indicates that the input is valid.
            return null;
        }

        // Returning the empty string indicates the input is invalid.
        return "";
    }
}

// To use this InputFilter, attach it to your EditText like so:
final EditText editText = (EditText) findViewById(R.id.editText);

EditText.setFilters(new InputFilter[]{new DecimalDigitsInputFilter(4, 4)});
Luke
  • 2,187
  • 1
  • 18
  • 29
16

The requirement is 2 digits after decimal. There should be no limit for digits before decimal point. So, solution should be,

public class DecimalDigitsInputFilter implements InputFilter {

    Pattern mPattern;

    public DecimalDigitsInputFilter() {
        mPattern = Pattern.compile("[0-9]*+((\\.[0-9]?)?)||(\\.)?");
    }

    @Override
    public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {
        Matcher matcher = mPattern.matcher(dest);
        if (!matcher.matches())
            return "";
        return null;
    }
}

And use it as,

mEditText.setFilters(new InputFilter[]{new DecimalDigitsInputFilter()});

Thanks to @Pinhassi for the inspiration.

Mangesh
  • 5,491
  • 5
  • 48
  • 71
12

My solution is simple and works perfect!

public class DecimalInputTextWatcher implements TextWatcher {

private String mPreviousValue;
private int mCursorPosition;
private boolean mRestoringPreviousValueFlag;
private int mDigitsAfterZero;
private EditText mEditText;

public DecimalInputTextWatcher(EditText editText, int digitsAfterZero) {
    mDigitsAfterZero = digitsAfterZero;
    mEditText = editText;
    mPreviousValue = "";
    mRestoringPreviousValueFlag = false;
}

@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
    if (!mRestoringPreviousValueFlag) {
        mPreviousValue = s.toString();
        mCursorPosition = mEditText.getSelectionStart();
    }
}

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

@Override
public void afterTextChanged(Editable s) {
    if (!mRestoringPreviousValueFlag) {

        if (!isValid(s.toString())) {
            mRestoringPreviousValueFlag = true;
            restorePreviousValue();
        }

    } else {
        mRestoringPreviousValueFlag = false;
    }
}

private void restorePreviousValue() {
    mEditText.setText(mPreviousValue);
    mEditText.setSelection(mCursorPosition);
}

private boolean isValid(String s) {
    Pattern patternWithDot = Pattern.compile("[0-9]*((\\.[0-9]{0," + mDigitsAfterZero + "})?)||(\\.)?");
    Pattern patternWithComma = Pattern.compile("[0-9]*((,[0-9]{0," + mDigitsAfterZero + "})?)||(,)?");

    Matcher matcherDot = patternWithDot.matcher(s);
    Matcher matcherComa = patternWithComma.matcher(s);

    return matcherDot.matches() || matcherComa.matches();
}
}

Usage:

myTextEdit.addTextChangedListener(new DecimalInputTextWatcher(myTextEdit, 2));
Dominik Suszczewicz
  • 1,469
  • 2
  • 16
  • 23
11

The simplest way to achieve that is:

et.addTextChangedListener(new TextWatcher() {
    public void onTextChanged(CharSequence arg0, int arg1, int arg2, int arg3) {
        String text = arg0.toString();
        if (text.contains(".") && text.substring(text.indexOf(".") + 1).length() > 2) {
            et.setText(text.substring(0, text.length() - 1));
            et.setSelection(et.getText().length());
        }
    }

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

    }

    public void afterTextChanged(Editable arg0) {
    }
});
Cody Gray - on strike
  • 239,200
  • 50
  • 490
  • 574
Sasa Ilic
  • 111
  • 1
  • 2
8

I have modified the above solutions and created following one. You can set number of digits before and after decimal point.

public class DecimalDigitsInputFilter implements InputFilter {

private final Pattern mPattern;

public DecimalDigitsInputFilter(int digitsBeforeZero, int digitsAfterZero) {
    mPattern = Pattern.compile(String.format("[0-9]{0,%d}(\\.[0-9]{0,%d})?", digitsBeforeZero, digitsAfterZero));
}

@Override
public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {
    Matcher matcher = mPattern.matcher(createResultString(source, start, end, dest, dstart, dend));
    if (!matcher.matches())
        return "";
    return null;
}

private String createResultString(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {
    String sourceString = source.toString();
    String destString = dest.toString();
    return destString.substring(0, dstart) + sourceString.substring(start, end) + destString.substring(dend);
}

}

tmorcinek
  • 93
  • 1
  • 4
6

Slightly improved @Pinhassi solution.

Works very well. It validates concatenated strings.

public class DecimalDigitsInputFilter implements InputFilter {

Pattern mPattern;

public DecimalDigitsInputFilter() {
    mPattern = Pattern.compile("([1-9]{1}[0-9]{0,2}([0-9]{3})*(\\.[0-9]{0,2})?|[1-9]{1}[0-9]{0,}(\\.[0-9]{0,2})?|0(\\.[0-9]{0,2})?|(\\.[0-9]{1,2})?)");

}

@Override
public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {

    String formatedSource = source.subSequence(start, end).toString();

    String destPrefix = dest.subSequence(0, dstart).toString();

    String destSuffix = dest.subSequence(dend, dest.length()).toString();

    String result = destPrefix + formatedSource + destSuffix;

    result = result.replace(",", ".");

    Matcher matcher = mPattern.matcher(result);

    if (matcher.matches()) {
        return null;
    }

    return "";
}

 }
pixel
  • 24,905
  • 36
  • 149
  • 251
6

Create a new class in Android kotlin with the name DecimalDigitsInputFilter

class DecimalDigitsInputFilter(digitsBeforeDecimal: Int, digitsAfterDecimal: Int) : InputFilter {

    var mPattern: Pattern = Pattern.compile("[0-9]{0,$digitsBeforeDecimal}+((\\.[0-9]{0,$digitsAfterDecimal})?)||(\\.)?")

    override fun filter(
        source: CharSequence?,
        start: Int,
        end: Int,
        dest: Spanned?,
        dstart: Int,
        dend: Int
    ): CharSequence? {
        val matcher: Matcher = mPattern.matcher(
            dest?.subSequence(0, dstart).toString() + source?.subSequence(
                start,
                end
            ).toString() + dest?.subSequence(dend, dest.length).toString()
        )
        if (!matcher.matches())
            return ""
        else
            return null
    }
}

Call this class with the following line

 et_buy_amount.filters = (arrayOf<InputFilter>(DecimalDigitsInputFilter(8,2)))

there are too many answers for the same but it will allow you to enter 8 digit before decimal and 2 digits after decimal

other answers are accepting only 8 digits

F.Mysir
  • 2,838
  • 28
  • 39
Sagar Bandamwar
  • 227
  • 2
  • 12
  • Come on Android SDK. It's 2021 and this much of work needed to limit decimal points. The scary thing is Android devs no longer even realize that they are doing too much of work to get this done. – HBB20 Feb 25 '21 at 23:43
5
DecimalFormat form = new DecimalFormat("#.##", new DecimalFormatSymbols(Locale.US));
    EditText et; 
    et.setOnEditorActionListener(new TextView.OnEditorActionListener() {
        @Override
        public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {

        if (actionId == EditorInfo.IME_ACTION_DONE) {
            double a = Double.parseDouble(et.getText().toString());
            et.setText(form.format(a));
        }
        return false;
    }
});

What this does is when you exit editing phase it formats the field to the right format. At them moment it has only 2 decimal charachters. I think this is pretty easy way to do this.

Jake1164
  • 12,291
  • 6
  • 47
  • 64
Sipka
  • 2,291
  • 2
  • 27
  • 30
5

I really liked Pinhassi's answer, but noticed that after the user had entered the specified number digits after the decimal point you could no longer enter text to the left side of the decimal point. The problem was that the solution only tested the previous text that had been entered, not the current text being entered. So here is my solution that inserts the new character into the original text for validation.

package com.test.test;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import android.text.InputFilter;
import android.text.Spanned;
import android.util.Log;

public class InputFilterCurrency implements InputFilter {
    Pattern moPattern;

    public InputFilterCurrency(int aiMinorUnits) {
        // http://www.regexplanet.com/advanced/java/index.html
        moPattern=Pattern.compile("[0-9]*+((\\.[0-9]{0,"+ aiMinorUnits + "})?)||(\\.)?");

    } // InputFilterCurrency

    @Override
    public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {
        String lsStart  = "";
        String lsInsert = "";
        String lsEnd    = "";
        String lsText   = "";

        Log.d("debug", moPattern.toString());
        Log.d("debug", "source: " + source + ", start: " + start + ", end:" + end + ", dest: " + dest + ", dstart: " + dstart + ", dend: " + dend );

        lsText = dest.toString();

        // If the length is greater then 0, then insert the new character
        // into the original text for validation
        if (lsText.length() > 0) {

            lsStart = lsText.substring(0, dstart);
            Log.d("debug", "lsStart : " + lsStart);
            // Check to see if they have deleted a character
            if (source != "") {
                lsInsert = source.toString();
                Log.d("debug", "lsInsert: " + lsInsert);
            } // if
            lsEnd = lsText.substring(dend);
            Log.d("debug", "lsEnd   : " + lsEnd);
            lsText = lsStart + lsInsert + lsEnd;
            Log.d("debug", "lsText  : " + lsText);

        } // if

        Matcher loMatcher = moPattern.matcher(lsText);
        Log.d("debug", "loMatcher.matches(): " + loMatcher.matches() + ", lsText: " + lsText);
        if(!loMatcher.matches()) {
            return "";
        }
        return null;

    } // CharSequence

} // InputFilterCurrency

And the call to set the editText filter

editText.setFilters(new InputFilter[] {new InputFilterCurrency(2)});

Ouput with two decimal places
05-22 15:25:33.434: D/debug(30524): [0-9]*+((\.[0-9]{0,2})?)||(\.)?
05-22 15:25:33.434: D/debug(30524): source: 5, start: 0, end:1, dest: 123.4, dstart: 5, dend: 5
05-22 15:25:33.434: D/debug(30524): lsStart : 123.4
05-22 15:25:33.434: D/debug(30524): lsInsert: 5
05-22 15:25:33.434: D/debug(30524): lsEnd   : 
05-22 15:25:33.434: D/debug(30524): lsText  : 123.45
05-22 15:25:33.434: D/debug(30524): loMatcher.matches(): true, lsText: 123.45

Ouput inserting a 5 in the middle
05-22 15:26:17.624: D/debug(30524): [0-9]*+((\.[0-9]{0,2})?)||(\.)?
05-22 15:26:17.624: D/debug(30524): source: 5, start: 0, end:1, dest: 123.45, dstart: 2, dend: 2
05-22 15:26:17.624: D/debug(30524): lsStart : 12
05-22 15:26:17.624: D/debug(30524): lsInsert: 5
05-22 15:26:17.624: D/debug(30524): lsEnd   : 3.45
05-22 15:26:17.624: D/debug(30524): lsText  : 1253.45
05-22 15:26:17.624: D/debug(30524): loMatcher.matches(): true, lsText: 1253.45
David Murry
  • 341
  • 3
  • 7
5

I improved on the solution that uses a regex by Pinhassi so it also handles the edge cases correctly. Before checking if the input is correct, first the final string is constructed as described by the android docs.

public class DecimalDigitsInputFilter implements InputFilter {

    private Pattern mPattern;

    private static final Pattern mFormatPattern = Pattern.compile("\\d+\\.\\d+");

    public DecimalDigitsInputFilter(int digitsBeforeDecimal, int digitsAfterDecimal) {
        mPattern = Pattern.compile(
            "^\\d{0," + digitsBeforeDecimal + "}([\\.,](\\d{0," + digitsAfterDecimal +
                "})?)?$");
    }

    @Override
    public CharSequence filter(CharSequence source, int start, int end, Spanned dest, 
                               int dstart, int dend) {

        String newString =
            dest.toString().substring(0, dstart) + source.toString().substring(start, end) 
            + dest.toString().substring(dend, dest.toString().length());

        Matcher matcher = mPattern.matcher(newString);
        if (!matcher.matches()) {
            return "";
        }
        return null;
    }
}

Usage:

editText.setFilters(new InputFilter[] {new DecimalDigitsInputFilter(5,2)});
reisub
  • 104
  • 2
  • 8
5

I have changed answer №6 (by Favas Kv) because there You can put just point in the first position.

final InputFilter [] filter = { new InputFilter() {

    @Override
    public CharSequence filter(CharSequence source, int start, int end,
                               Spanned dest, int dstart, int dend) {
        StringBuilder builder = new StringBuilder(dest);
        builder.replace(dstart, dend, source
                .subSequence(start, end).toString());
        if (!builder.toString().matches(
                "(([1-9]{1})([0-9]{0,4})?(\\.)?)?([0-9]{0,2})?"

        )) {
            if(source.length()==0)
                return dest.subSequence(dstart, dend);
            return "";
        }
        return null;
    }
}};
Cody Gray - on strike
  • 239,200
  • 50
  • 490
  • 574
koa73
  • 861
  • 2
  • 10
  • 27
5

Try using NumberFormat.getCurrencyInstance() to format your string before you put it into a TextView.

Something like:

NumberFormat currency = NumberFormat.getCurrencyInstance();
myTextView.setText(currency.format(dollars));

Edit - There is no inputType for currency that I could find in the docs. I imagine this is because there are some currencies that don't follow the same rule for decimal places, such as the Japanese Yen.

As LeffelMania mentioned, you can correct user input by using the above code with a TextWatcher that is set on your EditText.

Matthew
  • 44,826
  • 10
  • 98
  • 87
4

All answers here are pretty complex I tried to make it much simpler.Look at my code and decide for yourself -

int temp  = 0;
int check = 0;

editText.addTextChangedListener(new TextWatcher() {

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

        if(editText.getText().toString().length()<temp)
        {
            if(!editText.getText().toString().contains("."))
                editText.setFilters(new InputFilter[] { new InputFilter.LengthFilter(editText.getText().toString().length()-1) });
            else
                editText.setFilters(new InputFilter[] { new InputFilter.LengthFilter(editText.getText().toString().length()+1) });

        }

        if(!editText.getText().toString().contains("."))
        {
            editText.setFilters(new InputFilter[] { new InputFilter.LengthFilter(editText.getText().toString().length()+1) });
            check=0;
        }


        else if(check==0)
        {
            check=1;
            editText.setFilters(new InputFilter[] { new InputFilter.LengthFilter(editText.getText().toString().length()+2) });
        }
    }

    @Override
    public void beforeTextChanged(CharSequence s, int start, int count,
            int after) {
        temp = editText.getText().toString().length();


    }

    @Override
    public void afterTextChanged(Editable s) {
        // TODO Auto-generated method stub

    }
});
mihirjoshi
  • 12,161
  • 7
  • 47
  • 78
4

Simple Helper class is here to prevent the user entering more than 2 digits after decimal :

public class CostFormatter  implements TextWatcher {

private final EditText costEditText;

public CostFormatter(EditText costEditText) {
    this.costEditText = costEditText;
}

@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}

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

@Override
public synchronized void afterTextChanged(final Editable text) {
    String cost = text.toString().trim();

    if(!cost.endsWith(".") && cost.contains(".")){
        String numberBeforeDecimal = cost.split("\\.")[0];
        String numberAfterDecimal = cost.split("\\.")[1];

        if(numberAfterDecimal.length() > 2){
            numberAfterDecimal = numberAfterDecimal.substring(0, 2);
        }
        cost = numberBeforeDecimal + "." + numberAfterDecimal;
    }
    costEditText.removeTextChangedListener(this);
    costEditText.setText(cost);
    costEditText.setSelection(costEditText.getText().toString().trim().length());
    costEditText.addTextChangedListener(this);
}
}
Santhosh
  • 4,956
  • 12
  • 62
  • 90
4

Simple BindingAdapter in Kotlin:

@BindingAdapter("maxDecimalPlaces")
fun TextInputEditText.limitDecimalPlaces(maxDecimalPlaces: Int) {
    filters += InputFilter { source, _, _, dest, dstart, dend ->
        val value = if (source.isEmpty()) {
            dest.removeRange(dstart, dend)
        } else {
            StringBuilder(dest).insert(dstart, source)
        }
        val matcher = Pattern.compile("([1-9][0-9]*)|([1-9][0-9]*\\.[0-9]{0,$maxDecimalPlaces})|(\\.[0-9]{0,$maxDecimalPlaces})").matcher(value)
        if (!matcher.matches()) "" else null
    }
}
Marek
  • 201
  • 2
  • 7
3

Like others said, I added this class in my project and set the filter to the EditText I want.

The filter is copied from @Pixel's answer. I'm just putting it all together.

public class DecimalDigitsInputFilter implements InputFilter {

    Pattern mPattern;

    public DecimalDigitsInputFilter() {
        mPattern = Pattern.compile("([1-9]{1}[0-9]{0,2}([0-9]{3})*(\\.[0-9]{0,2})?|[1-9]{1}[0-9]{0,}(\\.[0-9]{0,2})?|0(\\.[0-9]{0,2})?|(\\.[0-9]{1,2})?)");

    }

    @Override
    public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {

        String formatedSource = source.subSequence(start, end).toString();

        String destPrefix = dest.subSequence(0, dstart).toString();

        String destSuffix = dest.subSequence(dend, dest.length()).toString();

        String result = destPrefix + formatedSource + destSuffix;

        result = result.replace(",", ".");

        Matcher matcher = mPattern.matcher(result);

        if (matcher.matches()) {
            return null;
        }

        return "";
    }
}

Now set the filter in your EditText like this.

mEditText.setFilters(new InputFilter[]{new DecimalDigitsInputFilter()});

Here one important thing is it does solves my problem of not allowing showing more than two digits after the decimal point in that EditText but the problem is when I getText() from that EditText, it returns the whole input I typed.

For example, after applying the filter over the EditText, I tried to set input 1.5699856987. So in the screen it shows 1.56 which is perfect.

Then I wanted to use this input for some other calculations so I wanted to get the text from that input field (EditText). When I called mEditText.getText().toString() it returns 1.5699856987 which was not acceptable in my case.

So I had to parse the value again after getting it from the EditText.

BigDecimal amount = new BigDecimal(Double.parseDouble(mEditText.getText().toString().trim()))
    .setScale(2, RoundingMode.HALF_UP);

setScale does the trick here after getting the full text from the EditText.

Reaz Murshed
  • 23,691
  • 13
  • 78
  • 98
  • Hello there, how can i make sure if user doesn't input decimal(.) he shouldn't be able to enter more than 2 digits? – ManishNegi Jul 18 '17 at 07:26
2

Here is my solution:

     yourEditText.addTextChangedListener(new TextWatcher() {
        @Override
        public void onTextChanged(CharSequence s, int start, int before, int count) {
            NumberFormat formatter = new DecimalFormat("#.##");
            double doubleVal = Double.parseDouble(s.toString());
            yourEditText.setText(formatter.format(doubleVal));
        }

        @Override
        public void beforeTextChanged(CharSequence s, int start, int count,int after) {}

        @Override
        public void afterTextChanged(Editable s) {}
    });

If the user enters a number with more than two numbers after the decimal point, it will be automatically corrected.

I hope I have helped!

lopez.mikhael
  • 9,943
  • 19
  • 67
  • 110
  • Did you test this code? It can't really work, because whenever you call setText() the TextWatcher fires again => infinite loop. – muetzenflo Dec 19 '13 at 14:26
  • 06-07 08:01:35.006: E/AndroidRuntime(30230): java.lang.StackOverflowError Not working – Anjula Jun 07 '14 at 02:34
2

I've also came across this problem. I wanted to be able to reuse the code in many EditTexts. This is my solution:

Usage :

CurrencyFormat watcher = new CurrencyFormat();
priceEditText.addTextChangedListener(watcher);

Class:

public static class CurrencyFormat implements TextWatcher {

    public void onTextChanged(CharSequence arg0, int start, int arg2,int arg3) {}

    public void beforeTextChanged(CharSequence arg0, int start,int arg2, int arg3) {}

    public void afterTextChanged(Editable arg0) {
        int length = arg0.length();
        if(length>0){
            if(nrOfDecimal(arg0.toString())>2)
                    arg0.delete(length-1, length);
        }

    }


    private int nrOfDecimal(String nr){
        int len = nr.length();
        int pos = len;
        for(int i=0 ; i<len; i++){
            if(nr.charAt(i)=='.'){
                pos=i+1;
                    break;
            }
        }
        return len-pos;
    }
}
Tom
  • 408
  • 1
  • 3
  • 7
2

@Meh for u..

txtlist.setFilters(new InputFilter[] { new DigitsKeyListener( Boolean.FALSE,Boolean.TRUE) {

        int beforeDecimal = 7;
        int afterDecimal = 2;

        @Override
        public CharSequence filter(CharSequence source, int start, int end,Spanned dest, int dstart, int dend) {

            String etText = txtlist.getText().toString();
            String temp = txtlist.getText() + source.toString();
            if (temp.equals(".")) {
                return "0.";
            } else if (temp.toString().indexOf(".") == -1) {
                // no decimal point placed yet
                 if (temp.length() > beforeDecimal) {
                    return "";
                }
            } else {
                int dotPosition ;
                int cursorPositon = txtlistprice.getSelectionStart();
                if (etText.indexOf(".") == -1) {
                    dotPosition = temp.indexOf(".");
                }else{
                    dotPosition = etText.indexOf(".");
                }
                if(cursorPositon <= dotPosition){
                    String beforeDot = etText.substring(0, dotPosition);
                    if(beforeDot.length()<beforeDecimal){
                        return source;
                    }else{
                        if(source.toString().equalsIgnoreCase(".")){
                            return source;
                        }else{
                            return "";
                        }
                    }
                }else{
                    temp = temp.substring(temp.indexOf(".") + 1);
                    if (temp.length() > afterDecimal) {
                        return "";
                    }
                }
            }
            return super.filter(source, start, end, dest, dstart, dend);
        }
    } });
RaRa
  • 2,024
  • 1
  • 18
  • 29
2

Here is the TextWatcher that allow only n number of digits after decimal point.

TextWatcher

private static boolean flag;
public static TextWatcher getTextWatcherAllowAfterDeci(final int allowAfterDecimal){

    TextWatcher watcher = new TextWatcher() {

        @Override
        public void onTextChanged(CharSequence s, int start, int before, int count) {
            // TODO Auto-generated method stub

        }

        @Override
        public void beforeTextChanged(CharSequence s, int start, int count,
                int after) {
            // TODO Auto-generated method stub

        }

        @Override
        public void afterTextChanged(Editable s) {
            // TODO Auto-generated method stub
            String str = s.toString();
            int index = str.indexOf ( "." );
            if(index>=0){
                if((index+1)<str.length()){
                    String numberD = str.substring(index+1);
                    if (numberD.length()!=allowAfterDecimal) {
                        flag=true;
                    }else{
                        flag=false;
                    }   
                }else{
                    flag = false;
                }                   
            }else{
                flag=false;
            }
            if(flag)
                s.delete(s.length() - 1,
                        s.length());
        }
    };
    return watcher;
}

How to use

yourEditText.addTextChangedListener(getTextWatcherAllowAfterDeci(1));
Cody Gray - on strike
  • 239,200
  • 50
  • 490
  • 574
Hiren Dabhi
  • 3,693
  • 5
  • 37
  • 59
2

A very late response: We can do it simply like this:

etv.addTextChangedListener(new TextWatcher() {
        @Override
        public void beforeTextChanged(CharSequence s, int start, int count, int after) {
        }

        @Override
        public void onTextChanged(CharSequence s, int start, int before, int count) {
            if (s.toString().length() > 3 && s.toString().contains(".")) {
                if (s.toString().length() - s.toString().indexOf(".") > 3) {
                    etv.setText(s.toString().substring(0, s.length() - 1));
                    etv.setSelection(edtSendMoney.getText().length());
                }
            }
        }

        @Override
        public void afterTextChanged(Editable arg0) {
        }
}
Navinpd
  • 772
  • 7
  • 15
1
et = (EditText) vw.findViewById(R.id.tx_edittext);

et.setFilters(new InputFilter[] {
        new DigitsKeyListener(Boolean.FALSE, Boolean.TRUE) {
            int beforeDecimal = 5, afterDecimal = 2;

            @Override
            public CharSequence filter(CharSequence source, int start, int end,
                    Spanned dest, int dstart, int dend) {
                String temp = et.getText() + source.toString();

                if (temp.equals(".")) {
                    return "0.";
                }
                else if (temp.toString().indexOf(".") == -1) {
                    // no decimal point placed yet
                    if (temp.length() > beforeDecimal) {
                        return "";
                    }
                } else {
                    temp = temp.substring(temp.indexOf(".") + 1);
                    if (temp.length() > afterDecimal) {
                        return "";
                    }
                }

                return super.filter(source, start, end, dest, dstart, dend);
            }
        }
});
Cody Gray - on strike
  • 239,200
  • 50
  • 490
  • 574
  • About 2 years from your answer time. I tried to use your code then i found the problem about your solution is you append `source` after `et.getText()`. It always understand that people type at the end of the box instead of begin of the box. `StringBuilder stringBuilder = new StringBuilder(text.getText().toString()); stringBuilder.replace(dstart, dend, source.toString()); String temp = stringBuilder.toString();` should work. Thanks anyway. – Harry T. Jul 13 '17 at 02:16
1

This works fine for me. It allows value to be entered even after focus changed and retrieved back. For example: 123.00, 12.12, 0.01, etc..

1.Integer.parseInt(getString(R.string.valuelength)) Specifies the length of the input digits.Values accessed from string.xml file.It is quiet easy to change values. 2.Integer.parseInt(getString(R.string.valuedecimal)), this is for decimal places max limit.

private InputFilter[] valDecimalPlaces;
private ArrayList<EditText> edittextArray;

valDecimalPlaces = new InputFilter[] { new DecimalDigitsInputFilterNew(
    Integer.parseInt(getString(R.string.valuelength)),
    Integer.parseInt(getString(R.string.valuedecimal))) 
};

Array of EditText values that allows to perform action.

for (EditText etDecimalPlace : edittextArray) {
            etDecimalPlace.setFilters(valDecimalPlaces);

I just used array of values that contain multiple edittext Next DecimalDigitsInputFilterNew.class file.

import android.text.InputFilter;
import android.text.Spanned;

public class DecimalDigitsInputFilterNew implements InputFilter {

    private final int decimalDigits;
    private final int before;

    public DecimalDigitsInputFilterNew(int before ,int decimalDigits) {
        this.decimalDigits = decimalDigits;
        this.before = before;
    }

    @Override
    public CharSequence filter(CharSequence source, int start, int end,
        Spanned dest, int dstart, int dend) {
        StringBuilder builder = new StringBuilder(dest);
        builder.replace(dstart, dend, source
              .subSequence(start, end).toString());
        if (!builder.toString().matches("(([0-9]{1})([0-9]{0,"+(before-1)+"})?)?(\\.[0-9]{0,"+decimalDigits+"})?")) {
             if(source.length()==0)
                  return dest.subSequence(dstart, dend);
             return "";
        }
        return null;
    }
}
Schemetrical
  • 5,506
  • 2
  • 26
  • 43
1

This is to build on pinhassi's answer - the issue that I came across was that you couldn't add values before the decimal once the decimal limit has been reached. To fix the issue, we need to construct the final string before doing the pattern match.

import java.util.regex.Matcher;
import java.util.regex.Pattern;

import android.text.InputFilter;
import android.text.Spanned;

public class DecimalLimiter implements InputFilter
{
    Pattern mPattern;

    public DecimalLimiter(int digitsBeforeZero,int digitsAfterZero) 
    {
        mPattern=Pattern.compile("[0-9]{0," + (digitsBeforeZero) + "}+((\\.[0-9]{0," + (digitsAfterZero) + "})?)||(\\.)?");
    }

    @Override
    public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) 
    {
        StringBuilder sb = new StringBuilder(dest);
        sb.insert(dstart, source, start, end);

        Matcher matcher = mPattern.matcher(sb.toString());
        if(!matcher.matches())
            return "";
        return null;
    }
}
1

This code works well,

public class DecimalDigitsInputFilter implements InputFilter {

    private final int digitsBeforeZero;
    private final int digitsAfterZero;
    private Pattern mPattern;

    public DecimalDigitsInputFilter(int digitsBeforeZero, int digitsAfterZero) {
        this.digitsBeforeZero = digitsBeforeZero;
        this.digitsAfterZero = digitsAfterZero;
        applyPattern(digitsBeforeZero, digitsAfterZero);
    }

    private void applyPattern(int digitsBeforeZero, int digitsAfterZero) {
        mPattern = Pattern.compile("[0-9]{0," + (digitsBeforeZero - 1) + "}+((\\.[0-9]{0," + (digitsAfterZero - 1) + "})?)|(\\.)?");
    }

    @Override
    public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {
        if (dest.toString().contains(".") || source.toString().contains("."))
            applyPattern(digitsBeforeZero + 2, digitsAfterZero);
        else
            applyPattern(digitsBeforeZero, digitsAfterZero);

        Matcher matcher = mPattern.matcher(dest);
        if (!matcher.matches())
            return "";
        return null;
    }

}

applying filter:

edittext.setFilters(new InputFilter[] {new DecimalDigitsInputFilter(5,2)});
Karthik Kompelli
  • 2,104
  • 1
  • 19
  • 22
1

My simple solution without regex

    int start=Edit1.getSelectionStart();
    String sp=Edit1.getText().toString();
    sp=sp.replace(",",".");
    Double d=Double.valueOf(sp);
    String s=String.format("%.2f",d );
    if(!Edit1.getText().toString().equals(s))
        Edit1.setText(s);
    if(start>Edit1.getText().length())start--;
    Edit1.setSelection(start);

putting into onTextChange. By deleting the comma, the zeros double because the number becomes an integer.

Paolo
  • 43
  • 2
0
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
    String numero = total.getText().toString();
    int dec = numero.indexOf(".");
    int longitud = numero.length();
    if (dec+3 == longitud && dec != -1) { //3 number decimal + 1
        log.i("ento","si");
        numero = numero.substring(0,dec+3);
        if (contador == 0) {
            contador = 1;
            total.setText(numero);
            total.setSelection(numero.length());
        } else {
            contador = 0;
        }
    }
}
Cody Gray - on strike
  • 239,200
  • 50
  • 490
  • 574
0

Like others said, I added this class in my project and set the filter to the EditText Simpler solution without using regex:

public class DecimalDigitsInputFilter implements InputFilter {
int digitsBeforeZero =0;
int digitsAfterZero=0;

public DecimalDigitsInputFilter(int digitsBeforeZero,int digitsAfterZero) {
this.digitsBeforeZero=digitsBeforeZero;
this.digitsAfterZero=digitsAfterZero;
}

@Override
public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {
if(dest!=null && dest.toString().trim().length()<(digitsBeforeZero+digitsAfterZero)){
    String value=dest.toString().trim();
    if(value.contains(".") && (value.substring(value.indexOf(".")).length()<(digitsAfterZero+1))){
        return ((value.indexOf(".")+1+digitsAfterZero)>dstart)?null:"";
    }else if(value.contains(".") && (value.indexOf(".")<dstart)){
        return "";
    }else if(source!=null && source.equals(".")&& ((value.length()-dstart)>=(digitsAfterZero+1))){
        return "";
    }

}else{
    return "";
}
    return null;
}

}

applying filter:

edittext.setFilters(new InputFilter[] {new DecimalDigitsInputFilter(5,2)});
saravanan
  • 1,082
  • 1
  • 15
  • 30
0

This implementation of InputFilter solves the problem.

import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.method.DigitsKeyListener;

public class MoneyValueFilter extends DigitsKeyListener {
    public MoneyValueFilter() {
        super(false, true);
    }

    private int digits = 2;

    public void setDigits(int d) {
        digits = d;
    }

    @Override
    public CharSequence filter(CharSequence source, int start, int end,
            Spanned dest, int dstart, int dend) {
        CharSequence out = super.filter(source, start, end, dest, dstart, dend);

        // if changed, replace the source
        if (out != null) {
            source = out;
            start = 0;
            end = out.length();
        }

        int len = end - start;

        // if deleting, source is empty
        // and deleting can't break anything
        if (len == 0) {
            return source;
        }

        int dlen = dest.length();

        // Find the position of the decimal .
        for (int i = 0; i < dstart; i++) {
            if (dest.charAt(i) == '.') {
                // being here means, that a number has
                // been inserted after the dot
                // check if the amount of digits is right
                return (dlen-(i+1) + len > digits) ? 
                    "" :
                    new SpannableStringBuilder(source, start, end);
            }
        }

        for (int i = start; i < end; ++i) {
            if (source.charAt(i) == '.') {
                // being here means, dot has been inserted
                // check if the amount of digits is right
                if ((dlen-dend) + (end-(i + 1)) > digits)
                    return "";
                else
                    break;  // return new SpannableStringBuilder(source, start, end);
            }
        }

        // if the dot is after the inserted part,
        // nothing can break
        return new SpannableStringBuilder(source, start, end);
    }
}

To use:

editCoin.setFilters(new InputFilter[] {new MoneyValueFilter(2)});
David Buck
  • 3,752
  • 35
  • 31
  • 35
Bao Bao
  • 135
  • 1
  • 7
0

If you want to have restrictions on integer part also here is the code

class PropertyCostInputFilter : DigitsKeyListener(false, true) {

override fun filter(source: CharSequence, start: Int, end: Int, dest: Spanned, dstart: Int, dend: Int): CharSequence {
    var source = source
    var start = start
    var end = end
    val out = super.filter(source, start, end, dest, dstart, dend)

    if (out != null) {
        source = out
        start = 0
        end = out.length
    }

    val sourceLength = end - start

    // If length = 0, then there was a deletion and therefore the length could not become greater than the max value
    if (sourceLength == 0) {
        return source
    }

    val result = dest.replaceRange((dstart until dend), source.substring(start, end))
    val parts = result.split(SEPARATOR)

    if (parts.size > 0 && parts[0].length > INTEGER_PART_MAX_DIGITS
        || parts.size > 1 && parts[1].length > FRACTIONAL_PART_MAX_DIGITS
    ) {
        return ""
    }

    return SpannableStringBuilder(source, start, end)
}

companion object {
    private const val INTEGER_PART_MAX_DIGITS = 20
    private const val FRACTIONAL_PART_MAX_DIGITS = 2
    private const val SEPARATOR = '.'
}
}
0

if you have restriction like maxDigitsBeforeDecimalPoint or maxDigitsAfterDecimalPoint then you can use below class.

public class DecimalDigitsInputFilter implements InputFilter {
@Override
public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {
    StringBuilder builder = new StringBuilder(dest);
    int maxDigitsBeforeDecimalPoint = 6;
    int maxDigitsAfterDecimalPoint = 2;
    builder.replace(dstart, dend, source
            .subSequence(start, end).toString());
    if (!builder.toString().matches(
            "(([1-9]{1})([0-9]{0," + (maxDigitsBeforeDecimalPoint - 1) + "})?)?(\\.[0-9]{0," + maxDigitsAfterDecimalPoint + "})?"

    )) {
        if (source.length() == 0)
            return dest.subSequence(dstart, dend);
        return "";
    }

    return null;
}

}

for me I have required max 6 digit before decimal point and max 2 digit after decimal so I have declared variable like int maxDigitsBeforeDecimalPoint = 6; int maxDigitsAfterDecimalPoint = 2; you can use according your requirement.

Usage

editText.setFilters(new InputFilter[]{new DecimalDigitsInputFilter()});

Hope it will help you as well.

Kishan Thakkar
  • 619
  • 6
  • 11
-1

For Kotlin

val inputFilter =  arrayOf<InputFilter>(DecimalDigitsInputFilter(5,2))
            et_total_value.setFilters(inputFilter)
shubomb
  • 672
  • 7
  • 20
-5

This is the simplest solution to limit the number of digits after decimal point to two:

myeditText2 = (EditText) findViewById(R.id.editText2);  
myeditText2.setInputType(3);
Cody Gray - on strike
  • 239,200
  • 50
  • 490
  • 574
  • 2
    First, **don't** use [magic numbers](http://en.wikipedia.org/wiki/Magic_number_%28programming%29), use the human readable [symbolic constants](http://developer.android.com/reference/android/widget/TextView.html#attr_android:inputType) instead. Second, `setInputType(3)` [is for a phone number](http://developer.android.com/reference/android/text/InputType.html#TYPE_CLASS_PHONE). There is no `InputType` for fixed precision decimal numbers, unfortunately. – kdbanman Feb 01 '15 at 22:00