【问题标题】:Limit Decimal Places in Android EditText在 Android EditText 中限制小数位数
【发布时间】:2011-07-18 11:10:28
【问题描述】:

我正在尝试编写一个可以帮助您管理财务的应用程序。我正在使用EditText 字段,用户可以在其中指定金额。

我将inputType 设置为numberDecimal 效果很好,但它允许人们输入诸如123.122 之类的数字,这对于金钱来说并不完美。

有没有办法将小数点后的字符数限制为两个?

【问题讨论】:

  • 您可以编写一个正则表达式并在编辑文本失去焦点时验证其内容。
  • 我找到了InputFilter 接口,它似乎做了我想要的developer.android.com/reference/android/text/method/…,但是我必须实现的方法filter 让我很困惑。有人写过这样的过滤器并且知道如何使用它吗?
  • 建议的解决方案是否适用于 RTL 语言环境?据我所知,他们不会...

标签: java android android-edittext


【解决方案1】:

更优雅的方式是使用正则表达式 (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)});

【讨论】:

  • 您好,仍有一些边缘情况没有得到很好的处理。例如,在我输入 2.45 后,我倾向于“将光标移动到文本的最前面”。我希望生成文本 12.45,它不允许。
  • 用户输入小数点后两位后,不允许更改小数点前的数字。
  • 很好的解决方案,但它并不完全正确。匹配器不应该检查 dest,它应该检查编辑文本中的值 (dest.subSequence(0, dstart) + source.subSequence(start, end) + dest.subSequence(dend, dest.length()))
  • Mihaela 是对的,我们应该匹配试图填充编辑文本的字符串。我发现如何连接另一个问题,例如 CharSequence match = TextUtils.concat(dest.subSequence(0, dstart), source.subSequence(start, end), dest.subSequence(dend, dest.length()));正则表达式后来引起了问题,所以我把它改成了这个 "^\\d{1," + digitsBeforeZero + "}(\\.\\d{0," + digitsAfterZero + "})?$" 但是你'之后也必须进行验证,因为“1”。对该正则表达式有效,但我们需要这样才能输入句点。
  • 您如何让逗号 (,) 也可以使用?世界上的某些地区使用逗号输入十进制数字(例如:123,45)。
【解决方案2】:

不使用正则表达式的更简单的解决方案:

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

【讨论】:

  • 什么会阻止我在字符串中插入非数字字符,例如'a'?
  • 这个:
  • 应该是:editText.setFilters(new InputFilter[] {new DecimalDigitsInputFilter(2)});
  • 这不能处理我输入“999”然后在第一个 9 之后插入小数点的情况。
  • 谢谢这个有用的。我们还可以使用像这样的长度过滤器来限制小数点前的数字。 Kotlin : edtAnyAmount.filters = arrayOf(InputFilter.LengthFilter(7),DecimalDigitsInputFilter(2))
【解决方案3】:

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”。字符强制执行。
  • 工作正常。非常感谢。
【解决方案4】:

这是一个示例 InputFilter,它只允许小数点前最多 4 位,之后最多 1 位。

edittext 允许的值:555.2555.2

编辑文本块的值:55555.2055.2555.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 });

【讨论】:

  • 不允许。需要输入
【解决方案5】:

我对@Pinhassi 解决方案做了一些修复。它处理一些情况:

1.你可以将光标移动到任何地方

2.减号处理

3.digitsbefore = 2 和 digitsafter = 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 为什么我不能输入负值(减号)?
  • 如果您设置了android:inputType="number"android:inputType="numberDecimal",它将不允许输入减号,这android:digits="0123456789.-" 没有帮助
  • 我试过android:inputType ="numbersigned",现在我可以输入减号,但我不能输入. dot! android:digits="0123456789." 没用!
  • 终于找到了解决方案android:digits="0123456789.-"android:inputType="numberDecimal|numberSigned"
【解决方案6】:

