Ogranicz miejsca dziesiętne w Androidzie EditText

125

Próbuję napisać aplikację, która pomoże Ci zarządzać swoimi finansami. Używam EditTextpola, w którym użytkownik może określić kwotę pieniędzy.

I ustawić inputTypena numberDecimalktóry działa dobrze, oprócz tego, że pozwala to ludzie, aby wprowadzić numery takie jak 123.122który nie jest idealne dla pieniędzy.

Czy istnieje sposób ograniczenia liczby znaków po przecinku do dwóch?

Konstantin Weitz
źródło
Możesz napisać wyrażenie regularne i zweryfikować treść edytowanego tekstu, gdy traci fokus.
Blindstuff
Znalazłem InputFilterinterfejs, wygląda na to, że robi to, co chcę developer.android.com/reference/android/text/method/… , ale metoda filter, którą muszę zaimplementować, jest dla mnie dość zagmatwana. Czy ktoś już napisał taki filtr i wie, jak go używać?
Konstantin Weitz
Czy któreś z sugerowanych rozwiązań działa w przypadku ustawień regionalnych RTL? O ile wiem, nie będą ...
Nick

Odpowiedzi:

118

Bardziej eleganckim sposobem byłoby użycie wyrażenia regularnego (regex) w następujący sposób:

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

}

Aby go użyć, wykonaj:

editText.setFilters(new InputFilter[] {new DecimalDigitsInputFilter(5,2)});
Asaf Pinhassi
źródło
32
Cześć, niektóre skrajne przypadki nadal nie są dobrze obsługiwane. Na przykład, po wpisaniu 2.45, mam tendencję do „przesuwania kursora na pierwszą część tekstu”. Chcę stworzyć tekst 12.45, to nie pozwoli.
Cheok Yan Cheng,
3
nie pozwala na zmianę cyfr przed przecinkiem po wprowadzeniu przez użytkownika 2 cyfr po przecinku.
Gaurav Singla
9
Świetne rozwiązanie, ale nie do końca poprawne. Matcher nie powinien sprawdzać dest, powinien sprawdzać wartość w edittext (dest.subSequence (0, dstart) + source.subSequence (start, end) + dest.subSequence (dend, dest.length ()))
Mihaela Romanca
7
Mihaela ma rację, powinniśmy dopasowywać się do ciągu, który próbuje wypełnić edittext. Znalazłem sposób konkatenacji w innym pytaniu, takim jak to CharSequence match = TextUtils.concat (dest.subSequence (0, dstart), source.subSequence (początek, koniec), dest.subSequence (dend, dest.length ())); Jednak wyrażenie regularne powodowało później problemy, więc zmieniłem je na "^ \\ d {1," + digitsBeforeZero + "} (\\. \\ d {0," + digitsAfterZero + "})? $" Ale ty ' Później też będę musiał przeprowadzić walidację, ponieważ „1”. jest poprawne z tym wyrażeniem regularnym, ale potrzebujemy go w ten sposób, aby można było wpisać kropkę.
dt0
6
Jak możesz to zrobić dla przecinków (,)? Niektóre regiony świata wpisują liczby dziesiętne przecinkami (np .: 123,45).
Andrew
65

Prostsze rozwiązanie bez użycia wyrażenia regularnego:

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

}

Używać:

editText.setFilters(new InputFilter[] {new DecimalDigitsInputFilter(2)});
peceps
źródło
Co powstrzymałoby mnie przed wstawieniem do ciągu znaków nienumerycznych, takich jak „a”?
Konstantin Weitz
4
To: <EditText ... android: inputType = "number" />
peceps
1
Powinno to być: editText.setFilters (new InputFilter [] {new DecimalDigitsInputFilter (2)});
frak
6
To nie obsługuje przypadku, w którym
wpisuję
1
Dzięki temu przydatne. Możemy również ograniczyć cyfry przed kropką dziesiętną za pomocą takiego filtra długości. Kotlin: edtAnyAmount.filters = arrayOf <InputFilter> (InputFilter.LengthFilter (7), DecimalDigitsInputFilter (2))
Faldu Jaldeep
37

Ta implementacja InputFilterrozwiązuje problem.

import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.method.DigitsKeyListener;

public class MoneyValueFilter extends DigitsKeyListener {
    public MoneyValueFilter() {
        super(false, true);
    }

    private int digits = 2;

