Limita le posizioni decimali in Android EditText


125

Sto cercando di scrivere un'app che ti aiuti a gestire le tue finanze. Sto utilizzando un EditTextcampo in cui l'utente può specificare un importo di denaro.

Ho impostato il inputTypeper numberDecimalche funziona bene, tranne che questo permette alle persone di inserire numeri, come 123.122che non è perfetto per i soldi.

C'è un modo per limitare il numero di caratteri dopo il punto decimale a due?


È possibile scrivere un'espressione regolare e verificare il contenuto del testo di modifica quando perde lo stato attivo.
cieco

Ho trovato l' InputFilterinterfaccia, sembra fare quello che voglio developer.android.com/reference/android/text/method/… , ma il metodo filterche devo implementare mi confonde piuttosto. Qualcuno ha già scritto un filtro del genere e sa come usarlo?
Konstantin Weitz,

Alcune delle soluzioni suggerite funzionano per le versioni locali di RTL? Per quanto ne so, non ...
Nick,

Risposte:


118

Un modo più elegante sarebbe usare un'espressione regolare (regex) come segue:

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

}

Per usarlo fai:

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

32
Ciao, ci sono alcuni casi limite ancora non gestiti bene. Ad esempio, dopo aver digitato 2.45, tendo a "spostare il cursore in primo piano per la maggior parte del testo". Vorrei produrre il testo 12.45, non lo permetterà.
Cheok Yan Cheng,

3
non consente di modificare le cifre prima dei decimali dopo che l'utente ha immesso 2 cifre dopo il punto decimale.
Gaurav Singla,

9
Ottima soluzione ma non è del tutto corretta. Il matcher non dovrebbe controllare dest, ma dovrebbe controllare il valore in edittext (dest.subSequence (0, dstart) + source.subSequence (inizio, fine) + dest.subSequence (dend, dest.length ()))
Mihaela Romanca

7
Mihaela ha ragione, dovremmo confrontarci con la stringa che sta cercando di riempire l'edittext. Ho trovato come concatenare un'altra domanda come questa corrispondenza CharSequence = TextUtils.concat (dest.subSequence (0, dstart), source.subSequence (start, end), dest.subSequence (dend, dest.length ())); Il regex stava causando problemi in seguito, quindi l'ho cambiato in questo "^ \\ d {1," + digitsBeforeZero + "} (\\. \\ d {0," + digitsAfterZero + "})? $" Ma tu ' In seguito dovrò fare anche la convalida perché "1." è valido con quel regex ma ne abbiamo bisogno in questo modo in modo che il periodo possa essere digitato.
dt0

6
Come funzioneresti anche per le virgole (,)? Alcune regioni del mondo digitano numeri decimali con virgole (es: 123,45).
Andrew,

65

Soluzione più semplice senza usare 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;
  }

}

Usare:

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

Cosa mi impedirebbe di inserire caratteri non numerici nella stringa come 'a'?
Konstantin Weitz,

4
Questo: <EditText ... android: inputType = "number" />
peceps

1
Dovrebbe essere: editText.setFilters (new InputFilter [] {new DecimalDigitsInputFilter (2)});
frak,

6
Questo non gestisce il caso in cui scrivo "999" e quindi inserisco un punto decimale dopo i primi 9.
Jake Stoeffler

1
Grazie utile. Possiamo anche limitare le cifre prima del punto decimale usando un filtro di lunghezza come questo. Kotlin: edtAnyAmount.filters = arrayOf <InputFilter> (InputFilter.LengthFilter (7), DecimalDigitsInputFilter (2))
Faldu Jaldeep

37

Questa implementazione di InputFilterrisolve il 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);
    }
}

Posso sapere se c'è qualche motivo per cui dobbiamo restituire SpannableStringBuilder anziché null? Lo collaudo con null, funziona anche bene. Inoltre, è necessario che ereditiamo da DigitsKeyListener? Come usando Android: inputType = "numberDecimal" eseguirà tutto "0123456789." applicazione dei personaggi.
Cheok Yan Cheng il

