[android] 통화 입력 editText를 포맷하는 더 나은 방법?

editText가 있고 시작 값은 $ 0.00입니다. 1을 누르면 $ 0.01로 변경됩니다. 4를 누르면 $ 0.14가됩니다. 8, $ 1.48을 누릅니다. 백 스페이스, $ 0.14 등을 누릅니다.

문제는 누군가가 수동으로 커서를 배치하면 서식에 문제가 발생한다는 것입니다. 소수점을 삭제하면 다시 나타나지 않습니다. 커서를 소수점 앞에 놓고 2를 입력하면 $ 2.00 대신 $ 02.00이 표시됩니다. 예를 들어 $를 삭제하려고하면 숫자가 대신 삭제됩니다.

여기에 제가 사용하고있는 코드가 있습니다. 어떤 제안이라도 주시면 감사하겠습니다.

mEditPrice.setRawInputType(Configuration.KEYBOARD_12KEY);
    public void priceClick(View view) {
    mEditPrice.addTextChangedListener(new TextWatcher(){
        DecimalFormat dec = new DecimalFormat("0.00");
        @Override
        public void afterTextChanged(Editable arg0) {
        }
        @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().matches("^\\$(\\d{1,3}(\\,\\d{3})*|(\\d+))(\\.\\d{2})?$"))
            {
                String userInput= ""+s.toString().replaceAll("[^\\d]", "");
                if (userInput.length() > 0) {
                    Float in=Float.parseFloat(userInput);
                    float percen = in/100;
                    mEditPrice.setText("$"+dec.format(percen));
                    mEditPrice.setSelection(mEditPrice.getText().length());
                }
            }
        }
    });



답변

나는 당신의 방법을 테스트했지만 큰 숫자를 사용하면 실패합니다 … 이것을 만들었습니다.

private String current = "";
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
    if(!s.toString().equals(current)){
       [your_edittext].removeTextChangedListener(this);

       String cleanString = s.toString().replaceAll("[$,.]", "");

       double parsed = Double.parseDouble(cleanString);
       String formatted = NumberFormat.getCurrencyInstance().format((parsed/100));

       current = formatted;
       [your_edittext].setText(formatted);
       [your_edittext].setSelection(formatted.length());

       [your_edittext].addTextChangedListener(this);
    }
}

Kotlin 변형 :

private var current: String = ""

         override fun onTextChanged(
            s: CharSequence,
            start: Int,
            before: Int,
            count: Int
        ) {
            if (s.toString() != current) {
                discount_amount_edit_text.removeTextChangedListener(this)

                val cleanString: String = s.replace("""[$,.]""".toRegex(), "")

                val parsed = cleanString.toDouble()
                val formatted = NumberFormat.getCurrencyInstance().format((parsed / 100))

                current = formatted
                discount_amount_edit_text.setText(formatted)
                discount_amount_edit_text.setSelection(formatted.length)

                discount_amount_edit_text.addTextChangedListener(this)
            }
        }


답변

위의 답변 중 일부를 기반으로 다음과 같이 사용할 MoneyTextWatcher를 만들었습니다.

priceEditText.addTextChangedListener(new MoneyTextWatcher(priceEditText));

그리고 여기에 수업이 있습니다 :

public class MoneyTextWatcher implements TextWatcher {
    private final WeakReference<EditText> editTextWeakReference;

    public MoneyTextWatcher(EditText editText) {
        editTextWeakReference = new WeakReference<EditText>(editText);
    }

    @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 editable) {
        EditText editText = editTextWeakReference.get();
        if (editText == null) return;
        String s = editable.toString();
        if (s.isEmpty()) return;
        editText.removeTextChangedListener(this);
        String cleanString = s.replaceAll("[$,.]", "");
        BigDecimal parsed = new BigDecimal(cleanString).setScale(2, BigDecimal.ROUND_FLOOR).divide(new BigDecimal(100), BigDecimal.ROUND_FLOOR);
        String formatted = NumberFormat.getCurrencyInstance().format(parsed);
        editText.setText(formatted);
        editText.setSelection(formatted.length());
        editText.addTextChangedListener(this);
    }
}


답변

여기 내 습관이 있습니다. CurrencyEditText

import android.content.Context;import android.graphics.Rect;import android.text.Editable;import android.text.InputFilter;import android.text.InputType;import android.text.TextWatcher;
import android.util.AttributeSet;import android.widget.EditText;import java.math.BigDecimal;import java.math.RoundingMode;
import java.text.DecimalFormat;import java.text.DecimalFormatSymbols;
import java.util.Locale;

/**
 * Some note <br/>
 * <li>Always use locale US instead of default to make DecimalFormat work well in all language</li>
 */
public class CurrencyEditText extends android.support.v7.widget.AppCompatEditText {
    private static String prefix = "VND ";
    private static final int MAX_LENGTH = 20;
    private static final int MAX_DECIMAL = 3;
    private CurrencyTextWatcher currencyTextWatcher = new CurrencyTextWatcher(this, prefix);

    public CurrencyEditText(Context context) {
        this(context, null);
    }