    public void setDigits(int d) {
        digits = d;
    }

    @Override
    public CharSequence filter(CharSequence source, int start, int end,
            Spanned dest, int dstart, int dend) {
        CharSequence out = super.filter(source, start, end, dest, dstart, dend);

        // if changed, replace the source
        if (out != null) {
            source = out;
            start = 0;
            end = out.length();
        }

        int len = end - start;

        // if deleting, source is empty
        // and deleting can't break anything
        if (len == 0) {
            return source;
        }

        int dlen = dest.length();

        // Find the position of the decimal .
        for (int i = 0; i < dstart; i++) {
            if (dest.charAt(i) == '.') {
                // being here means, that a number has
                // been inserted after the dot
                // check if the amount of digits is right
                return (dlen-(i+1) + len > digits) ? 
                    "" :
                    new SpannableStringBuilder(source, start, end);
            }
        }

        for (int i = start; i < end; ++i) {
            if (source.charAt(i) == '.') {
                // being here means, dot has been inserted
                // check if the amount of digits is right
                if ((dlen-dend) + (end-(i + 1)) > digits)
                    return "";
                else
                    break;  // return new SpannableStringBuilder(source, start, end);
            }
        }

        // if the dot is after the inserted part,
        // nothing can break
        return new SpannableStringBuilder(source, start, end);
    }
}
Konstantin Weitz
źródło
Czy mogę wiedzieć, czy jest jakiś powód, dla którego musimy zwrócić SpannableStringBuilder zamiast null? Testuję to z null, to też działa dobrze. Czy istnieje potrzeba, abyśmy dziedziczyli po DigitsKeyListener? Tak jak w przypadku Androida: inputType = "numberDecimal" wykona wszystkie "0123456789". wymuszanie znaków.
Cheok Yan Cheng,
1
Działa w porządku. Dziękuję Ci bardzo.
Andrei Aulaska
34

Oto przykład InputFilter który dopuszcza maksymalnie 4 cyfry przed kropką dziesiętną i maksymalnie 1 cyfrę po tym.

Wartości, na które pozwala edittext : 555,2 , 555 , .2

Wartości blokowane przez 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
źródło
nie pozwala . do
wpisania
22

Zrobiłem kilka poprawek dla rozwiązania @Pinhassi. Obsługuje niektóre przypadki:

1. możesz przesuwać kursor w dowolnym miejscu

2. obsługa znaku minus

3.digitsbefore = 2 i digitsafter = 4 i wpisz 12.4545. Jeśli chcesz usunąć „.”, Nie pozwoli.

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
źródło
Myślę, że powinno to być idealne rozwiązanie, które zrobiło mój dzień. Dziękuję Ci.
Pratik Butani
1
@Omkar to źle. warunek ten będzie zawsze prawdziwe, nawet jeśli długość> 0, dest.length () == 0 zawsze prawdziwe, nawet jeśli cały tekst EditText więcej niż 0 ...
user924
@Omkar usuń swój komentarz.
Pls
@android_dev dlaczego nie mogę wpisać wartości ujemnych (minus)?
user924
Jeśli ustawisz android:inputType="number"lub android:inputType="numberDecimal"nie pozwolisz na wpisanie minus, to android:digits="0123456789.-"nie pomoże
user924
18

Nie podoba mi się drugie rozwiązanie i stworzyłem własne. W przypadku tego rozwiązania nie można wprowadzić więcej niż MAX_BEFORE_POINT cyfry przed kropką, a liczba miejsc po przecinku nie może być większa niż MAX_DECIMAL.

Po prostu nie możesz wpisać nadmiaru cyfry, żadnych innych efektów! Dodatkowo, jeśli napiszesz „.” wpisuje „0”

  1. Ustaw EditText w układzie na:

    android: inputType = "numberDecimal"

  2. Dodaj Listener w swoim onCreate. Jeśli chcesz zmodyfikować liczbę cyfr przed i po punkcie, edytuj wywołanie PerfectDecimal (str, NUMBER_BEFORE_POINT, NUMBER_DECIMALS), tutaj jest ustawiony na 3 i 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. Uwzględnij tę funkcję:

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

Gotowe!