1
Funziona bene. Grazie mille.
Andrei Aulaska,

34

Ecco un InputFilter di esempio che consente solo massimo 4 cifre prima del punto decimale e massimo 1 cifra dopo.

Valori che EditText permette: 555,2 , 555 , .2

Valori che blocca edittext: 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 });

non lascia. da scrivere
AmiNadimi

22

Ho apportato alcune correzioni per la soluzione @Pinhassi. Gestisce alcuni casi:

1.puoi spostare il cursore ovunque

2.minus gestione dei segni

3.digitsbefore = 2 e digititsafter = 4 e si immette 12.4545. Quindi se si desidera rimuovere ".", Non lo consentirà.

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

Penso che dovrebbe essere la soluzione perfetta, ha reso la mia giornata. Grazie.
Pratik Butani,

1
@Omkar questo è sbagliato. questa condizione sarà sempre vera anche se lunghezza> 0, dest.length () == 0 sempre vera anche se hai inserito l'intero testo più di 0 ...
user924

@Omkar rimuovi il tuo commento per favore
user924

@android_dev perché non riesco a digitare valori negativi (meno)?
user924

Se hai impostato android:inputType="number"o android:inputType="numberDecimal"non ti permetterà di digitare meno, questo android:digits="0123456789.-"non aiuta
user924

18

Non mi piace l'altra soluzione e ho creato la mia. Con questa soluzione non puoi inserire più di MAX_BEFORE_POINT cifra prima del punto e i decimali non possono essere più di MAX_DECIMAL.

Non puoi semplicemente digitare la cifra in eccesso, nessun altro effetto! In aggiunta se scrivi "." digita "0"

  1. Imposta EditText nel layout su:

    Android: InputType = "numberDecimal"

  2. Aggiungi il Listener in onCreate. Se vuoi modificare il numero di cifre prima e dopo il punto modifica la chiamata su PerfectDecimal (str, NUMBER_BEFORE_POINT, NUMBER_DECIMALS), qui è impostato su 3 e 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. Includi questa funzione:

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

Ed è fatto!


1
Saluti. Funziona molto bene quando l'altro non ha funzionato per me
Orso

1
Questa dovrebbe essere la risposta accettata. È perfetta. Tutte le condizioni sono soddisfatte qui.
Nayan,

1
Oltre a tutte le risposte molto votate, questa risposta ha funzionato davvero per me.
Rohit Mandiwal il

Molto bene! Ho provato tutte le combinazioni e sembra che funzioni alla grande. Grazie.
akelec il

Non so come funzioni, ma funziona come un incantesimo.
Wonsuc

17

L'ho raggiunto con l'aiuto del TextWatcherseguente modo

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

Questa soluzione non consentirà all'utente di inserire più di due cifre dopo il punto decimale. Inoltre è possibile inserire un numero qualsiasi di cifre prima del punto decimale. Vedi questo blog http://v4all123.blogspot.com/2013/05/set-limit-for-fraction-in-decimal.html per impostare il filtro per più EditText. Spero che questo possa aiutare. Grazie.


Ci scusiamo per le informazioni in ritardo. NON dimenticare di inizializzare countcon -1. Quindi solo questo funzionerà correttamente. int count = -1;
Gunaseelan,

Gunaseelan - ho provato il codice sopra e funziona bene. Ma quando elimino il testo digitato e ricomincio a digitare digitando solo una cifra, qualsiasi soluzione per questo .....
Siva K

@SivaK Nessun amico. Se elimini e digiti, accetta almeno 100 cifre. Non ho accesso a questo listener. Ad ogni modo, dai un'occhiata al mio post sul blog . Potresti avere un'idea. Se non puoi per favore fammi sapere. Ti aiuterò riguardo a questo problema.
Gunaseelan,

ho verificato cosa ha detto @SivaK. questo è intelligente in ogni caso, ma ho intenzione di apportare alcune modifiche a questo in modo che sia pienamente funzionale (secondo me)
MrTristan,

