Limite los lugares decimales en Android EditText

125

Estoy tratando de escribir una aplicación que te ayude a administrar tus finanzas. Estoy usando un EditTextcampo donde el usuario puede especificar una cantidad de dinero.

He establecido la inputTypea numberDecimalla que funciona bien, excepto que esto permite que la gente entre en números como 123.122que no es perfecto para el dinero.

¿Hay alguna forma de limitar el número de caracteres después del punto decimal a dos?

Konstantin Weitz
fuente
Puede escribir una expresión regular y verificar el contenido del texto de edición cuando pierde el foco.
blindstuff
Encontré la InputFilterinterfaz, parece hacer lo que quiero developer.android.com/reference/android/text/method/… , pero el método filterque tengo que implementar es bastante confuso para mí. ¿Alguien ya escribió dicho filtro y sabe cómo usarlo?
Konstantin Weitz
¿Alguna de las soluciones sugeridas funciona para entornos locales RTL? Por lo que puedo decir, no lo harán ...
Nick

Respuestas:

118

Una forma más elegante sería usar una expresión regular (regex) de la siguiente manera:

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

}

Para usarlo haz:

editText.setFilters(new InputFilter[] {new DecimalDigitsInputFilter(5,2)});
Asaf Pinhassi
fuente
32
Hola, hay algunos casos extremos que todavía no se manejan bien. Por ejemplo, después de escribir 2.45, tiendo a "mover el cursor al frente la mayor parte del texto". Deseo producir el texto 12.45, no me lo permitirá.
Cheok Yan Cheng
3
no permite cambiar dígitos antes del decimal después de que el usuario haya ingresado 2 dígitos después del punto decimal.
Gaurav Singla
9
Gran solución pero no es correcta por completo. El comparador no debe verificar dest, debe verificar el valor en el texto de edición (dest.subSequence (0, dstart) + source.subSequence (start, end) + dest.subSequence (dend, dest.length ()))
Mihaela Romanca
77
Mihaela tiene razón, deberíamos hacer coincidir la cadena que intenta llenar el texto de edición. Encontré cómo concatenar en otra pregunta como esta CharSequence match = TextUtils.concat (dest.subSequence (0, dstart), source.subSequence (start, end), dest.subSequence (dend, dest.length ())); Sin embargo, la expresión regular causó problemas después, así que la cambié a "^ \\ d {1," + digitsBeforeZero + "} (\\. \\ d {0," + digitsAfterZero + "})? $" Pero usted " También tendré que hacer la validación después porque "1." es válido con esa expresión regular, pero la necesitamos de esa manera para que se pueda escribir el período.
dt0
66
¿Cómo haría que esto funcionara también con comas (,)? Algunas regiones del mundo escriben números decimales con comas (por ejemplo, 123,45).
Andrew
65

Solución más simple sin usar expresiones regulares:

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

}

Usar:

editText.setFilters(new InputFilter[] {new DecimalDigitsInputFilter(2)});
peceps
fuente
¿Qué me impediría insertar caracteres no numéricos en la cadena como 'a'?
Konstantin Weitz
44
Esto: <EditText ... android: inputType = "number" />
acepta el
1
Eso debería ser: editText.setFilters (nuevo InputFilter [] {new DecimalDigitsInputFilter (2)});
frak
66
Esto no maneja el caso donde
escribo
1
Gracias esta útil. También podemos restringir los dígitos antes del punto decimal usando un filtro de longitud como este. Kotlin: edtAnyAmount.filters = arrayOf <InputFilter> (InputFilter.LengthFilter (7), DecimalDigitsInputFilter (2))
Faldu Jaldeep
37

Esta implementación de InputFilterresuelve el problema.

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
fuente
¿Puedo saber si hay alguna razón por la que necesitemos devolver SpannableStringBuilder en lugar de nulo? Lo pruebo con nulo, también funciona bien. Además, ¿hay alguna necesidad de que heredemos de DigitsKeyListener? Al usar Android: inputType = "numberDecimal" realizará todos los "0123456789". Aplicación de personajes.
Cheok Yan Cheng
1
Funciona bien. Muchas gracias.
Andrei Aulaska
34

Aquí hay una muestra de InputFilter que solo permite un máximo de 4 dígitos antes del punto decimal y un máximo de 1 dígito después de eso.