我不喜欢其他解决方案,我创建了自己的解决方案。 使用此解决方案,您不能在点前输入超过 MAX_BEFORE_POINT 位,并且小数不能超过 MAX_DECIMAL。

你不能输入多余的数字,没有其他效果! 另外,如果你写“。”它输入“0”。

  1. 将布局中的EditText设置为:

    android: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);
            targetEditText.setSelection(str2.length());
        }
      }
    });
    
  3. 包括这个函数:

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

它已经完成了!

【讨论】:

  • 干杯。当另一个不适合我的时候工作得很好
  • 这应该是已接受的答案.. 很完美.. 这里所有条件都满足了。
  • 在所有高度投票的答案中,这个答案实际上对我有用。
  • 干得好!我尝试了所有组合,似乎效果很好。谢谢。
  • 我不知道它是如何工作的,但它就像一个魅力。
【解决方案7】:

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

此解决方案将不允许用户在小数点后输入超过两位数。您也可以在小数点前输入任意位数。我希望这将有所帮助。谢谢。

【讨论】:

  • 抱歉信息迟到了。不要忘记用-1 初始化count。然后只有这样才能正常工作。 int count = -1;
  • Gunaseelan - 我尝试了上面的代码,它运行良好。但是当我删除输入的文本并再次开始输入时,它只输入一个数字,任何解决方案......
  • @SivaK 没办法的朋友。如果您删除然后键入它将接受至少 100 位数字。我不知道你如何访问这个listener。无论如何,请查看我的博客post。你可能会有一个想法。如果你不能请告诉我。我会帮你解决这个问题。
  • 我已经验证了@SivaK 所说的话。这在任何情况下都很聪明,但我将对此进行一些编辑,以使其功能齐全(在我看来)
  • @Gunaseelan 感谢您的解决方案。但它有一些错误。例如,当我删除第二个小数时,不可能再次输入(我必须删除所有小数才能再次输入)。此外,删除整个条目后,再次输入时会出现一些奇怪的限制。
【解决方案8】:

要求小数点后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 的启发。

【讨论】:

  • 好...工作正常
  • 简单明了的答案,它按预期工作。
【解决方案9】:

我提出的 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”是不允许的序列。如何做到这一点?
  • 这个解决方案允许在开头打点
【解决方案10】:

我的解决方案很简单,效果很好!

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() 调用时重新创建模式。
【解决方案11】:

实现这一目标的最简单方法是:

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

【讨论】:

  • 简单而完美的答案
  • 这是最好的答案之一。简单而有效!一项建议是检查 is et is not null。
  • 非常好。和我一起工作。非常感谢。
【解决方案12】:

在将字符串放入 TextView 之前,尝试使用 NumberFormat.getCurrencyInstance() 格式化字符串。

类似:

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

编辑 - 我在文档中找不到货币的 inputType。我想这是因为有些货币不遵循相同的小数位规则,例如日元。

正如 LeffelMania 所提到的,您可以使用上面的代码和在您的 EditText 上设置的 TextWatcher 来更正用户输入。

