3

I have custom EditText which will convert the input and also able to reverse it. However it will always make the input decimal, with 1 or 2 values behind the input. Right now I am making some calculation app, which need integer. How to make this custom EditText to just take integer input and output?

The code:

@TargetApi(Build.VERSION_CODES.GINGERBREAD)
public class NumericEditText extends EditText {
    private final char GROUPING_SEPARATOR = DecimalFormatSymbols.getInstance().getGroupingSeparator();
    private final char DECIMAL_SEPARATOR = DecimalFormatSymbols.getInstance().getDecimalSeparator();
    private final String LEADING_ZERO_FILTER_REGEX = "^0+(?!$)";

    private String mDefaultText = null;
    private String mPreviousText = "";
    private String mNumberFilterRegex = "[^\\d\\" + DECIMAL_SEPARATOR + "]";

    /**
     * Interface to notify listeners when numeric value has been changed or cleared
     */
    public interface NumericValueWatcher {
        /**
         * Fired when numeric value has been changed
         * @param newValue new numeric value
         */
        void onChanged(double newValue);

        /**
         * Fired when numeric value has been cleared (text field is empty)
         */
        void onCleared();
    }

    private List<NumericValueWatcher> mNumericListeners = new ArrayList<NumericValueWatcher>();
    private final TextWatcher mTextWatcher = new TextWatcher() {
        private boolean validateLock = false;

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

            // valid decimal number should not have more than 2 decimal separators
            if (StringUtils.countMatches(s.toString(), String.valueOf(DECIMAL_SEPARATOR)) > 1) {
                validateLock = true;
                setText(mPreviousText); // cancel change and revert to previous input
                setSelection(mPreviousText.length());
                validateLock = false;
                return;
            }

            if (s.length() == 0) {
                handleNumericValueCleared();
                return;
            }

            setTextInternal(format(s.toString()));
            setSelection(getText().length());
            handleNumericValueChanged();
        }

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

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

    private void handleNumericValueCleared() {
        mPreviousText = "";
        for (NumericValueWatcher listener : mNumericListeners) {
            listener.onCleared();
        }
    }

    private void handleNumericValueChanged() {
        mPreviousText = getText().toString();
        for (NumericValueWatcher listener : mNumericListeners) {
            listener.onChanged(getNumericValue());
        }
    }

    public NumericEditText(Context context, AttributeSet attrs) {
        super(context, attrs);

        addTextChangedListener(mTextWatcher);
        setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // disable moving cursor
                setSelection(getText().length());
            }
        });
    }

    /**
     * Add listener for numeric value changed events
     * @param watcher   listener to add
     */
    public void addNumericValueChangedListener(NumericValueWatcher watcher) {
        mNumericListeners.add(watcher);
    }

    /**
     * Remove all listeners to numeric value changed events
     */
    public void removeAllNumericValueChangedListeners() {
        while (!mNumericListeners.isEmpty()) {
            mNumericListeners.remove(0);
        }
    }

    /**
     * Set default numeric value and how it should be displayed, this value will be used if
     * {@link #clear} is called
     * @param defaultNumericValue   numeric value
     * @param defaultNumericFormat  display format for numeric value
     */
    public void setDefaultNumericValue(double defaultNumericValue, final String defaultNumericFormat) {
        mDefaultText = String.format(defaultNumericFormat, defaultNumericValue);
        setTextInternal(mDefaultText);
    }

    /**
     * Clear text field and replace it with default value set in {@link #setDefaultNumericValue} if
     * any
     */
    public void clear() {
        setTextInternal(mDefaultText != null ? mDefaultText : "");
        if (mDefaultText != null) {
            handleNumericValueChanged();
        }
    }

    /**
     * Return numeric value repesented by the text field
     * @return  numeric value or {@link Double.NaN} if not a number
     */
    public double getNumericValue() {
        String original = getText().toString().replaceAll(mNumberFilterRegex, "");
        try {
            return NumberFormat.getInstance().parse(original).doubleValue();
        } catch (ParseException e) {
            return Double.NaN;
        }
    }

    /**
     * Add grouping separators to string
     * @param original  original string, may already contains incorrect grouping separators
     * @return  string with correct grouping separators
     */
    private String format(final String original) {
        final String[] parts = original.split("\\" + DECIMAL_SEPARATOR, -1);
        String number = parts[0] // since we split with limit -1 there will always be at least 1 part
                .replaceAll(mNumberFilterRegex, "")
                .replaceFirst(LEADING_ZERO_FILTER_REGEX, "");

        // add grouping separators, need to reverse back and forth since Java regex does not support
        // right to left matching
        number = StringUtils.reverse(
                StringUtils.reverse(number).replaceAll("(.{3})", "$1" + GROUPING_SEPARATOR));
        // remove leading grouping separator if any
        number = StringUtils.removeStart(number, String.valueOf(GROUPING_SEPARATOR));

        // add fraction part if any
        if (parts.length > 1) {
            number += DECIMAL_SEPARATOR + parts[1];
        }

        return number;
    }

    /**
     * Change display text without triggering numeric value changed
     * @param text  new text to apply
     */
    private void setTextInternal(String text) {
        removeTextChangedListener(mTextWatcher);
        setText(text);
        addTextChangedListener(mTextWatcher);
    }
}