Valores que edittext permite: 555.2 , 555 , .2

Valores que editan bloques de texto: 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
fuente
no deja. para ser escrito
AmiNadimi
22

Hice algunas correcciones para la solución @Pinhassi. Maneja algunos casos:

1.puedes mover el cursor a cualquier parte

Manejo de signos 2.minus

3.digitsbefore = 2 y digitsafter = 4 e ingresa 12.4545. Entonces, si desea eliminar ".", No lo permitirá.

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
fuente
Creo que debería ser la solución perfecta, me alegró el día. Gracias.
Pratik Butani
1
@ Omkar esto está mal. esta condición siempre será verdadera incluso si length> 0, dest.length () == 0 siempre es cierto incluso si edita el texto completo de texto más de 0 ...
user924
@Omkar elimina tu comentario por favor
user924
@android_dev ¿por qué no puedo escribir valores negativos (menos)?
usuario924
SI configura android:inputType="number"o android:inputType="numberDecimal"no permitirá escribir menos, esto android:digits="0123456789.-"no ayuda
usuario924
18

No me gusta la otra solución y creé la mía. Con esta solución, no puede ingresar más de MAX_BEFORE_POINT dígitos antes del punto y los decimales no pueden ser más de MAX_DECIMAL.

Simplemente no puede escribir el dígito en exceso, ¡no hay otros efectos! En adicional si escribe "." escribe "0"

  1. Establezca EditText en el diseño para:

    android: inputType = "numberDecimal"

  2. Agregue el oyente en su onCreate. Si desea modificar el número de dígitos antes y después del punto, edite la llamada a PerfectDecimal (str, NUMBER_BEFORE_POINT, NUMBER_DECIMALS), aquí se establece en 3 y 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);
            int pos = targetEditText.getText().length();
            targetEditText.setSelection(pos);
        }
      }
    });
  3. Incluye esta función:

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

¡Y está hecho!

Apoleo
fuente
1
Salud. Trabaja muy bien cuando el otro no funcionó para mí
Bear
1
Esta debería ser la respuesta aceptada. Es perfecta. Todas las condiciones se cumplen aquí.
Nayan
1
Sobre todas las respuestas altamente votadas, esta respuesta realmente ha funcionado para mí.
Rohit Mandiwal
¡Bien hecho! Intenté todas las combinaciones y parece que funciona muy bien. Gracias.
akelec
No sé cómo funciona, pero funciona de maravilla.
wonsuc
17

Lo logré con la ayuda de TextWatcherla siguiente manera

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

Esta solución no permitirá que el usuario ingrese más de dos dígitos después del punto decimal. También puede ingresar cualquier número de dígitos antes del punto decimal. Vea este blog http://v4all123.blogspot.com/2013/05/set-limit-for-fraction-in-decimal.html para establecer el filtro para múltiples EditText. Espero que esto sea de ayuda. Gracias.

Gunaseelan
fuente
Perdón por la información tardía. NO olvides inicializar countcon -1. Entonces solo esto funcionará correctamente. int count = -1;
Gunaseelan
Gunaseelan: probé el código anterior y funciona bien. Pero cuando elimino el texto escrito y empiezo a escribir nuevamente, solo escribe un dígito, cualquier solución para esto .....
Siva K
@SivaK De ninguna manera amigo. Si elimina y luego escribe, aceptará un mínimo de 100 dígitos. No sé cómo accedes a esto listener. De cualquier manera por favor, eche un vistazo mi blog un mensaje . Puedes tener una idea. Si no puede, hágamelo saber. Te ayudaré con respecto a este tema.
Gunaseelan
He verificado lo que ha dicho @SivaK. Esto es inteligente en cualquier caso, pero voy a hacer algunas modificaciones para que sea completamente funcional (en mi opinión)
MrTristan
@Gunaseelan gracias por tu solución. Pero tiene algunos errores. Por ejemplo, cuando borro el segundo decimal, es imposible escribirlo nuevamente (debo borrar todos los decimales para poder volver a escribirlo). Además, después de eliminar toda la entrada, se producen algunas limitaciones extrañas al escribir de nuevo.
akelec
14