【讨论】:

    【解决方案13】:

    稍微改进了@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 "";
    }
    
     }
    

    【讨论】:

      【解决方案14】:

      我已经修改了上述解决方案并创建了以下解决方案。您可以设置小数点前后的位数。

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

      }

      【讨论】:

      • 这几乎是 reisub 在 2014 年在同一问题上回答的内容。
      【解决方案15】:
      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 个十进制字符。我认为这是非常简单的方法。

      【讨论】:

        【解决方案16】:

        这里的所有答案都非常复杂,我试图让它变得更简单。看看我的代码并自己决定 -

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

        【讨论】:

        • 它非常适合我。我检查了所有场景。谢谢。
        • 假设我输入了1234.56,现在我想像这样编辑12378.56,不删除小数就不行了。
        【解决方案17】:

        我真的很喜欢 Pinhassi 的回答,但注意到用户在小数点后输入了指定的数字后,您无法再在小数点左侧输入文本。问题是该解决方案只测试了之前输入的文本,而不是当前输入的文本。所以这是我的解决方案,将新字符插入到原始文本中进行验证。

        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
        

        【讨论】:

          【解决方案18】:

          我改进了 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)});
          

          【讨论】:

            【解决方案19】:

            这里有一个简单的 Helper 类来防止用户在小数点后输入超过 2 位数字:

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

            【讨论】:

              【解决方案20】:

              我已经更改了答案 №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;
                  }
              }};
              

              【讨论】:

                【解决方案21】:

                在 Android kotlin 中创建一个名为 DecimalDigitsInputFilter 的新类

                class DecimalDigitsInputFilter(digitsBeforeDecimal: Int, digitsAfterDecimal: Int) : InputFilter {
                
                    var mPattern: Pattern = Pattern.compile("[0-9]{0,$digitsBeforeDecimal}+((\\.[0-9]{0,$digitsAfterDecimal})?)||(\\.)?")
                
                    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 位数字

                【讨论】:

                • 来吧 Android SDK。现在是 2021 年,需要做大量工作来限制小数点。可怕的是,Android 开发人员甚至不再意识到他们为完成这项工作付出了太多。
                【解决方案22】:

                就像其他人说的,我在我的项目中添加了这个类,并将过滤器设置为我想要的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 中不允许在小数点后显示超过两位数的问题,但问题是当我从 EditText 中的 getText() 时,它会返回整个输入我打字。

                例如,在对EditText 应用过滤器后,我尝试设置输入 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 获取全文后,就在这里发挥作用。

                【讨论】:

                • 你好,我怎样才能确定如果用户不输入小数(。)他应该不能输入超过 2 位数字?
                【解决方案23】:

                这是我的解决方案:

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

                如果用户输入的数字的小数点后有两个以上的数字,则会自动更正。

                希望对我有所帮助!

                【讨论】:

                • 你测试过这段代码吗?它不能真正起作用,因为每当您调用 setText() 时,TextWatcher 都会再次触发 => 无限循环。
                • 06-07 08:01:35.006: E/AndroidRuntime(30230): java.lang.*Error 不工作
                【解决方案24】:

                我也遇到过这个问题。我希望能够在许多 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;
                    }
                }
                

                【讨论】:

                  【解决方案25】:

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

                  【讨论】:

                    【解决方案26】:

                    一个很晚的回应: 我们可以这样做:

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

                    【讨论】:

                      【解决方案27】:

                      这是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 :)
                      【解决方案28】:

                      Kotlin 中的简单 BindingAdapter:

                      @BindingAdapter("maxDecimalPlaces")
                      fun TextInputEditText.limitDecimalPlaces(maxDecimalPlaces: Int) {
                          filters += InputFilter { source, _, _, dest, dstart, dend ->
                              val value = if (source.isEmpty()) {
                                  dest.removeRange(dstart, dend)
                              } else {
                                  StringBuilder(dest).insert(dstart, source)
                              }
                              val matcher = Pattern.compile("([1-9][0-9]*)|([1-9][0-9]*\\.[0-9]{0,$maxDecimalPlaces})|(\\.[0-9]{0,$maxDecimalPlaces})").matcher(value)
                              if (!matcher.matches()) "" else null
                          }
                      }
                      

                      【讨论】:

                        【解决方案29】:

                        这对我来说很好。即使在焦点更改并取回之后,它也允许输入值。例如:123.0012.120.01等。

                        1.Integer.parseInt(getString(R.string.valuelength)) 指定从string.xml 文件访问的输入digits.Values 的长度。更改值很容易。 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 文件。

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

                        【讨论】:

                          【解决方案30】:

                          这是基于 pinhassi 的回答 - 我遇到的问题是,一旦达到小数限制,您就无法在小数点前添加值。为了解决这个问题,我们需要在进行模式匹配之前构造最终的字符串。

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

                          【讨论】: