Limit Decimal Places in Android EditText

JavaAndroidAndroid Edittext

Java Problem Overview


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?

Java Solutions


Solution 1 - Java

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)});

Solution 2 - Java

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)});

Solution 3 - Java

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);
	}
}

Solution 4 - Java

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 });

Solution 5 - Java

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 "";
    }
}

Solution 6 - Java

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!

Solution 7 - Java

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.

Solution 8 - Java

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.

Solution 9 - Java

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)});

Solution 10 - Java

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));

Solution 11 - Java

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) {
	}
});

Solution 12 - Java

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.

Solution 13 - Java

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 "";
}

 }

Solution 14 - Java

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);
}

}

Solution 15 - Java

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.

Solution 16 - Java

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

Solution 17 - Java

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

Solution 18 - Java

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

	}
});

Solution 19 - Java

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)});

Solution 20 - Java

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);
}
}

Solution 21 - Java

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;
    }
}};

Solution 22 - Java

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.

Solution 23 - Java

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!

Solution 24 - Java

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;
	}
}

Solution 25 - Java

@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);
        }
    } });

Solution 26 - Java

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));

Solution 27 - Java

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) {
        }
}

Solution 28 - Java

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
    }
}

Solution 29 - Java

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);
            }
        }
});

Solution 30 - Java

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;
    }
}

Solution 31 - Java

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;
    }
}

Solution 32 - Java

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)});

Solution 33 - Java

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.

Solution 34 - Java

@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;
        }
    }
}

Solution 35 - Java

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)});

Solution 36 - Java

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)});

Solution 37 - Java

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 = '.'
}
}

Solution 38 - Java

For Kotlin

val inputFilter =  arrayOf<InputFilter>(DecimalDigitsInputFilter(5,2))
            et_total_value.setFilters(inputFilter)

Solution 39 - Java

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);

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionKonstantin WeitzView Question on Stackoverflow
Solution 1 - JavaAsaf PinhassiView Answer on Stackoverflow
Solution 2 - JavapecepsView Answer on Stackoverflow
Solution 3 - JavaKonstantin WeitzView Answer on Stackoverflow
Solution 4 - JavaFavas KvView Answer on Stackoverflow
Solution 5 - Javaandroid_devView Answer on Stackoverflow
Solution 6 - JavaApoleoView Answer on Stackoverflow
Solution 7 - JavaGunaseelanView Answer on Stackoverflow
Solution 8 - JavaMangeshView Answer on Stackoverflow
Solution 9 - JavaLukeView Answer on Stackoverflow
Solution 10 - JavaDominik SuszczewiczView Answer on Stackoverflow
Solution 11 - JavaSasa IlicView Answer on Stackoverflow
Solution 12 - JavaMatthew WillisView Answer on Stackoverflow
Solution 13 - JavapixelView Answer on Stackoverflow
Solution 14 - JavatmorcinekView Answer on Stackoverflow
Solution 15 - JavaSipkaView Answer on Stackoverflow
Solution 16 - JavaDavid MurryView Answer on Stackoverflow
Solution 17 - JavaSagar BandamwarView Answer on Stackoverflow
Solution 18 - JavamihirjoshiView Answer on Stackoverflow
Solution 19 - JavareisubView Answer on Stackoverflow
Solution 20 - JavaSanthoshView Answer on Stackoverflow
Solution 21 - Javakoa73View Answer on Stackoverflow
Solution 22 - JavaReaz MurshedView Answer on Stackoverflow
Solution 23 - Javalopez.mikhaelView Answer on Stackoverflow
Solution 24 - JavaTomView Answer on Stackoverflow
Solution 25 - JavaRaRaView Answer on Stackoverflow
Solution 26 - JavaHiren DabhiView Answer on Stackoverflow
Solution 27 - JavaNanPdView Answer on Stackoverflow
Solution 28 - JavaMarekView Answer on Stackoverflow
Solution 29 - JavaPawan ShuklaView Answer on Stackoverflow
Solution 30 - JavaKarthi KeyanView Answer on Stackoverflow
Solution 31 - JavaRobert ConleyView Answer on Stackoverflow
Solution 32 - JavasaravananView Answer on Stackoverflow
Solution 33 - JavaPaoloView Answer on Stackoverflow
Solution 34 - JavaJhony UrbanoView Answer on Stackoverflow
Solution 35 - JavaKarthik KompelliView Answer on Stackoverflow
Solution 36 - JavaBao BaoView Answer on Stackoverflow
Solution 37 - JavaБулат МухутдиновView Answer on Stackoverflow
Solution 38 - JavashubombView Answer on Stackoverflow
Solution 39 - JavaShukana789View Answer on Stackoverflow