El InputFilter que se me ocurrió le permite configurar el número de dígitos antes y después del lugar decimal. Además, no permite los ceros a la izquierda.

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
fuente
Buena solución! Funciona para mí, pero quiero no permitir el punto inicial (punto). Por ejemplo, ".123" no se permite la secuencia. ¿Cómo lograr esto?
ibogolyubskiy
13

El requisito es de 2 dígitos después del decimal. No debe haber límite para los dígitos antes del punto decimal. Entonces, la solución debería ser,

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

Y úsalo como,

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

Gracias a @Pinhassi por la inspiración.

Mangesh
fuente
Bien ... Funciona bien
jojo
12

¡Mi solución es simple y funciona perfecta!

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

Uso:

myTextEdit.addTextChangedListener(new DecimalInputTextWatcher(myTextEdit, 2));
Dominik Suszczewicz
fuente
Mueva patrones del isValid()constructor para evitar recrear patrones en cada isValid()llamada.
Hemant Kaushik
6

Intente usar NumberFormat.getCurrencyInstance () para formatear su cadena antes de ponerla en TextView.

Algo como:

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

Editar : no hay inputType para la moneda que pude encontrar en los documentos. Me imagino que esto se debe a que hay algunas monedas que no siguen la misma regla para los decimales, como el yen japonés.

Como mencionó LeffelMania, puede corregir la entrada del usuario utilizando el código anterior con uno TextWatcherque está configurado en su EditText.

Matthew Willis
fuente
6

Solución ligeramente mejorada @Pinhassi.

Funciona muy bien Valida cadenas concatenadas.

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

 }
píxel
fuente
6

Modifiqué las soluciones anteriores y creé la siguiente. Puede establecer el número de dígitos antes y después del punto decimal.

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
fuente
Es casi lo que Reisub ha respondido en la misma pregunta aquí en 2014.
Mehul Joisar
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;
    }
});

Lo que esto hace es que al salir de la fase de edición formatea el campo al formato correcto. En este momento solo tiene 2 caracteres decimales. Creo que esta es una manera bastante fácil de hacer esto.

Sipka
fuente
4

Todas las respuestas aquí son bastante complejas. Intenté simplificarlo mucho. Mira mi código y decide por ti mismo:

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

    }
});
mjosh
fuente
Funciona perfecto para mí. Revisé todo el escenario. Gracias.
Amarnath Baitha
Supongamos que he ingresado 1234.56, ahora quiero editarlo así 12378.56, no puedo hacerlo sin eliminar el decimal.
Aman Verma
4

Realmente me gustó la respuesta de Pinhassi, pero noté que después de que el usuario había ingresado los dígitos numéricos especificados después del punto decimal, ya no podía ingresar texto al lado izquierdo del punto decimal. El problema fue que la solución solo probó el texto anterior que se había ingresado, no el texto actual que se ingresó. Así que aquí está mi solución que inserta el nuevo personaje en el texto original para su validación.

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

Y la llamada para establecer el filtro editText

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
fuente
4

Mejoré la solución que usa una expresión regular de Pinhassi para que también maneje los casos de borde correctamente. Antes de verificar si la entrada es correcta, primero la cadena final se construye como lo describen los documentos de Android.

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

Uso:

editText.setFilters(new InputFilter[] {new DecimalDigitsInputFilter(5,2)});
reisub
fuente
4

La clase Simple Helper está aquí para evitar que el usuario ingrese más de 2 dígitos después del 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
fuente
4

He cambiado la respuesta №6 (por Favas Kv) porque allí puedes poner solo el punto en la primera posición.

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;
    }
}};
koa73
fuente
3

Como dijeron otros, agregué esta clase en mi proyecto y configuré el filtro como EditTextquiero.

El filtro se copia de la respuesta de @ Pixel. Solo lo estoy poniendo todo junto.

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

Ahora configure el filtro en su EditTextcomo este.

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

Aquí, una cosa importante es que resuelve mi problema de no permitir mostrar más de dos dígitos después del punto decimal en eso, EditTextpero el problema es que cuando getText()de eso EditText, devuelve toda la entrada que escribí.

Por ejemplo, después de aplicar el filtro sobre EditText, intenté configurar la entrada 1.5699856987. Entonces en la pantalla muestra 1.56, que es perfecto.

Entonces quería usar esta entrada para algunos otros cálculos, así que quería obtener el texto de ese campo de entrada ( EditText). Cuando lo llamé mEditText.getText().toString()devuelve 1.5699856987 que no era aceptable en mi caso.

Así que tuve que analizar el valor nuevamente después de obtenerlo de EditText.

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

setScalehace el truco aquí después de obtener el texto completo de EditText.

Reaz Murshed
fuente
Hola, ¿cómo puedo asegurarme de que si el usuario no ingresa decimal (.) No debería poder ingresar más de 2 dígitos?
ManishNegi
2

También me he encontrado con este problema. Quería poder reutilizar el código en muchos EditTexts. Esta es mi solución:

Uso:

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

Clase:

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
fuente
2

@Meh para ti ...

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
fuente
2

Una respuesta muy tardía: podemos hacerlo simplemente así:

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) {
        }
}
NanPd
fuente
2

Aquí está el TextWatcherque permite solo n número de dígitos después del punto decimal.

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

Cómo utilizar

yourEditText.addTextChangedListener(getTextWatcherAllowAfterDeci(1));
Hiren Dabhi
fuente
¡¡Funciona de maravilla!!. Gracias Hiren :)
nisha.113a5
2

La forma más sencilla de lograr eso es:

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) {
    }
});
Sasa Ilic
fuente
Respuesta simple y perfecta
Logo
1

Aquí está mi solución:

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

Si el usuario ingresa un número con más de dos números después del punto decimal, se corregirá automáticamente.

Espero haberte ayudado!

lopez.mikhael
fuente
¿Probaste este código? Realmente no puede funcionar, porque cada vez que llama a setText () el TextWatcher se dispara nuevamente => bucle infinito.
muetzenflo
06-07 08: 01: 35.006: E / AndroidRuntime (30230): java.lang.StackOverflowError No funciona
Anjula
1

Esto funciona bien para mi. Permite ingresar el valor incluso después de cambiar el foco y recuperarlo. Por ejemplo: 123.00, 12.12, 0.01, etc ..

1. Integer.parseInt(getString(R.string.valuelength)) Especifica la longitud de la entrada a la que se digits.Valuesaccede desde el string.xmlarchivo. Es silencioso y fácil cambiar los valores. 2. Integer.parseInt(getString(R.string.valuedecimal)), esto es para el límite máximo de decimales.

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

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

Matriz de EditTextvalores que permite realizar acciones.

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

Acabo de usar una matriz de valores que contienen múltiples DecimalDigitsInputFilterNew.classarchivos de texto de edición Siguiente .

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;
    }
}
Karthi Keyan
fuente
1

Esto se basa en la respuesta de pinhassi: el problema que encontré fue que no se podían agregar valores antes del decimal una vez que se había alcanzado el límite decimal. Para solucionar el problema, necesitamos construir la cadena final antes de hacer la coincidencia del patrón.

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;
    }
}
Robert Conley
fuente
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);
            }
        }
});
Pawan Shukla
fuente
Aproximadamente 2 años desde su tiempo de respuesta. Traté de usar su código y luego descubrí que el problema con su solución es que se agrega sourcedespués et.getText(). Siempre entiende que las personas escriben al final del cuadro en lugar de comenzar el cuadro. StringBuilder stringBuilder = new StringBuilder(text.getText().toString()); stringBuilder.replace(dstart, dend, source.toString()); String temp = stringBuilder.toString();Deberia trabajar. Gracias de cualquier manera.
Truong Hieu
1

Cree una nueva clase en Android kotlin con el nombre DecimalDigitsInputFilter

class DecimalDigitsInputFilter(digitsBeforeZero: Int, digitsAfterZero: Int) : InputFilter {
lateinit var mPattern: Pattern
init {
    mPattern =
        Pattern.compile("[0-9]{0," + (digitsBeforeZero) + "}+((\\.[0-9]{0," + (digitsAfterZero) + "})?)||(\\.)?")
}
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
}

Llama a esta clase con la siguiente línea

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

hay demasiadas respuestas para el mismo pero le permitirá ingresar 8 dígitos antes del decimal y 2 dígitos después del decimal

otras respuestas aceptan solo 8 dígitos

Sagar Bandamwar
fuente