Konstantin Weitz
Konstantin Weitz

Reputation: 6422

Limit Decimal Places in Android EditText

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?

Upvotes: 153

Views: 151653

Answers (30)

Kishan Thakkar
Kishan Thakkar

Reputation: 755

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.

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

Upvotes: 0

Marek
Marek

Reputation: 132

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

Upvotes: 4

Gunaseelan
Gunaseelan

Reputation: 15525

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.

Upvotes: 20

Sagar Bandamwar
Sagar Bandamwar

Reputation: 227

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

Upvotes: 6

Apoleo
Apoleo

Reputation: 2470

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!

Upvotes: 31

Bao Bao
Bao Bao

Reputation: 145

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

Upvotes: 0

shubomb
shubomb

Reputation: 832

For Kotlin

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

Upvotes: -1

Paolo
Paolo

Reputation: 43

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.

Upvotes: 1

saravanan
saravanan

Reputation: 1092

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

Upvotes: 0

Karthik Kompelli
Karthik Kompelli

Reputation: 2214

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

Upvotes: 2

Shukana789
Shukana789

Reputation: 1

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

Upvotes: -5

Jhony Urbano
Jhony Urbano

Reputation: 1

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

Upvotes: 0

koa73
koa73

Reputation: 881

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

Upvotes: 5

Sasa Ilic
Sasa Ilic

Reputation: 111

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

Upvotes: 11

Hiren Dabhi
Hiren Dabhi

Reputation: 3713

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

Upvotes: 2

Pawan Shukla
Pawan Shukla

Reputation: 41

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

Upvotes: 1

Luke
Luke

Reputation: 2225

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

Upvotes: 18

Reaz Murshed
Reaz Murshed

Reputation: 24211

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.

Upvotes: 3

Robert Conley
Robert Conley

Reputation: 182

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

Upvotes: 1

Navinpd
Navinpd

Reputation: 832

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

Upvotes: 2

Santhosh
Santhosh

Reputation: 5016

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

Upvotes: 4

tmorcinek
tmorcinek

Reputation: 93

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

}

Upvotes: 8

Mangesh
Mangesh

Reputation: 5841

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.

Upvotes: 16

Karthi Keyan
Karthi Keyan

Reputation: 21

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

Upvotes: 1

Favas Kv
Favas Kv

Reputation: 3079

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

Upvotes: 36

RaRa
RaRa

Reputation: 2044

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

Upvotes: 2

Dominik Suszczewicz
Dominik Suszczewicz

Reputation: 1499

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

Upvotes: 12

reisub
reisub

Reputation: 104

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

Upvotes: 5

android_dev
android_dev

Reputation: 4188

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

Upvotes: 31

Related Questions