Apoleo
źródło
1
Twoje zdrowie. Działał bardzo dobrze, gdy drugi nie działał dla mnie
Bear
1
To powinna być zaakceptowana odpowiedź. Jest idealna. Wszystkie warunki są tutaj spełnione.
Nayan
1
Spośród wszystkich wysoko ocenionych odpowiedzi, ta odpowiedź faktycznie zadziałała dla mnie.
Rohit Mandiwal
Dobra robota! Wypróbowałem wszystkie kombinacje i wygląda na to, że działa świetnie. Dziękuję Ci.
akelec
Nie wiem, jak to działa, ale działa jak urok.
wonsuc
17

Osiągnąłem to z pomocą TextWatcherw następujący sposób

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

To rozwiązanie nie pozwoli użytkownikowi na wprowadzenie więcej niż dwóch cyfr po przecinku. Możesz także wprowadzić dowolną liczbę cyfr przed kropką dziesiętną. Zobacz ten blog http://v4all123.blogspot.com/2013/05/set-limit-for-fraction-in-decimal.html, aby ustawić filtr dla wielu EditText. Mam nadzieję, że to pomoże. Dziękuję Ci.

Gunaseelan
źródło
Przepraszamy za późne informacje. Nie zapomnij, aby zainicjować countz -1. Wtedy tylko to będzie działać poprawnie. int count = -1;
Gunaseelan
Gunaseelan - wypróbowałem powyższy kod i działa dobrze. Ale kiedy usunę wpisany tekst i zacznę pisać ponownie, wpisuję tylko jedną cyfrę, jakieś rozwiązanie na to .....
Siva K
@SivaK Nie ma mowy przyjacielu. Jeśli usuniesz, a następnie wpiszesz, zostanie zaakceptowane minimum 100 cyfr. Nie wiem, jak masz do tego dostęp listener. W każdym razie spójrz na mój wpis na blogu . Możesz mieć pomysł. Jeśli nie możesz, daj mi znać. Pomogę Ci w tej sprawie.
Gunaseelan
Sprawdziłem, co powiedział @SivaK. w każdym razie jest to sprytne, ale zamierzam wprowadzić pewne zmiany, aby było w pełni funkcjonalne (moim zdaniem)
MrTristan
@Gunaseelan dziękuję za rozwiązanie. Ale ma kilka błędów. Np. Kiedy usuwam drugie miejsce po przecinku, nie można go ponownie wpisać (muszę usunąć wszystkie miejsca po przecinku, aby móc go ponownie wpisać). Ponadto po usunięciu całego wpisu pojawiają się dziwne ograniczenia przy ponownym wpisywaniu.
akelec
14

InputFilter, który wymyśliłem, pozwala skonfigurować liczbę cyfr przed i po miejscu dziesiętnym. Dodatkowo nie zezwala na zera wiodące.

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)});
Łukasz
źródło
Niezłe rozwiązanie! U mnie działa, ale chcę wyłączyć kropkę wiodącą (kropka). Na przykład „.123” nie jest dozwoloną sekwencją. Jak to osiągnąć?
ibogolyubskiy
13

Wymagane są 2 cyfry po przecinku. Nie powinno być ograniczeń co do cyfr przed przecinkiem. Tak więc rozwiązaniem powinno być:

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

I używaj go jako

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

Dzięki @Pinhassi za inspirację.

Mangesh
źródło
Dobrze ... Działa dobrze
jojo
12

Moje rozwiązanie jest proste i działa idealnie!

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

Stosowanie:

myTextEdit.addTextChangedListener(new DecimalInputTextWatcher(myTextEdit, 2));
Dominik Suszczewicz
źródło
Przenieś wzorce z isValid()do konstruktora, aby uniknąć ponownego tworzenia wzorców w każdym isValid()wywołaniu.
Hemant Kaushik
6

Spróbuj użyć NumberFormat.getCurrencyInstance (), aby sformatować ciąg przed umieszczeniem go w TextView.

Coś jak:

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

Edycja - nie ma inputType dla waluty, którą mogłem znaleźć w dokumentacji. Wyobrażam sobie, że dzieje się tak, ponieważ istnieją waluty, w których nie obowiązują te same zasady dotyczące miejsc po przecinku, takie jak japoński jen.

Jak wspomniała LeffelMania, możesz poprawić dane wprowadzone przez użytkownika, używając powyższego kodu z TextWatcherustawionym na EditText.

Matthew Willis
źródło
6

Nieco ulepszone rozwiązanie @Pinhassi.