@Gunaseelan grazie per la tua soluzione. Ma ha alcuni bug. Ad esempio, quando cancello il secondo decimale, è impossibile digitarlo di nuovo (devo eliminare tutti i decimali per poterlo digitare di nuovo). Inoltre, dopo aver eliminato l'intera voce, si verificano alcune strane limitazioni durante la digitazione.
akelec il

14

InputFilter che mi è venuto in mente ti consente di configurare il numero di cifre prima e dopo il decimale. Inoltre, non consente gli zeri iniziali.

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

Bella soluzione! Funziona per me, ma voglio non consentire il periodo iniziale (punto). Ad esempio, ".123" non è consentita la sequenza. Come raggiungere questo obiettivo?
ibogolyubskiy,

13

Il requisito è di 2 cifre dopo il decimale. Non ci dovrebbero essere limiti per le cifre prima del punto decimale. Quindi, la soluzione dovrebbe essere,

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

E usalo come,

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

Grazie a @Pinhassi per l'ispirazione.


Bene ... Funziona benissimo
jojo,

12

La mia soluzione è semplice e funziona perfettamente!

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

Spostare i pattern dal isValid()costruttore per evitare di ricreare i pattern su ogni isValid()chiamata.
Hemant Kaushik,

6

Prova a utilizzare NumberFormat.getCurrencyInstance () per formattare la stringa prima di inserirla in TextView.

Qualcosa di simile a:

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

Modifica : non esiste alcun inputType per la valuta che ho trovato nei documenti. Immagino che ciò avvenga perché ci sono alcune valute che non seguono la stessa regola per i decimali, come lo yen giapponese.

Come accennato da LeffelMania, puoi correggere l'input dell'utente usando il codice sopra con un valore TextWatcherimpostato sul tuo EditText.


6

Soluzione @Pinhassi leggermente migliorata.

Funziona molto bene Convalida stringhe concatenate.

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

 }

6

Ho modificato le soluzioni sopra e ho creato una dopo. È possibile impostare il numero di cifre prima e dopo il punto decimale.

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

}


È quasi ciò a cui reisub ha risposto sulla stessa domanda qui nel 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;
    }
});

Ciò che fa è quando si esce dalla fase di modifica formatta il campo nel formato giusto. Al momento ha solo 2 caratteri decimali. Penso che questo sia un modo abbastanza semplice per farlo.


4

Tutte le risposte qui sono piuttosto complesse, ho cercato di renderlo molto più semplice. Guarda il mio codice e decidi tu stesso -

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

    }
});

funziona perfettamente per me. Ho controllato tutto lo scenario. Grazie.
Amarnath Baitha,

Supponiamo di aver inserito 1234.56, ora voglio modificarlo in questo modo 12378.56, non posso farlo senza eliminare il decimale.
Aman Verma,

4

Mi è piaciuta molto la risposta di Pinhassi, ma ho notato che dopo che l'utente ha inserito le cifre del numero specificato dopo il punto decimale, non è più possibile inserire il testo sul lato sinistro del punto decimale. Il problema era che la soluzione testava solo il testo precedente che era stato inserito, non il testo corrente che veniva inserito. Quindi, ecco la mia soluzione che inserisce il nuovo carattere nel testo originale per la convalida.

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

E la chiamata per impostare il 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

4

Ho migliorato la soluzione che utilizza un regex di Pinhassi in modo che gestisca correttamente anche i casi limite. Prima di verificare se l'input è corretto, innanzitutto la stringa finale è costruita come descritto dai documenti 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)});

4

La classe Helper semplice è qui per impedire all'utente di inserire più di 2 cifre dopo i decimali:

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

4

Ho cambiato la risposta №6 (di Favas Kv) perché lì puoi mettere solo il punto nella prima posizione.

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

3

Come altri hanno già detto, ho aggiunto questa classe al mio progetto e impostato il filtro sul valore EditTextdesiderato.

Il filtro viene copiato dalla risposta di @ Pixel. Sto solo mettendo tutto insieme.

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

Ora imposta il filtro in EditTextquesto modo.

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

