Ограничение десятичных знаков в Android EditText

125

Я пытаюсь написать приложение, которое поможет вам управлять своими финансами. Я использую EditTextполе, в котором пользователь может указать сумму денег.

Я установил inputTypeв numberDecimalкоторой работает отлично, за исключением того, что это позволяет людям вводить цифры , такие как 123.122, не идеально подходит для денег.

Есть ли способ ограничить количество символов после десятичной точки до двух?

Константин Вайц
источник
Вы можете написать регулярное выражение и проверить содержимое текста редактирования, когда он теряет фокус.
blindstuff
Я нашел InputFilterинтерфейс, кажется, он делает то, что я хочу, developer.android.com/reference/android/text/method/… , но метод, filterкоторый я должен реализовать, меня довольно смущает. Кто-нибудь уже писал такой фильтр и умеет им пользоваться?
Konstantin Weitz
Работает ли какое-либо из предложенных решений для локалей RTL? Насколько я могу судить, они не будут ...
Ник

Ответы:

118

Более элегантным способом было бы использование регулярного выражения (regex) следующим образом:

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

}

Чтобы использовать это:

editText.setFilters(new InputFilter[] {new DecimalDigitsInputFilter(5,2)});
Асаф Пинхасси
источник
32
Привет, есть некоторые крайние случаи, которые все еще не решаются. Например, после того, как я набираю 2.45, я стараюсь «перемещать курсор в начало текста». Хочу выдать текст 12.45, не позволит.
Cheok Yan Cheng
3
он не позволяет изменять цифры перед десятичной запятой после того, как пользователь ввел 2 цифры после десятичной точки.
Gaurav Singla
9
Отличное решение, но не совсем правильное. Сопоставитель не должен проверять dest, он должен проверять значение в edittext (dest.subSequence (0, dstart) + source.subSequence (start, end) + dest.subSequence (dend, dest.length ()))
Михаэла Романка
7
Михаэла права, мы должны сопоставлять строку, которая пытается заполнить текст редактирования. Я нашел, как объединить другой вопрос, например CharSequence match = TextUtils.concat (dest.subSequence (0, dstart), source.subSequence (start, end), dest.subSequence (dend, dest.length ())); Впоследствии регулярное выражение вызывало проблемы, поэтому я изменил его на "^ \\ d {1," + digitsBeforeZero + "} (\\. \\ d {0," + digitsAfterZero + "})? $" Но вы ' После этого тоже нужно будет выполнить проверку, потому что «1». действительно с этим регулярным выражением, но нам это нужно, чтобы можно было ввести точку.
dt0
6
Как бы вы сделали это и для запятых (,)? Некоторые регионы мира вводят десятичные числа через запятую (например: 123,45).
Эндрю
65

Более простое решение без использования регулярного выражения:

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

}

Использовать:

editText.setFilters(new InputFilter[] {new DecimalDigitsInputFilter(2)});
peceps
источник
Что могло бы помешать мне вставить в строку нечисловые символы, такие как 'a'?
Константин Вайц
4
Это: <EditText ... android: inputType = "number" />
peceps
1
Это должно быть: editText.setFilters (новый InputFilter [] {новый DecimalDigitsInputFilter (2)});
frak
6
Это не относится к случаю, когда я
набираю
1
Спасибо, это полезно. Мы также можем ограничить количество цифр перед десятичной точкой, используя фильтр длины, подобный этому. Котлин: edtAnyAmount.filters = arrayOf <InputFilter> (InputFilter.LengthFilter (7), DecimalDigitsInputFilter (2))
Faldu Jaldeep
37

Эта реализация InputFilterрешает проблему.

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);
    }
}
Константин Вайц
источник
Могу ли я узнать, есть ли причина, по которой нам нужно возвращать SpannableStringBuilder вместо null? Я тестирую его с помощью null, он тоже хорошо работает. Кроме того, есть ли необходимость в наследовании от DigitsKeyListener? При использовании android: inputType = "numberDecimal" выполнит все "0123456789." исполнение персонажей.
Cheok Yan Cheng
1
Работает отлично. Большое спасибо.
Андрей Ауласка
34

Вот пример InputFilter, который допускает максимум 4 цифры до десятичной точки и максимум 1 цифру после нее .

Значения, которые позволяет edittext: 555.2 , 555 , .2