Działa bardzo dobrze. Sprawdza poprawność połączonych ciągów.

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

 }
piksel
źródło
6

Zmodyfikowałem powyższe rozwiązania i stworzyłem kolejne. Możesz ustawić liczbę cyfr przed i po przecinku.

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
źródło
To prawie to, co reisub odpowiedział na to samo pytanie tutaj w 2014 roku.
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;
    }
});

Po wyjściu z fazy edycji formatuje pole do odpowiedniego formatu. W tej chwili ma tylko 2 znaki dziesiętne. Myślę, że jest to dość łatwy sposób na zrobienie tego.

Sipka
źródło
4

Wszystkie odpowiedzi tutaj są dość złożone Starałem się to uprościć Spójrz na mój kod i sam zdecyduj -

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
źródło
działa idealnie dla mnie. Sprawdziłem cały scenariusz. Dzięki.
Amarnath Baitha
Załóżmy, że wprowadziłem 1234,56, teraz chcę go edytować w ten sposób 12378.56, nie mogę tego zrobić bez usunięcia liczby dziesiętnej.
Aman Verma,
4

Bardzo podobała mi się odpowiedź Pinhassiego, ale zauważyłem, że po wpisaniu przez użytkownika określonych cyfr po przecinku nie można już wprowadzać tekstu po lewej stronie przecinka. Problem polegał na tym, że rozwiązanie testowało tylko poprzednio wprowadzony tekst, a nie aktualnie wprowadzany. Oto moje rozwiązanie, które wstawia nowy znak do oryginalnego tekstu w celu weryfikacji.

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

I wezwanie do ustawienia filtra 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
źródło
4

Poprawiłem rozwiązanie, które używa wyrażenia regularnego Pinhassi, więc poprawnie obsługuje również przypadki brzegowe. Przed sprawdzeniem, czy dane wejściowe są poprawne, najpierw konstruowany jest końcowy ciąg, zgodnie z opisem w dokumentacji systemu 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;
    }
}

Stosowanie:

editText.setFilters(new InputFilter[] {new DecimalDigitsInputFilter(5,2)});
reisub
źródło
4

Klasa Simple Helper zapobiega wpisywaniu przez użytkownika więcej niż 2 cyfr po przecinku:

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
źródło
4

Zmieniłem odpowiedź nr 6 (autorstwa Favas Kv), ponieważ tam możesz umieścić tylko punkt na pierwszej pozycji.

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
źródło
3

Jak powiedzieli inni, dodałem tę klasę do mojego projektu i ustawiłem filtr na EditTextchcę.

Filtr jest kopiowany z odpowiedzi @ Pixel. Po prostu składam to wszystko razem.

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

Teraz ustaw filtr w EditTextten sposób.

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

Tutaj jedna ważna rzecz polega na tym, że rozwiązuje mój problem polegający na tym, że nie pozwala na wyświetlanie więcej niż dwóch cyfr po przecinku, EditTextale problem polega getText()na tym EditText, że kiedy z tego zwracam cały wpis, który wpisałem.

Np. Po nałożeniu filtra na EditText, próbowałem ustawić wejście 1.5699856987. Więc na ekranie pokazuje 1.56, co jest idealne.

Następnie chciałem użyć tych danych wejściowych do innych obliczeń, więc chciałem uzyskać tekst z tego pola wejściowego ( EditText). Kiedy zadzwoniłem mEditText.getText().toString(), zwraca 1,5699856987, co w moim przypadku było nie do przyjęcia.

Musiałem więc ponownie przeanalizować wartość po pobraniu jej z pliku EditText.

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

setScalerozwiązuje problem po pobraniu pełnego tekstu z pliku EditText.

Reaz Murshed
źródło
Witam, jak mogę się upewnić, że użytkownik nie wprowadza dziesiętnych (.), Nie powinien być w stanie wprowadzić więcej niż 2 cyfry?
ManishNegi
2

Ja też natknąłem się na ten problem. Chciałem móc ponownie wykorzystać kod w wielu EditTexts. To jest moje rozwiązanie:

Stosowanie :

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

Klasa:

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;
    }
}
Tomek
źródło
2

@Meh dla u ..

