Limiter les décimales dans Android EditText

125

J'essaie d'écrire une application qui vous aide à gérer vos finances. J'utilise un EditTextchamp dans lequel l'utilisateur peut spécifier un montant d'argent.

J'ai réglé le inputTypesur numberDecimalqui fonctionne bien, sauf que cela permet aux gens d'entrer des nombres tels que 123.122qui n'est pas parfait pour l'argent.

Existe-t-il un moyen de limiter à deux le nombre de caractères après la virgule décimale?

Konstantin Weitz
la source
Vous pouvez écrire une expression régulière et vérifier le contenu du texte modifié lorsqu'il perd le focus.
blindstuff
J'ai trouvé l' InputFilterinterface, elle semble faire ce que je veux developer.android.com/reference/android/text/method/… , mais la méthode filterque je dois mettre en œuvre me déroute assez. Quelqu'un a-t-il déjà écrit un tel filtre et sait comment l'utiliser?
Konstantin Weitz
L'une des solutions suggérées fonctionne-t-elle pour les paramètres régionaux RTL? Autant que je sache, ils ne le feront pas ...
Nick

Réponses:

118

Une manière plus élégante serait d'utiliser une expression régulière (regex) comme suit:

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

}

Pour l'utiliser, procédez comme suit:

editText.setFilters(new InputFilter[] {new DecimalDigitsInputFilter(5,2)});
Asaf Pinhassi
la source
32
Salut, il y a des cas extrêmes qui ne sont toujours pas bien traités. Par exemple, après avoir tapé 2.45, j'ai tendance à "déplacer le curseur vers l'avant le plus du texte". Je souhaite produire le texte 12.45, cela ne permettra pas.
Cheok Yan Cheng
3
il ne permet pas de changer les chiffres avant la virgule après que l'utilisateur ait entré 2 chiffres après la virgule décimale.
Gaurav Singla
9
Excellente solution mais ce n'est pas entièrement correct. Le matcher ne doit pas vérifier dest, il doit vérifier la valeur dans le edittext (dest.subSequence (0, dstart) + source.subSequence (start, end) + dest.subSequence (dend, dest.length ()))
Mihaela Romanca
7
Mihaela a raison, nous devrions correspondre à la chaîne qui tente de remplir le texte édité. J'ai trouvé comment concaténer sur une autre question comme cette correspondance CharSequence = TextUtils.concat (dest.subSequence (0, dstart), source.subSequence (début, fin), dest.subSequence (dend, dest.length ())); Le regex causait des problèmes par la suite, alors je l'ai changé en "^ \\ d {1," + digitsBeforeZero + "} (\\. \\ d {0," + digitsAfterZero + "})? $" Mais vous ' Je devrai faire une validation par la suite car "1." est valide avec cette expression régulière mais nous en avons besoin de cette façon pour que le point puisse être saisi.
dt0
6
Comment feriez-vous cela pour les virgules (,) aussi? Certaines régions du monde saisissent des nombres décimaux avec des virgules (ex: 123,45).
Andrew
65

Solution plus simple sans utiliser 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;
  }

}

Utiliser:

editText.setFilters(new InputFilter[] {new DecimalDigitsInputFilter(2)});
peceps
la source
Qu'est-ce qui m'empêcherait d'insérer des caractères non numériques dans la chaîne telle que «a»?
Konstantin Weitz
4
Ceci: <EditText ... android: inputType = "number" />
peceps
1
Cela devrait être: editText.setFilters (new InputFilter [] {new DecimalDigitsInputFilter (2)});
frak
6
Cela ne gère pas le cas où je tape «999» puis insère un point décimal après le premier 9.
Jake Stoeffler
1
Merci cet utile. Nous pouvons également restreindre les chiffres avant la virgule décimale en utilisant un filtre de longueur comme celui-ci. Kotlin: edtAnyAmount.filters = arrayOf <InputFilter> (InputFilter.LengthFilter (7), DecimalDigitsInputFilter (2))
Faldu Jaldeep
37

Cette implémentation de InputFilterrésout le problème.

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
la source
Puis-je savoir s'il y a une raison pour laquelle nous devons renvoyer SpannableStringBuilder au lieu de null? Je le teste avec null, ça marche bien aussi. Aussi, est-il nécessaire que nous héritions de DigitsKeyListener? En utilisant Android: inputType = "numberDecimal" exécutera tous les "0123456789." application des caractères.
Cheok Yan Cheng
1
Fonctionne très bien. Merci beaucoup.
Andrei Aulaska
34