Значения, которые редактируют блоки текста: 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
источник
не пускает. набирается
AmiNadimi 04
22

Я сделал несколько исправлений для решения @Pinhassi. Он обрабатывает некоторые случаи:

1. вы можете перемещать курсор куда угодно

2. обработка знака минус

3. цифры перед = 2 и цифры после = 4, и вы вводите 12,4545. Тогда, если вы хотите удалить ".", Это не позволит.

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
источник
Я думаю, это должно быть идеальное решение, сделавшее мой день лучше. Спасибо.
Pratik Butani
1
@Omkar, это неправильно. это условие всегда будет истинным, даже если length> 0, dest.length () == 0 всегда истинен, даже если вы редактируете весь текст больше 0 ...
user924
@Omkar, пожалуйста, удалите свой комментарий
user924
@android_dev, почему я не могу ввести отрицательные значения (минус)?
user924
ЕСЛИ вы установили android:inputType="number"или android:inputType="numberDecimal"он не позволяет вводить минус, это android:digits="0123456789.-"не помогает
user924
18

Другое решение мне не нравится, и я создал свое. С помощью этого решения вы не можете ввести больше, чем MAX_BEFORE_POINT цифр перед точкой, а десятичные дроби не могут быть больше MAX_DECIMAL.

Вы просто не можете набрать лишнюю цифру, никаких других эффектов! Дополнительно, если вы напишете "." он набирает «0».

  1. Установите EditText в макете на:

    андроид: inputType = "numberDecimal"

  2. Добавьте слушателя в свой onCreate. Если вы хотите изменить количество цифр до и после точки, отредактируйте вызов на PerfectDecimal (str, NUMBER_BEFORE_POINT, NUMBER_DECIMALS), здесь установлено 3 и 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. Включите эту Funcion:

    public String PerfectDecimal(String str, int MAX_BEFORE_POINT, int MAX_DECIMAL){
      if(str.charAt(0) == '.') str = "0"+str;
      int max = str.length();
    
      String rFinal = "";
      boolean after = false;
      int i = 0, up = 0, decimal = 0; char t;
      while(i < max){
        t = str.charAt(i);
        if(t != '.' && after == false){
            up++;
            if(up > MAX_BEFORE_POINT) return rFinal;
        }else if(t == '.'){
            after = true;
        }else{
            decimal++;
            if(decimal > MAX_DECIMAL)
                return rFinal;
        }
        rFinal = rFinal + t;
        i++;
      }return rFinal;
    }

Готово!

Apoleo
источник
1
Приветствия. Работает очень хорошо, когда другой не работает для меня
Медведь
1
Это должен быть принятый ответ .. Он идеален .. Здесь выполнены все условия.
Nayan
1
Несмотря на все ответы, получившие большое количество голосов, этот ответ действительно сработал для меня.
Рохит Мандивал,
Отлично сработано! Я перепробовал все комбинации и, кажется, отлично работает. Спасибо.
Акелек,
Я не знаю, как это работает, но это просто чудо.
Вонсук
17

Я добился этого с помощью TextWatcherследующего способа

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

Это решение не позволит пользователю вводить более двух цифр после десятичной точки. Также вы можете ввести любое количество цифр до десятичной точки. См. Этот блог http://v4all123.blogspot.com/2013/05/set-limit-for-fraction-in-decimal.html, чтобы установить фильтр для нескольких EditText. Я надеюсь, это поможет. Спасибо.

Gunaseelan
источник
Извините за запоздалую информацию. НЕ забудьте инициализировать countс помощью -1. Только тогда это будет работать правильно. int count = -1;
Gunaseelan
Gunaseelan - я попробовал приведенный выше код, и он работает хорошо. Но когда я удаляю набранный текст и снова начинаю печатать, он набирает только одну цифру, любое решение для этого ...
Siva K
@SivaK Ни за что, друг. Если вы удалите, а затем введите, он будет принимать минимум 100 цифр. Я не знаю, как у вас к этому доступ listener. В любом случае , пожалуйста, смотрите в моем блоге пост . Вы можете получить представление. Если не можете, дайте мне знать. Помогу вам в этом вопросе.
Gunaseelan 05
Я проверил, что сказал @SivaK. в любом случае это умно, но я собираюсь внести в это некоторые правки, чтобы он стал полностью функциональным (на мой взгляд),
MrTristan
@Gunaseelan спасибо за ваше решение. Но в нем есть баги. Например, когда я удаляю второй десятичный знак, его невозможно ввести снова (я должен удалить все десятичные знаки, чтобы иметь возможность ввести его снова). Кроме того, после удаления всей записи при повторном вводе возникают некоторые странные ограничения.
Акелек,
14