    public CurrencyEditText(Context context, AttributeSet attrs) {
        this(context, attrs, android.support.v7.appcompat.R.attr.editTextStyle);
    }

    public CurrencyEditText(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        this.setInputType(InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_FLAG_DECIMAL);
        this.setHint(prefix);
        this.setFilters(new InputFilter[] { new InputFilter.LengthFilter(MAX_LENGTH) });
    }

    @Override
    protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
        super.onFocusChanged(focused, direction, previouslyFocusedRect);
        if (focused) {
            this.addTextChangedListener(currencyTextWatcher);
        } else {
            this.removeTextChangedListener(currencyTextWatcher);
        }
        handleCaseCurrencyEmpty(focused);
    }

    /**
     * When currency empty <br/>
     * + When focus EditText, set the default text = prefix (ex: VND) <br/>
     * + When EditText lose focus, set the default text = "", EditText will display hint (ex:VND)
     */
    private void handleCaseCurrencyEmpty(boolean focused) {
        if (focused) {
            if (getText().toString().isEmpty()) {
                setText(prefix);
            }
        } else {
            if (getText().toString().equals(prefix)) {
                setText("");
            }
        }
    }

    private static class CurrencyTextWatcher implements TextWatcher {
        private final EditText editText;
        private String previousCleanString;
        private String prefix;

        CurrencyTextWatcher(EditText editText, String prefix) {
            this.editText = editText;
            this.prefix = prefix;
        }

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

        @Override
        public void afterTextChanged(Editable editable) {
            String str = editable.toString();
            if (str.length() < prefix.length()) {
                editText.setText(prefix);
                editText.setSelection(prefix.length());
                return;
            }
            if (str.equals(prefix)) {
                return;
            }
            // cleanString this the string which not contain prefix and ,
            String cleanString = str.replace(prefix, "").replaceAll("[,]", "");
            // for prevent afterTextChanged recursive call
            if (cleanString.equals(previousCleanString) || cleanString.isEmpty()) {
                return;
            }
            previousCleanString = cleanString;

            String formattedString;
            if (cleanString.contains(".")) {
                formattedString = formatDecimal(cleanString);
            } else {
                formattedString = formatInteger(cleanString);
            }
            editText.removeTextChangedListener(this); // Remove listener
            editText.setText(formattedString);
            handleSelection();
            editText.addTextChangedListener(this); // Add back the listener
        }

        private String formatInteger(String str) {
            BigDecimal parsed = new BigDecimal(str);
            DecimalFormat formatter =
                    new DecimalFormat(prefix + "#,###", new DecimalFormatSymbols(Locale.US));
            return formatter.format(parsed);
        }

        private String formatDecimal(String str) {
            if (str.equals(".")) {
                return prefix + ".";
            }
            BigDecimal parsed = new BigDecimal(str);
            // example pattern VND #,###.00
            DecimalFormat formatter = new DecimalFormat(prefix + "#,###." + getDecimalPattern(str),
                    new DecimalFormatSymbols(Locale.US));
            formatter.setRoundingMode(RoundingMode.DOWN);
            return formatter.format(parsed);
        }

        /**
         * It will return suitable pattern for format decimal
         * For example: 10.2 -> return 0 | 10.23 -> return 00, | 10.235 -> return 000
         */
        private String getDecimalPattern(String str) {
            int decimalCount = str.length() - str.indexOf(".") - 1;
            StringBuilder decimalPattern = new StringBuilder();
            for (int i = 0; i < decimalCount && i < MAX_DECIMAL; i++) {
                decimalPattern.append("0");
            }
            return decimalPattern.toString();
        }

        private void handleSelection() {
            if (editText.getText().length() <= MAX_LENGTH) {
                editText.setSelection(editText.getText().length());
            } else {
                editText.setSelection(MAX_LENGTH);
            }
        }
    }
}

XML에서 사용하십시오.

 <...CurrencyEditText
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        />

프로젝트에 적합하도록 아래 2 개의 상수를 편집해야합니다.

private static String prefix = "VND ";
private static final int MAX_DECIMAL = 3;

여기에 이미지 설명 입력

github 데모


답변

실제로 이전에 제공된 솔루션이 작동하지 않습니다. 100.00을 입력하려면 작동하지 않습니다.

바꾸다:

double parsed = Double.parseDouble(cleanString);
String formato = NumberFormat.getCurrencyInstance().format((parsed/100));

와:

BigDecimal parsed = new BigDecimal(cleanString).setScale(2,BigDecimal.ROUND_FLOOR).divide(new BigDecimal(100),BigDecimal.ROUND_FLOOR);
String formato = NumberFormat.getCurrencyInstance().format(parsed);

나는 내 코드를 약간 수정했다고 말해야한다. 문제는 BigDecimal을 사용해야한다는 것입니다.


답변

값을 편집 할 때 Brasil 통화 형식을 사용하고 커서 위치를 조정하도록 TextWatcher를 구현하여 클래스를 변경합니다.

