1

I want to have EditText that automatically formats user input in real time, for example, it converts 1000000 to 1,000,000

I tried Android Money Input with fixed decimal but when I set the text, the app crashes

EditText etMoney;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    etMoney = (EditText)findViewById(R.id.etMoney);
    etMoney.addTextChangedListener(new TextWatcher() {
        @Override
        public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
        }

        @Override
        public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
            etMoney.setText(currencyFormat(etMoney.getText().toString()));
        }

        @Override
        public void afterTextChanged(Editable editable) {
        }
    });
}
public static String currencyFormat(String amount) {
    DecimalFormat formatter = new DecimalFormat("###,###,###");
    return formatter.format(Double.parseDouble(amount));
}

The output I want is a real-time converter directly into the EditText but as soon as I type something in EditText the app crashes.

Crash Log:

06-20 15:19:57.453 1699-1715/system_process E/ActivityManager: ANR in com.hassilproject.myapplication (com.hassilproject.myapplication/.MainActivity)
                                                               PID: 2521
                                                               Reason: Input dispatching timed out (Waiting to send key event because the focused window has not finished processing all of the input events that were previously delivered to it.  Outbound queue length: 0.  Wait queue length: 1.)
Fidz
  • 89
  • 9

5 Answers5

1

//Create Method

private TextWatcher onTextChangedListener() {
        return 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) {

            }

            @Override
            public void afterTextChanged(Editable s) {
                editText.removeTextChangedListener(this);

                try {
                    String originalString = s.toString();

                    Long longval;
                    if (originalString.contains(",")) {
                        originalString = originalString.replaceAll(",", "");
                    }
                    longval = Long.parseLong(originalString);

                    DecimalFormat formatter = (DecimalFormat) NumberFormat.getInstance(Locale.US);
                    formatter.applyPattern("#,###,###,###");
                    String formattedString = formatter.format(longval);

                    //setting text after format to EditText
                    editText.setText(formattedString);
                    editText.setSelection(editText.getText().length());
                } catch (NumberFormatException nfe) {
                    nfe.printStackTrace();
                }

                editText.addTextChangedListener(this);
            }
        };
    }

//Call method

editText.addTextChangedListener(onTextChangedListener());
Dhara Jani
  • 461
  • 3
  • 10
0

change this code

 etMoney.addTextChangedListener(new TextWatcher() {
    @Override
    public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
    }

    @Override
    public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {

    }

    @Override
    public void afterTextChanged(Editable editable) {
        etMoney.setText(currencyFormat(editable.toString()));
    }
});
Gaurav
  • 171
  • 1
  • 13
0

Try this, I created a custom view to input currency. Use currency() method to get value in EditText.

class CurrencyInput(context: Context, attributeSet: AttributeSet) : TextInputEditText(context, attributeSet) {

    private val currencyTextWatcher = CurrencyWatcher()

    override fun onFocusChanged(focused: Boolean, direction: Int, previouslyFocusedRect: Rect?) {
        super.onFocusChanged(focused, direction, previouslyFocusedRect)
        if (focused) addTextChangedListener(currencyTextWatcher) else removeTextChangedListener(currencyTextWatcher)
    }

    fun currency(): Long = currencyTextWatcher.amount
    private fun Long.toDecimalStr(): String = decimalFormatter().format(this)

    inner class CurrencyWatcher : TextWatcher {
        var amount = 0L
        var amountDecimal = ""
        override fun afterTextChanged(editable: Editable?) {
            editable?.toString()?.run {
                when {
                    equals("") -> {
                        amount = 0
                        amountDecimal = "0"
                    }
                    else -> {
                        amount = replace(",", "").replace(".", "").toLong()
                        amountDecimal = if (amount >= 1000) amount.toDecimalStr() else amount.toString()
                    }
                }
                this@CurrencyInput.apply {
                    removeTextChangedListener(this@CurrencyWatcher)
                    amount.let {
                        setText(amountDecimal)
                        setSelection(amountDecimal.length)
                    }
                    addTextChangedListener(this@CurrencyWatcher)
                }
            }
        }
        override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) { }
        override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) { }
    }

}

Replace with your package name when using CurrencyInput in your layout

<com.google.android.material.textfield.TextInputLayout
            style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginBottom="7dp"
            android:hint="Currency"
            app:endIconMode="clear_text">
            <com.package.CurrencyInput
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:inputType="number"/>
        </com.google.android.material.textfield.TextInputLayout>
Son Huynh
  • 839
  • 1
  • 7
  • 11
0

I hope this will work for you.

 etMoney.addTextChangedListener(new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {

            }

            @Override
            public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {

            }

            @Override
            public void afterTextChanged(Editable editable) {
                etMoney.removeTextChangedListener(this);
                new Handler().postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            if (!editable.toString().equalsIgnoreCase("")) {
                                etMoney.setText("" + currencyFormat(editable.toString()));
                            }
                        } catch (NumberFormatException e) {
                            e.printStackTrace();
                        }
                    }
                }, 4000); //after 4 second it will update to UI.
               etMoney.addTextChangedListener(this);
            }
        });

Currency Format method given below

 public String currencyFormat(String amount) {
        return new DecimalFormat("###,###,###").format(Double.parseDouble(amount));
    }
GParekar
  • 1,209
  • 1
  • 8
  • 15
0

Every time you change the text of EditText, the TextWatcher method will get called. So you must set flag to indicate that change source. And the TextWatcher callback method. check indicating flag to avoid infinite loop.

etMoney.addTextChangedListener(new TextWatcher() {
    private boolean changedByFormatText = false;
    @Override
    public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
    }

    @Override
    public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {

    }

    @Override
    public void afterTextChanged(Editable editable) {
        if(changedByFormatText) {
            changedByFormatText = false;
            return;
        } else {
           changedByFormatText = true;
           etMoney.setText(currencyFormat(etMoney.getText().toString()));
        }

    }
});
Tshunglee
  • 337
  • 2
  • 11