Voici un exemple de InputFilter qui que 4 chiffres maximum avant la virgule décimale et 1 chiffre maximum par la suite.

Valeurs autorisées par edittext: 555,2 , 555 , .2

Valeurs bloquées par 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 });
Favas Kv
la source
ne laisse pas. à taper
AmiNadimi
22

J'ai fait quelques corrections pour la solution @Pinhassi. Il gère certains cas:

1.vous pouvez déplacer le curseur n'importe où

2. manipulation du signe moins

3.digitsbefore = 2 et digitsafter = 4 et vous entrez 12.4545. Ensuite, si vous souhaitez supprimer ".", Il ne le permettra pas.

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
la source
Je pense que cela devrait être la solution parfaite, a fait ma journée. Je vous remercie.
Pratik Butani
1
@Omkar c'est faux. cette condition sera toujours vraie même si length> 0, dest.length () == 0 toujours vraie même si vous éditez du texte entier plus de 0 ...
user924
@Omkar supprimer votre commentaire pls
user924
@android_dev pourquoi je ne peux pas saisir de valeurs négatives (moins)?
user924
Si vous définissez android:inputType="number"ou si android:inputType="numberDecimal"cela ne permet pas de taper moins, cela android:digits="0123456789.-"n'aide pas
user924
18

Je n'aime pas l'autre solution et j'ai créé la mienne. Avec cette solution, vous ne pouvez pas entrer plus de MAX_BEFORE_POINT chiffre avant le point et les décimales ne peuvent pas dépasser MAX_DECIMAL.

Vous ne pouvez tout simplement pas taper le chiffre en excès, pas d'autres effets! En plus si vous écrivez "." il tape "0".

  1. Définissez EditText dans la mise en page sur:

    android: inputType = "numberDecimal"

  2. Ajoutez l'écouteur dans votre onCreate. Si vous souhaitez modifier le nombre de chiffres avant et après le point, éditez l'appel à PerfectDecimal (str, NUMBER_BEFORE_POINT, NUMBER_DECIMALS), ici est mis à 3 et 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. Incluez cette fonction:

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

Et c'est fait!

Apoleo
la source
1
À votre santé. Fonctionne très bien quand l'autre ne fonctionnait pas pour moi
Ours
1
Cela devrait être la réponse acceptée. C'est parfait. Toutes les conditions sont satisfaites ici.
Nayan
1
Sur toutes les réponses hautement votées, cette réponse a fonctionné pour moi.
Rohit Mandiwal
Bien joué! J'ai essayé toutes les combinaisons et il semble que cela fonctionne très bien. Je vous remercie.
akelec
Je ne sais pas comment ça marche, mais ça marche comme un charme.
wonsuc
17

J'ai réalisé cela avec l'aide de TextWatcherde la manière suivante

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

Cette solution ne permettra pas à l'utilisateur d'entrer plus de deux chiffres après la virgule décimale. Vous pouvez également entrer n'importe quel nombre de chiffres avant la virgule décimale. Consultez ce blog http://v4all123.blogspot.com/2013/05/set-limit-for-fraction-in-decimal.html pour définir un filtre pour plusieurs EditText. J'espère que cela aidera. Je vous remercie.

Gunaseelan
la source
Désolé pour les informations tardives. N'oubliez pas d'initialiser countavec -1. Alors seulement cela fonctionnera correctement. int count = -1;
Gunaseelan
Gunaseelan - j'ai essayé le code ci-dessus et il fonctionne bien. Mais quand je supprime le texte dactylographié et que je recommence à taper, il ne tape qu'un seul chiffre, toute solution pour cela .....
Siva K
@SivaK Pas un ami. Si vous supprimez puis tapez, il acceptera au moins 100 chiffres. Je ne sais pas comment vous y accédez listener. De toute façon s'il vous plaît jeter un oeil mon blog un message . Vous pouvez avoir une idée. Si vous ne pouvez pas, faites-le moi savoir. Je vais vous aider à aborder cette question.
Gunaseelan
J'ai vérifié ce que @SivaK a dit. c'est malin en tout cas mais je vais y apporter quelques modifications pour qu'il soit pleinement fonctionnel (à mon avis)
MrTristan
@Gunaseelan merci pour votre solution. Mais il a quelques bugs. Par exemple, lorsque je supprime la deuxième décimale, il est impossible de le retaper (je dois supprimer toutes les décimales pour pouvoir le saisir à nouveau). De plus, après la suppression de l'entrée entière, certaines limitations étranges se produisent lorsque vous tapez à nouveau.
akelec
14