Example:

input 10000 it will be 10,000 in an instant, input 10000.12 it will be 10,000.12

what I've tried:

                int input2 = 0;
                String text2 = etpersen2.getText().toString();
                if (text2.length() > 0)
                     input2 = Integer.parseInt(text2);
                String e = String.valueOf(input2);

                etresult.setText("" + e);
A-Sharabiani
  • 17,750
  • 17
  • 113
  • 128
Jedi Fighter
  • 377
  • 1
  • 4
  • 20

1 Answers1

1

Using Math.round() should round the float to the nearest whole number. It returns an int value so typecasting using (int) is redundant.

public class Test{ 

   public static void main(String args[]){
      double d = 100.675;
      double e = 100.500;
      float f = 100;
      float g = 90f;

      System.out.println(Math.round(d));
      System.out.println(Math.round(e)); 
      System.out.println(Math.round(f)); 
      System.out.println(Math.round(g)); 
   }
}
Pang
  • 9,564
  • 146
  • 81
  • 122
Devraj
  • 1,479
  • 1
  • 22
  • 42
  • sounds promising,i'll try – Jedi Fighter Apr 15 '15 at 23:54
  • how to use it? --->> int x = (int) Math.round(TheInput); <----- Is it wrong? – Jedi Fighter Apr 15 '15 at 23:58
  • I am sorry for asking too many times, but i am curious, will Math.round() also take care of NaN? i mean What if the input is NaN – Jedi Fighter Apr 16 '15 at 00:05
  • It returns int value so typecasting using (int) is redundant, that being said meaning I just have to use String e = String.valueOf(Math.round(input2)); instead of ->> int x = (int) Math.round(TheInput); <-? – Jedi Fighter Apr 16 '15 at 00:21
  • Any efford cant be done, because the edittext itself is a custom one. The edittext will add .0(dot zero) or (.##) or (.#) if the result is a floating number. Need to change the custom edittext in a whole(remove the private final char DECIMAL_SEPARATOR = DecimalFormatSymbols.getInstance().getDecimalSeparator();) or change the regex to also exclude leading (zero(0),zero dot zero(0.0), something dot zero(#.0) ,dot zero(.0) and dot(.)) or customized the edittext to accept integer value and if int it will not add floating decimal separator. Thanks for helping me this far – Jedi Fighter Apr 16 '15 at 07:45
  • i need to explore more about REGEX cause if i can set the regex there from LEADING_ZERO_FILTER_REGEX = "^0+(?!$)"; <---- this will exclude leading zero, if only i know how to exclude leading (zero(0),zero dot zero(0.0), something dot zero(#.0) ,dot zero(.0) and dot(.)). And yes at this post, the answer is to change the regex and remake the custom edittext. – Jedi Fighter Apr 17 '15 at 00:12
  • you want to change your entered integer value into currency... right?? – Devraj Apr 17 '15 at 06:14
  • 1
    yes, the problem solved. i use this trick at the onfocuschangelistener `if (output == (long) output) e = String.format("%d", (long) output); else e = String.format("%.2f", output); editText.setText(e);` – Jedi Fighter Apr 17 '15 at 06:31
  • when the output is equal to long it will be printed without fraction, else it will give double output with .## format – Jedi Fighter Apr 17 '15 at 06:34
  • 1
    That is great!!!! I've also found some good links for reference. may be helpful for others... 1) http://codereview.stackexchange.com/questions/38317/optimize-parsing-of-number-for-currency-conversion 2)http://stackoverflow.com/questions/3821539/decimal-separator-comma-with-numberdecimal-inputtype-in-edittext 3)http://stackoverflow.com/questions/2379221/java-currency-number-format – Devraj Apr 17 '15 at 06:50