공용 클래스 MoneyTextWatcher는 TextWatcher {를 구현합니다.

    개인 EditText editText;

    private String lastAmount = "";

    private int lastCursorPosition = -1;

    public MoneyTextWatcher (EditText editText) {
        감독자();
        this.editText = editText;
    }

    @우세하다
    public void onTextChanged (CharSequence amount, int start, int before, int count) {

        if (! amount.toString (). equals (lastAmount)) {

            문자열 cleanString = clearCurrencyToNumber (amount.toString ());

            {

                문자열 formattedAmount = transformToCurrency (cleanString);
                editText.removeTextChangedListener (this);
                editText.setText (formattedAmount);
                editText.setSelection (formattedAmount.length ());
                editText.addTextChangedListener (this);

                if (lastCursorPosition! = lastAmount.length () && lastCursorPosition! = -1) {
                    int lengthDelta = formattedAmount.length ()-lastAmount.length ();
                    int newCursorOffset = max (0, min (formattedAmount.length (), lastCursorPosition + lengthDelta));
                    editText.setSelection (newCursorOffset);
                }
            } catch (예외 e) {
               // 로그
            }
        }
    }

    @우세하다
    public void afterTextChanged (Editable s) {
    }

    @우세하다
    public void beforeTextChanged (CharSequence s, int start, int count, int after) {
        문자열 값 = s.toString ();
        if (! value.equals ( "")) {
            문자열 cleanString = clearCurrencyToNumber (value);
            문자열 formattedAmount = transformToCurrency (cleanString);
            lastAmount = formattedAmount;
            lastCursorPosition = editText.getSelectionStart ();
        }
    }

    public static String clearCurrencyToNumber (String currencyValue) {
        문자열 결과 = null;

        if (currencyValue == null) {
            결과 = "";
        } else {
            result = currencyValue.replaceAll ( "[(az) | (AZ) | ($ ,.)]", "");
        }
        반환 결과;
    }

    public static boolean isCurrencyValue (String currencyValue, boolean podeSerZero) {
        부울 결과;

        if (currencyValue == null || currencyValue.length () == 0) {
            결과 = 거짓;
        } else {
            if (! podeSerZero && currencyValue.equals ( "0,00")) {
                결과 = 거짓;
            } else {
                결과 = 참;
            }
        }
        반환 결과;
    }

    public static String transformToCurrency (String value) {
        double parsed = Double.parseDouble (value);
        String formatted = NumberFormat.getCurrencyInstance (new Locale ( "pt", "BR")). format ((parsed / 100));
        formatted = formatted.replaceAll ( "[^ (0-9) (.,)]", "");
        반환 형식;
    }
}


답변

Guilhermes 답변을 기반으로 작성했지만 커서의 위치를 ​​유지하고 마침표도 다르게 처리합니다. 이렇게하면 사용자가 마침표 이후에 입력하는 경우 마침표 이전의 숫자에는 영향을주지 않습니다. .

    [yourtextfield].addTextChangedListener(new TextWatcher()
    {
        NumberFormat currencyFormat = NumberFormat.getCurrencyInstance();
        private String current = "";

        @Override
        public void onTextChanged(CharSequence s, int start, int before, int count)
        {
            if(!s.toString().equals(current))
            {
                   [yourtextfield].removeTextChangedListener(this);

                   int selection = [yourtextfield].getSelectionStart();


                   // We strip off the currency symbol
                   String replaceable = String.format("[%s,\\s]", NumberFormat.getCurrencyInstance().getCurrency().getSymbol());
                   String cleanString = s.toString().replaceAll(replaceable, "");

                   double price;

                   // Parse the string                     
                   try
                   {
                       price = Double.parseDouble(cleanString);
                   }
                   catch(java.lang.NumberFormatException e)
                   {
                       price = 0;
                   }

                   // If we don't see a decimal, then the user must have deleted it.
                   // In that case, the number must be divided by 100, otherwise 1
                   int shrink = 1;
                   if(!(s.toString().contains(".")))
                   {
                       shrink = 100;
                   }

                   // Reformat the number
                   String formated = currencyFormat.format((price / shrink));

                   current = formated;
                   [yourtextfield].setText(formated);
                   [yourtextfield].setSelection(Math.min(selection, [yourtextfield].getText().length()));

                   [yourtextfield].addTextChangedListener(this);
                }
        }


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

        }


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


답변

여기에 많은 답변이 있지만 가장 강력하고 깨끗한 답변이라고 믿기 때문에 여기 에서 찾은이 코드를 공유하고 싶습니다 .

class CurrencyTextWatcher implements TextWatcher {

    boolean mEditing;

    public CurrencyTextWatcher() {
        mEditing = false;
    }

    public synchronized void afterTextChanged(Editable s) {
        if(!mEditing) {
            mEditing = true;

            String digits = s.toString().replaceAll("\\D", "");
            NumberFormat nf = NumberFormat.getCurrencyInstance();
            try{
                String formatted = nf.format(Double.parseDouble(digits)/100);
                s.replace(0, s.length(), formatted);
            } catch (NumberFormatException nfe) {
                s.clear();
            }

            mEditing = false;
        }
    }

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

    public void onTextChanged(CharSequence s, int start, int before, int count) { }

}

도움이되기를 바랍니다.