Le InputFilter que j'ai créé vous permet de configurer le nombre de chiffres avant et après la décimale. De plus, il interdit les zéros non significatifs.

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
la source
Belle solution! Cela fonctionne pour moi, mais je veux interdire la période de début (point). Par exemple, ".123" n'est pas une séquence autorisée. Comment y parvenir?
ibogolyubskiy
13

L'exigence est de 2 chiffres après la virgule. Il ne devrait y avoir aucune limite pour les chiffres avant la virgule décimale. Donc, la solution devrait être,

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

Et utilisez-le comme,

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

Merci à @Pinhassi pour l'inspiration.

Mangesh
la source
Good ... Working fine
jojo
12

Ma solution est simple et fonctionne parfaitement!

public class DecimalInputTextWatcher implements TextWatcher {

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

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

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

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

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

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

    } else {
        mRestoringPreviousValueFlag = false;
    }
}

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

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

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

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

Usage:

myTextEdit.addTextChangedListener(new DecimalInputTextWatcher(myTextEdit, 2));
Dominik Suszczewicz
la source
Déplacez les modèles de isValid()vers le constructeur pour éviter de recréer des modèles à chaque isValid()appel.
Hemant Kaushik
6

Essayez d'utiliser NumberFormat.getCurrencyInstance () pour formater votre chaîne avant de la mettre dans un TextView.

Quelque chose comme:

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

Modifier - Il n'y a pas de type d'entrée pour la devise que je pourrais trouver dans la documentation. J'imagine que c'est parce que certaines devises ne suivent pas la même règle pour les décimales, comme le yen japonais.

Comme LeffelMania l'a mentionné, vous pouvez corriger l'entrée de l'utilisateur en utilisant le code ci-dessus avec un TextWatcherqui est défini sur votre EditText.

Matthew Willis
la source
6

Solution @Pinhassi légèrement améliorée.

Fonctionne très bien. Il valide les chaînes concaténées.

public class DecimalDigitsInputFilter implements InputFilter {

Pattern mPattern;

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

}

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

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

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

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

    String result = destPrefix + formatedSource + destSuffix;

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

    Matcher matcher = mPattern.matcher(result);

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

    return "";
}

 }
pixel
la source
6

J'ai modifié les solutions ci-dessus et créé la suivante. Vous pouvez définir le nombre de chiffres avant et après la virgule décimale.

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
la source
C'est presque ce que reisub a répondu à la même question ici 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;
    }
});

Lorsque vous quittez la phase d'édition, cela formate le champ au bon format. En ce moment, il n'a que 2 caractères décimaux. Je pense que c'est un moyen assez simple de le faire.

Sipka
la source
4

Toutes les réponses ici sont assez complexes, j'ai essayé de le rendre beaucoup plus simple.Regardez mon code et décidez par vous-même -

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
la source
son fonctionnement parfait pour moi. J'ai vérifié tous les scénarios. Merci.
Amarnath Baitha
Supposons que j'ai entré 1234,56, maintenant je veux le modifier comme ceci 12378,56, je ne peux pas le faire sans supprimer la décimale.
Aman Verma
4

J'ai vraiment aimé la réponse de Pinhassi, mais j'ai remarqué qu'après que l'utilisateur avait entré les chiffres du nombre spécifié après la virgule décimale, vous ne pouviez plus entrer de texte à gauche du point décimal. Le problème était que la solution ne testait que le texte précédent qui avait été saisi, et non le texte actuel saisi. Voici donc ma solution qui insère le nouveau caractère dans le texte d'origine pour validation.

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

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

public class InputFilterCurrency implements InputFilter {
    Pattern moPattern;

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

    } // InputFilterCurrency

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

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

        lsText = dest.toString();

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

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

        } // if

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

    } // CharSequence

} // InputFilterCurrency

Et l'appel pour définir le filtre 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
la source
4

J'ai amélioré la solution qui utilise une expression régulière de Pinhassi afin qu'elle gère également correctement les cas de bord. Avant de vérifier si l'entrée est correcte, la chaîne finale est d'abord construite comme décrit dans la documentation 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;
    }
}

Usage:

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

La classe Simple Helper est là pour empêcher l'utilisateur de saisir plus de 2 chiffres après la décimale:

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
la source
4

J'ai changé la réponse №6 (par Favas Kv) parce que là Vous pouvez mettre juste le point en première position.

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

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

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

Comme d'autres l'ont dit, j'ai ajouté cette classe à mon projet et défini le filtre sur EditTextJe veux.