txtlist.setFilters(new InputFilter[] { new DigitsKeyListener( Boolean.FALSE,Boolean.TRUE) {

        int beforeDecimal = 7;
        int afterDecimal = 2;

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

            String etText = txtlist.getText().toString();
            String temp = txtlist.getText() + source.toString();
            if (temp.equals(".")) {
                return "0.";
            } else if (temp.toString().indexOf(".") == -1) {
                // no decimal point placed yet
                 if (temp.length() > beforeDecimal) {
                    return "";
                }
            } else {
                int dotPosition ;
                int cursorPositon = txtlistprice.getSelectionStart();
                if (etText.indexOf(".") == -1) {
                    dotPosition = temp.indexOf(".");
                }else{
                    dotPosition = etText.indexOf(".");
                }
                if(cursorPositon <= dotPosition){
                    String beforeDot = etText.substring(0, dotPosition);
                    if(beforeDot.length()<beforeDecimal){
                        return source;
                    }else{
                        if(source.toString().equalsIgnoreCase(".")){
                            return source;
                        }else{
                            return "";
                        }
                    }
                }else{
                    temp = temp.substring(temp.indexOf(".") + 1);
                    if (temp.length() > afterDecimal) {
                        return "";
                    }
                }
            }
            return super.filter(source, start, end, dest, dstart, dend);
        }
    } });
RaRa
źródło
2

Bardzo późna odpowiedź: możemy to zrobić w ten sposób:

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
źródło
2

Tutaj jest TextWatcherdozwolone tylko n liczby cyfr po przecinku.

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

Jak używać

yourEditText.addTextChangedListener(getTextWatcherAllowAfterDeci(1));
Hiren Dabhi
źródło
Działa jak marzenie!!. Dzięki Hiren :)
nisha.113a5
2

Najprostszym sposobem na osiągnięcie tego jest:

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
źródło
Prosta i trafna odpowiedź
Logo
1

Oto moje rozwiązanie:

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

Jeśli użytkownik wprowadzi liczbę zawierającą więcej niż dwie liczby po przecinku, zostanie ona automatycznie poprawiona.

Mam nadzieję, że pomogłem!

lopez.mikhael
źródło
Czy przetestowałeś ten kod? To naprawdę nie może działać, ponieważ za każdym razem, gdy wywołujesz setText (), TextWatcher uruchamia się ponownie => nieskończona pętla.
muetzenflo
06-07 08: 01: 35.006: E / AndroidRuntime (30230): java.lang.StackOverflowError Nie działa
Anjula
1

U mnie to działa dobrze. Umożliwia wprowadzenie wartości nawet po zmianie i przywróceniu fokusa. Na przykład: 123.00, 12.12, 0.01, itd ..

1. Integer.parseInt(getString(R.string.valuelength)) Określa długość danych wejściowych, do których digits.Valuesmożna uzyskać dostęp z string.xmlpliku. Zmiana wartości jest cicha. 2. Integer.parseInt(getString(R.string.valuedecimal)), dotyczy to maksymalnego limitu miejsc dziesiętnych.

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

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

Tablica EditTextwartości umożliwiająca wykonanie akcji.

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

Właśnie użyłem tablicy wartości, które zawierają wiele DecimalDigitsInputFilterNew.classplików edittext Next .

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
źródło
1

Ma to na celu rozwinięcie odpowiedzi Pinhassi - problem, na który natknąłem się, polegał na tym, że nie można było dodawać wartości przed miejscami dziesiętnymi po osiągnięciu limitu dziesiętnego. Aby rozwiązać ten problem, musimy skonstruować końcowy ciąg przed dopasowaniem wzorca.

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
źródło
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
źródło
Około 2 lata od czasu odpowiedzi. Próbowałem użyć twojego kodu, ale znalazłem problem dotyczący twojego rozwiązania, który dodajesz sourcepo et.getText(). Zawsze rozumie, że ludzie piszą na końcu pola zamiast na początku pola. StringBuilder stringBuilder = new StringBuilder(text.getText().toString()); stringBuilder.replace(dstart, dend, source.toString()); String temp = stringBuilder.toString();powinno działać. W każdym razie dzięki.
Truong Hieu
1

Utwórz nową klasę w systemie Android Kotlin o nazwie 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
}

Nazwij tę klasę następującym wierszem

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

jest zbyt wiele odpowiedzi na to samo, ale pozwoli ci to wprowadzić 8 cyfr przed przecinkiem i 2 cyfry po przecinku

inne odpowiedzi przyjmują tylko 8 cyfr

Sagar Bandamwar
źródło