2

I have an app with single numberdecimal EditText. I want to filter digits before point from 1 to 120. And I want to allow user to input one digit after point. Example: User can input 1, 2.3, 23, 45.7, 89.6, 119.9, 120.0 etc. User can't input 3.34, 45.76, 89.652, 120.00, 121.00 etc. How I can do that filter? Please, write me filter that work with EditText on my terms)

musica
  • 1,373
  • 3
  • 15
  • 34

2 Answers2

2
mEditText.setFilters(new InputFilter[]{new DigitsInputFilter(maxDigitsBeforeDot, maxDigitsAfterDot, maxValue)});

If you don't want to limit digits before or after dot just put Integer.MAX_VALUE, to disable max value limit use Double.POSITIVE_INFINITY.

public class DigitsInputFilter implements InputFilter {

    private final String DOT = ".";

    private int mMaxIntegerDigitsLength;
    private int mMaxDigitsAfterLength;
    private double mMax;


    public DigitsInputFilter(int maxDigitsBeforeDot, int maxDigitsAfterDot, double maxValue) {
        mMaxIntegerDigitsLength = maxDigitsBeforeDot;
        mMaxDigitsAfterLength = maxDigitsAfterDot;
        mMax = maxValue;
    }

    @Override
    public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {
        String allText = getAllText(source, dest, dstart);
        String onlyDigitsText = getOnlyDigitsPart(allText);

        if (allText.isEmpty()) {
            return null;
        } else {
            double enteredValue;
            try {
                enteredValue = Double.parseDouble(onlyDigitsText);
            } catch (NumberFormatException e) {
                return "";
            }
            return checkMaxValueRule(enteredValue, onlyDigitsText);
        }
    }


    private CharSequence checkMaxValueRule(double enteredValue, String onlyDigitsText) {
        if (enteredValue > mMax) {
            return "";
        } else {
            return handleInputRules(onlyDigitsText);
        }
    }

    private CharSequence handleInputRules(String onlyDigitsText) {
        if (isDecimalDigit(onlyDigitsText)) {
            return checkRuleForDecimalDigits(onlyDigitsText);
        } else {
            return checkRuleForIntegerDigits(onlyDigitsText.length());
        }
    }

    private boolean isDecimalDigit(String onlyDigitsText) {
        return onlyDigitsText.contains(DOT);
    }

    private CharSequence checkRuleForDecimalDigits(String onlyDigitsPart) {
        String afterDotPart = onlyDigitsPart.substring(onlyDigitsPart.indexOf(DOT), onlyDigitsPart.length() - 1);
        if (afterDotPart.length() > mMaxDigitsAfterLength) {
            return "";
        }
        return null;
    }

    private CharSequence checkRuleForIntegerDigits(int allTextLength) {
        if (allTextLength > mMaxIntegerDigitsLength) {
            return "";
        }
        return null;
    }

    private String getOnlyDigitsPart(String text) {
        return text.replaceAll("[^0-9?!\\.]", "");
    }

    private String getAllText(CharSequence source, Spanned dest, int dstart) {
        String allText = "";
        if (!dest.toString().isEmpty()) {
            if (source.toString().isEmpty()) {
                allText = deleteCharAtIndex(dest, dstart);
            } else {
                allText = new StringBuilder(dest).insert(dstart, source).toString();
            }
        }
        return allText;
    }

    private String deleteCharAtIndex(Spanned dest, int dstart) {
        StringBuilder builder = new StringBuilder(dest);
        builder.deleteCharAt(dstart);
        return builder.toString();
    }
}
Taras Smakula
  • 331
  • 2
  • 7
0

You can implement your logic in onTextChanged method of TextWatcher

editText.addTextChangedListener(new TextWatcher() {

       @Override
       public void afterTextChanged(Editable s) {}

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

       @Override    
       public void onTextChanged(CharSequence s, int start,

       }
      });

Update For beginning You can use something like this:

editText.addTextChangedListener(new TextWatcher() {

            boolean ignoreChange = false;

            @Override
            public void afterTextChanged(Editable s) {}

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

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

                if (!ignoreChange) {
                    String string = charSequence.toString();

                    String[] parts = string.split("\\.");
                    if (parts.length > 1) {
                        String digitsAfterPoint = parts[1];
                        if (digitsAfterPoint.length() > 1) {
                            ignoreChange = true;
                            editText.setText(string.substring(0, string.indexOf(".") + 2));
                            editText.setSelection(editText.getText().length());
                            ignoreChange = false;
                        }
                    }
                }

            }

        });

But You also should control duplicates of "." symbol or something else.

Update #2 For value You can use, for example, that code:

        editText.addTextChangedListener(new TextWatcher() {

            boolean ignoreChange = false;
            String beforeChange;

            @Override
            public void afterTextChanged(Editable s) {}

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

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

                if (!ignoreChange) {
                    String string = charSequence.toString();

                    String[] parts = string.split("\\.");
                    if (parts.length > 1) {
                        String digitsAfterPoint = parts[1];
                        if (digitsAfterPoint.length() > 1) {
                            ignoreChange = true;
                            editText.setText(string.substring(0, string.indexOf(".") + 2));
                            editText.setSelection(editText.getText().length());
                            ignoreChange = false;
                        }
                    }

                    String strValue = editText.getText().toString();
                    float value = Float.valueOf(strValue);
                    if (value < 1 || value > 120) {
                        ignoreChange = true;
                        editText.setText(beforeChange);
                        editText.setSelection(editText.getText().length());
                        ignoreChange = false;
                    }
                    beforeChange = editText.getText().toString();
                }

            }

But You should carry about correct values input. And think about NumberPicker or Spinner for each digit.

Andrii Omelchenko
  • 13,183
  • 12
  • 43
  • 79