Qui una cosa importante è che risolve il mio problema di non consentire la visualizzazione di più di due cifre dopo il punto decimale in quello, EditTextma il problema è che quando getText()da quello EditText, restituisce l'intero input che ho digitato.

Ad esempio, dopo aver applicato il filtro su EditText, ho provato a impostare l'input 1.5699856987. Quindi nella schermata mostra 1.56 che è perfetto.

Quindi volevo usare questo input per alcuni altri calcoli, quindi volevo ottenere il testo da quel campo di input ( EditText). Quando ho chiamatomEditText.getText().toString() restituisce 1.5699856987 che non era accettabile nel mio caso.

Quindi ho dovuto analizzare nuovamente il valore dopo averlo ottenuto da EditText.

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

setScalefa il trucco qui dopo aver ottenuto il testo completo da EditText.


Ciao, come posso assicurarmi che se l'utente non inserisce il decimale (.) Non dovrebbe essere in grado di inserire più di 2 cifre?
ManishNegi,

2

Ho anche riscontrato questo problema. Volevo poter riutilizzare il codice in molti EditTexts. Questa è la mia soluzione:

Utilizzo:

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

Classe:

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

2

@Meh per te ..

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

2

Una risposta molto tardi: possiamo farlo semplicemente in questo modo:

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

2

Ecco quello TextWatcherche consente solo n numero di cifre dopo il punto decimale.

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

Come usare

yourEditText.addTextChangedListener(getTextWatcherAllowAfterDeci(1));

Funziona come un fascino !!. Grazie Hiren :)
nisha.113a5 il

2

Il modo più semplice per raggiungere questo obiettivo è:

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

Risposta semplice e perfetta
Logo

1

Ecco la mia soluzione:

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

Se l'utente immette un numero con più di due numeri dopo il punto decimale, verrà automaticamente corretto.

Spero di aver aiutato!


Hai testato questo codice? Non può davvero funzionare, perché ogni volta che chiami setText () TextWatcher si attiva di nuovo => ciclo infinito.
muetzenflo,

06-07 08: 01: 35.006: E / AndroidRuntime (30230): java.lang.StackOverflowError Non funziona
Anjula

1

Questo funziona bene per me. Permette di inserire il valore anche dopo che la messa a fuoco è stata modificata e recuperata. Ad esempio: 123.00, 12.12, 0.01, ecc ..

1. Integer.parseInt(getString(R.string.valuelength)) Specifica la lunghezza dell'input a cui si digits.Valuesaccede dal string.xmlfile. È facile cambiare i valori. 2. Integer.parseInt(getString(R.string.valuedecimal)), questo è per il limite massimo di posizioni decimali.

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

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

Matrice di EditTextvalori che consente di eseguire azioni.

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

Ho appena usato una matrice di valori che contengono più DecimalDigitsInputFilterNew.classfile Next edittext .

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

1

Questo è basato sulla risposta di Pinhassi: il problema che ho riscontrato è che non è stato possibile aggiungere valori prima del decimale una volta raggiunto il limite decimale. Per risolvere il problema, dobbiamo costruire la stringa finale prima di eseguire la corrispondenza del modello.

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

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

public class DecimalLimiter implements InputFilter
{
    Pattern mPattern;

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

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

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

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

Circa 2 anni dal momento della risposta. Ho provato ad usare il tuo codice, poi ho scoperto che il problema relativo alla tua soluzione è quello che hai aggiunto sourcedopo et.getText(). Capisce sempre che le persone digitano alla fine della casella anziché all'inizio della casella. StringBuilder stringBuilder = new StringBuilder(text.getText().toString()); stringBuilder.replace(dstart, dend, source.toString()); String temp = stringBuilder.toString();dovrebbe funzionare. Grazie comunque.
Truong Hieu,

1

Crea una nuova classe in Android kotlin con il nome 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
}

Chiama questa classe con la seguente linea

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

ci sono troppe risposte per lo stesso ma ti permetterà di inserire 8 cifre prima dei decimali e 2 cifre dopo i decimali

altre risposte accettano solo 8 cifre

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.