Придуманный мной InputFilter позволяет настраивать количество цифр до и после десятичного разряда. Кроме того, он запрещает нули в начале.

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)});
Люк
источник
Хорошее решение! У меня это работает, но я хочу запретить начальную точку (точку). Например, «.123» - недопустимая последовательность. Как этого добиться?
ибоголюбский
13

Требуется 2 цифры после запятой. Количество цифр перед десятичной точкой не должно быть ограничено . Итак, решение должно быть таким,

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

И используйте его как

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

Спасибо @Pinhassi за вдохновение.

Mangesh
источник
Хорошо ... Работает нормально
jojo
12

Мое решение простое и отлично работает!

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

Использование:

myTextEdit.addTextChangedListener(new DecimalInputTextWatcher(myTextEdit, 2));
Доминик Сущевич
источник
Переместите шаблоны из isValid()в конструктор, чтобы избежать повторного создания шаблонов при каждом isValid()вызове.
Хемант Кошик
6

Попробуйте использовать NumberFormat.getCurrencyInstance () для форматирования строки перед тем, как поместить ее в TextView.

Что-то вроде:

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

Изменить - нет inputType для валюты, которую я мог найти в документах. Я полагаю, это потому, что есть некоторые валюты, которые не подчиняются тому же правилу для десятичных знаков, например, японская иена.

Как упоминала LeffelMania, вы можете исправить вводимые пользователем данные, используя приведенный выше код с TextWatcherустановленным на вашем EditText.

Мэтью Уиллис
источник
6

Немного улучшенное решение @Pinhassi.

Работает очень хорошо. Он проверяет сцепленные строки.

public class DecimalDigitsInputFilter implements InputFilter {

Pattern mPattern;

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

}

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

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

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

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

    String result = destPrefix + formatedSource + destSuffix;

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

    Matcher matcher = mPattern.matcher(result);

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

    return "";
}

 }
пиксель
источник
6

Я изменил вышеуказанные решения и создал следующее. Вы можете установить количество цифр до и после десятичной точки.

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
источник
Это почти то, что reisub ответил на тот же вопрос здесь, в 2014 году.
Мехул Джойсар
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;
    }
});

Когда вы выходите из фазы редактирования, он форматирует поле до нужного формата. На данный момент в нем всего 2 десятичных знака. Я думаю, это довольно простой способ сделать это.

Sipka
источник
4

Все ответы здесь довольно сложные, я постарался сделать это намного проще. Посмотрите на мой код и решите сами -

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
источник
его работа идеально подходит для меня. Я проверил весь сценарий. Спасибо.
Амарнатх Байта,
Предположим, я ввел 1234,56, теперь я хочу отредактировать его как 12378,56, я не могу сделать это без удаления десятичной дроби.
Аман Верма
4

Мне очень понравился ответ Пинхасси, но я заметил, что после того, как пользователь ввел указанные числовые цифры после десятичной точки, вы больше не могли вводить текст слева от десятичной точки. Проблема заключалась в том, что решение проверяло только предыдущий введенный текст, а не текущий вводимый текст. Итак, вот мое решение, которое вставляет новый символ в исходный текст для проверки.

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

И призыв установить фильтр editText

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

Ouput with two decimal places
05-22 15:25:33.434: D/debug(30524): [0-9]*+((\.[0-9]{0,2})?)||(\.)?
05-22 15:25:33.434: D/debug(30524): source: 5, start: 0, end:1, dest: 123.4, dstart: 5, dend: 5
05-22 15:25:33.434: D/debug(30524): lsStart : 123.4
05-22 15:25:33.434: D/debug(30524): lsInsert: 5
05-22 15:25:33.434: D/debug(30524): lsEnd   : 
05-22 15:25:33.434: D/debug(30524): lsText  : 123.45
05-22 15:25:33.434: D/debug(30524): loMatcher.matches(): true, lsText: 123.45