Le filtre est copié à partir de la réponse de @ Pixel. Je mets juste tout ça ensemble.

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

Maintenant, définissez le filtre dans votre EditTextcomme ça.

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

Ici, une chose importante est que cela résout mon problème de ne pas permettre d'afficher plus de deux chiffres après la virgule décimale, EditTextmais le problème est que lorsque je à getText()partir de cela EditText, il renvoie toute l'entrée que j'ai tapée.

Par exemple, après avoir appliqué le filtre sur le EditText, j'ai essayé de définir l'entrée 1.5699856987. Donc, sur l'écran, il affiche 1,56, ce qui est parfait.

Ensuite, j'ai voulu utiliser cette entrée pour d'autres calculs, donc je voulais obtenir le texte de ce champ d'entrée ( EditText). Quand j'ai appelémEditText.getText().toString() il renvoie 1.5699856987, ce qui n'était pas acceptable dans mon cas.

J'ai donc dû analyser à nouveau la valeur après l'avoir obtenue à partir du fichier EditText.

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

setScalefait l'affaire ici après avoir récupéré le texte intégral du EditText.

Reaz Murshed
la source
Bonjour, comment puis-je m'assurer que si l'utilisateur ne saisit pas de décimal (.), Il ne devrait pas pouvoir saisir plus de 2 chiffres?
ManishNegi
2

J'ai également rencontré ce problème. Je voulais pouvoir réutiliser le code dans de nombreux EditTexts. Voici ma solution:

Utilisation:

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;
    }
}
À M
la source
2

@Meh pour toi ..

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
la source
2

Une réponse très tardive: nous pouvons le faire simplement comme ceci:

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
la source
2

Voici ceux TextWatcherqui n'autorisent que n nombre de chiffres après la virgule décimale.

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

Comment utiliser

yourEditText.addTextChangedListener(getTextWatcherAllowAfterDeci(1));
Hiren Dabhi
la source
Fonctionne comme un charme !!. Merci Hiren :)
nisha.113a5
2

Le moyen le plus simple d'y parvenir est:

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
la source
Réponse simple et parfaite
Logo
1

Voici ma solution:

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

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

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

Si l'utilisateur entre un nombre avec plus de deux nombres après la virgule décimale, il sera automatiquement corrigé.

J'espère avoir aidé!

lopez.mikhael
la source
Avez-vous testé ce code? Cela ne peut pas vraiment fonctionner, car chaque fois que vous appelez setText (), le TextWatcher se déclenche à nouveau => boucle infinie.
muetzenflo
06-07 08: 01: 35.006: E / AndroidRuntime (30230): java.lang.StackOverflowError ne fonctionne pas
Anjula
1

Cela fonctionne bien pour moi. Il permet de saisir une valeur même après la modification de la mise au point et la récupération. Par exemple: 123.00, 12.12, 0.01, etc ..

1. Integer.parseInt(getString(R.string.valuelength)) Spécifie la longueur de l'entrée digits.Valuesaccessible depuisstring.xml fichier. Il est très facile de modifier les valeurs. 2. Integer.parseInt(getString(R.string.valuedecimal)), ceci est pour la limite maximum de décimales.

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

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

Tableau de EditText valeurs qui permet d'effectuer une action.

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

Je viens d'utiliser un tableau de valeurs contenant plusieurs fichiers suivant edittext DecimalDigitsInputFilterNew.class.

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
la source
1

C'est pour s'appuyer sur la réponse de pinhassi - le problème que j'ai rencontré était que vous ne pouviez pas ajouter de valeurs avant la virgule une fois la limite décimale atteinte. Pour résoudre le problème, nous devons construire la chaîne finale avant de faire la correspondance de modèle.

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
la source
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
la source
Environ 2 ans à compter de votre délai de réponse. J'ai essayé d'utiliser votre code, puis j'ai trouvé que le problème concernant votre solution était de l'ajouter sourceaprès et.getText(). Il comprend toujours que les gens tapent à la fin de la boîte au lieu du début de la boîte. StringBuilder stringBuilder = new StringBuilder(text.getText().toString()); stringBuilder.replace(dstart, dend, source.toString()); String temp = stringBuilder.toString();devrait marcher. Merci quand même.
Truong Hieu
1

Créez une nouvelle classe dans Android kotlin avec le nom 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
}

Appelez cette classe avec la ligne suivante

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

il y a trop de réponses pour la même chose mais cela vous permettra d'entrer 8 chiffres avant la décimale et 2 chiffres après la décimale

les autres réponses n'acceptent que 8 chiffres

Sagar Bandamwar
la source