Ouput inserting a 5 in the middle
05-22 15:26:17.624: D/debug(30524): [0-9]*+((\.[0-9]{0,2})?)||(\.)?
05-22 15:26:17.624: D/debug(30524): source: 5, start: 0, end:1, dest: 123.45, dstart: 2, dend: 2
05-22 15:26:17.624: D/debug(30524): lsStart : 12
05-22 15:26:17.624: D/debug(30524): lsInsert: 5
05-22 15:26:17.624: D/debug(30524): lsEnd   : 3.45
05-22 15:26:17.624: D/debug(30524): lsText  : 1253.45
05-22 15:26:17.624: D/debug(30524): loMatcher.matches(): true, lsText: 1253.45
Дэвид Мерри
источник
4

Я улучшил решение, которое использует регулярное выражение от Pinhassi, поэтому оно также правильно обрабатывает крайние случаи. Перед проверкой правильности ввода сначала создается последняя строка, как описано в документации 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;
    }
}

Использование:

editText.setFilters(new InputFilter[] {new DecimalDigitsInputFilter(5,2)});
reisub
источник
4

Класс Simple Helper предназначен для предотвращения ввода пользователем более двух цифр после запятой:

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
источник
4

Я изменил ответ №6 (автор Favas Kv), потому что в нем можно поставить точку на первой позиции.

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
источник
3

Как говорили другие, я добавил этот класс в свой проект и установил фильтр на « EditTextЯ хочу».

Фильтр скопирован из ответа @ Pixel. Я просто собираю все вместе.

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

Теперь установите фильтр в вашем EditTextвот так.

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

Здесь одна важная вещь заключается в том, что он решает мою проблему, заключающуюся в том, что я не позволяю отображать более двух цифр после десятичной точки в этом, EditTextно проблема в том, что когда я getText()из этого EditText, он возвращает весь введенный мной ввод.

Например, после применения фильтра к EditText, я попытался установить input 1.5699856987. Итак, на экране отображается 1,56, что идеально.

Затем я хотел использовать этот ввод для некоторых других вычислений, поэтому я хотел получить текст из этого поля ввода (EditText ). Когда я позвонилmEditText.getText().toString() он вернул 1,5699856987, что в моем случае было неприемлемо.

Поэтому мне пришлось снова проанализировать значение после получения его из EditText.

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

setScaleделает фокус здесь после получения полного текста из EditText.

Реаз Муршед
источник
Здравствуйте, как я могу убедиться, что если пользователь не вводит десятичную дробь (.), Он не может вводить более двух цифр?
ManishNegi
2

Я тоже столкнулся с этой проблемой. Я хотел иметь возможность повторно использовать код во многих EditTexts. Это мое решение:

Использование :

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

Класс:

public static class CurrencyFormat implements TextWatcher {

    public void onTextChanged(CharSequence arg0, int start, int arg2,int arg3) {}

    public void beforeTextChanged(CharSequence arg0, int start,int arg2, int arg3) {}

    public void afterTextChanged(Editable arg0) {
        int length = arg0.length();
        if(length>0){
            if(nrOfDecimal(arg0.toString())>2)
                    arg0.delete(length-1, length);
        }

    }


    private int nrOfDecimal(String nr){
        int len = nr.length();
        int pos = len;
        for(int i=0 ; i<len; i++){
            if(nr.charAt(i)=='.'){
                pos=i+1;
                    break;
            }
        }
        return len-pos;
    }
}
Том
источник
2

@Meh для тебя ..

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

        int beforeDecimal = 7;
        int afterDecimal = 2;

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

            String etText = txtlist.getText().toString();
            String temp = txtlist.getText() + source.toString();
            if (temp.equals(".")) {
                return "0.";
            } else if (temp.toString().indexOf(".") == -1) {
                // no decimal point placed yet
                 if (temp.length() > beforeDecimal) {
                    return "";
                }
            } else {
                int dotPosition ;
                int cursorPositon = txtlistprice.getSelectionStart();
                if (etText.indexOf(".") == -1) {
                    dotPosition = temp.indexOf(".");
                }else{
                    dotPosition = etText.indexOf(".");
                }
                if(cursorPositon <= dotPosition){
                    String beforeDot = etText.substring(0, dotPosition);
                    if(beforeDot.length()<beforeDecimal){
                        return source;
                    }else{
                        if(source.toString().equalsIgnoreCase(".")){
                            return source;
                        }else{
                            return "";
                        }
                    }
                }else{
                    temp = temp.substring(temp.indexOf(".") + 1);
                    if (temp.length() > afterDecimal) {
                        return "";
                    }
                }
            }
            return super.filter(source, start, end, dest, dstart, dend);
        }
    } });
РаРа
источник
2

Очень поздний ответ: мы можем сделать это просто так:

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
источник
2

Вот те, TextWatcherкоторые позволяют только n цифр после десятичной точки.

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

Как пользоваться

yourEditText.addTextChangedListener(getTextWatcherAllowAfterDeci(1));
Хирен Дабхи
источник
Работает как шарм !!. Спасибо Hiren :)
nisha.113a5
2

Самый простой способ добиться этого:

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) {
    }
});
Саша Илич
источник
Простой и точный ответ
Logo
1

Вот мое решение:

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

Если пользователь вводит число, состоящее из более чем двух цифр после десятичной точки, оно будет автоматически исправлено.

Надеюсь, я помог!

lopez.mikhael
источник
Вы тестировали этот код? Это не может работать, потому что всякий раз, когда вы вызываете setText (), TextWatcher снова запускается => бесконечный цикл.
muetzenflo
06-07 08: 01: 35.006: E / AndroidRuntime (30230): java.lang.StackOverflowError Не работает
Анжула
1

У меня это отлично работает. Это позволяет вводить значение даже после изменения фокуса и возврата. Например: 123.00, 12.12, 0.01и т.д ..

1. Integer.parseInt(getString(R.string.valuelength)) Определяет длину входных данных, digits.Valuesк которым осуществляется доступ из string.xmlфайла. Значения легко изменить. 2. Integer.parseInt(getString(R.string.valuedecimal))Это максимальный предел десятичных разрядов.

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

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

Массив EditTextзначений, позволяющий совершить действие.

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

Я просто использовал массив значений, содержащий несколько DecimalDigitsInputFilterNew.classфайлов 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;
    }
}
Карти Кейян
источник
1

Это основано на ответе Пиньясси - проблема, с которой я столкнулся, заключалась в том, что вы не могли добавлять значения до десятичной дроби после достижения десятичного предела. Чтобы решить эту проблему, нам нужно создать последнюю строку перед сопоставлением с шаблоном.

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

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

public class DecimalLimiter implements InputFilter
{
    Pattern mPattern;

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

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

        Matcher matcher = mPattern.matcher(sb.toString());
        if(!matcher.matches())
            return "";
        return null;
    }
}
Роберт Конли
источник
1
et = (EditText) vw.findViewById(R.id.tx_edittext);

et.setFilters(new InputFilter[] {
        new DigitsKeyListener(Boolean.FALSE, Boolean.TRUE) {
            int beforeDecimal = 5, afterDecimal = 2;

            @Override
            public CharSequence filter(CharSequence source, int start, int end,
                    Spanned dest, int dstart, int dend) {
                String temp = et.getText() + source.toString();

                if (temp.equals(".")) {
                    return "0.";
                }
                else if (temp.toString().indexOf(".") == -1) {
                    // no decimal point placed yet
                    if (temp.length() > beforeDecimal) {
                        return "";
                    }
                } else {
                    temp = temp.substring(temp.indexOf(".") + 1);
                    if (temp.length() > afterDecimal) {
                        return "";
                    }
                }

                return super.filter(source, start, end, dest, dstart, dend);
            }
        }
});
Паван Шукла
источник
Примерно 2 года с момента вашего ответа. Я попытался использовать ваш код, а затем обнаружил, что проблема с вашим решением - добавить его sourceпосле et.getText(). Он всегда понимает, что люди печатают в конце поля, а не в начале поля. StringBuilder stringBuilder = new StringBuilder(text.getText().toString()); stringBuilder.replace(dstart, dend, source.toString()); String temp = stringBuilder.toString();должно сработать. Спасибо, в любом случае.
Чыонг Хьеу
1

Создайте новый класс в Android kotlin с именем 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
}

Вызовите этот класс со следующей строкой

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

есть слишком много ответов для одного и того же, но это позволит вам ввести 8 цифр перед десятичной и 2 цифры после десятичной

другие ответы принимают только 8 цифр

Сагар Бандамвар
источник