格式化货币输入editText的更好方法?

Rog*_*ger 81 android

我有一个editText,起始值是0.00美元.按1时,它变为$ 0.01.按4,它达到$ 0.14.按8,$ 1.48.按退格键,0.14美元等

这是有效的,问题是,如果有人手动定位光标,格式化中会出现问题.如果他们要删除小数,它就不会回来.如果他们将光标放在小数前面并输入2,它将显示$ 02.00而不是$ 2.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());
                }
            }
        }
    });
Run Code Online (Sandbox Code Playgroud)

Gui*_*ira 141

我测试了你的方法,但是当我使用大数字时它失败了...我创建了这个:

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 可能更好地执行以下操作而不是假设美元符号:`String replaceable = String.format("[%s,.]",NumberFormat.getCurrencyInstance().getCurrency().getSymbol()); String cleanString = s.toString().replaceAll(replaceable,"");` (28认同)
  • 是不是建议*不*在`onTextChanged()中进行更改,而不是在`afterTextChanged()中进行更改 (6认同)
  • 嗯,实际上我现在已经尝试过这个,来自replaceAll的正则表达式模式看起来应该像这样,处理空格:`String replaceable = String.format("[%s,.\\ s]",NumberFormat.getCurrencyInstance() ..getCurrency()getSymbol());` (5认同)
  • 当你输入1 - > 0 - > 0得到1,00时,我不工作.它是因为你到了0,1变为010字符串而010变为`double`的地步是10.`10/100 = 0,1`你无法超越它. (5认同)
  • 我有兴趣知道为什么文本更改后的侦听器被删除然后每次都重新添加?对我来说,如果只添加一次(我将更改移到afterTextChanged),它就有效 (3认同)

Tod*_*ddH 25

基于上面的一些答案,我创建了一个MoneyTextWatcher,你可以使用如下:

priceEditText.addTextChangedListener(new MoneyTextWatcher(priceEditText));
Run Code Online (Sandbox Code Playgroud)

这是班级:

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);
    }
}
Run Code Online (Sandbox Code Playgroud)


Pha*_*inh 16

这是我的习惯 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);
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

在XML中使用它

 <...CurrencyEditText
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        />
Run Code Online (Sandbox Code Playgroud)

您应该在下面编辑2常量以适合您的项目

private static String prefix = "VND ";
private static final int MAX_DECIMAL = 3;
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述

在github上演示

  • 这太棒了! (2认同)

sfr*_*ini 12

实际上,之前提供的解决方案不起作用.如果要输入100.00,则不起作用.

更换:

double parsed = Double.parseDouble(cleanString);
String formato = NumberFormat.getCurrencyInstance().format((parsed/100));
Run Code Online (Sandbox Code Playgroud)

附:

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

我必须说我对我的代码进行了一些修改.问题是你应该使用BigDecimal


Fra*_*ior 7

我使用 Nathan Leigh 引用的实现以及 Kayvan N 和 user2582318 建议的正则表达式来删除除数字之外的所有字符,以创建以下版本:

fun EditText.addCurrencyFormatter() {

    // Reference: /sf/ask/357553101/#29993290
    this.addTextChangedListener(object: TextWatcher {

        private var current = ""

        override fun afterTextChanged(s: Editable?) {
        }

        override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
        }

        override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {

            if (s.toString() != current) {
                this@addCurrencyFormatter.removeTextChangedListener(this)
                // strip off the currency symbol

                // Reference for this replace regex: /sf/ask/357553101/#28005836
                val cleanString = s.toString().replace("\\D".toRegex(), "")
                val parsed = if (cleanString.isBlank()) 0.0 else cleanString.toDouble()
                // format the double into a currency format
                val formated = NumberFormat.getCurrencyInstance()
                        .format(parsed / 100)

                current = formated
                this@addCurrencyFormatter.setText(formated)
                this@addCurrencyFormatter.setSelection(formated.length)

                this@addCurrencyFormatter.addTextChangedListener(this)
            }
        }
    })

}
Run Code Online (Sandbox Code Playgroud)

这是 Kotlin 中的一个扩展函数,它将 TextWatcher 添加到 EditText 的 TextChangedListener 中。

为了使用它,只需:

yourEditText = (EditText) findViewById(R.id.edit_text_your_id);
yourEditText.addCurrencyFormatter()
Run Code Online (Sandbox Code Playgroud)

我希望它有帮助。


小智 6

我使用实现TextWatcher更改类以使用Brasil货币格式并在编辑值时调整光标位置.

public class MoneyTextWatcher implements TextWatcher {

    private EditText editText;

    private String lastAmount = "";

    private int lastCursorPosition = -1;

    public MoneyTextWatcher(EditText editText) {
        super();
        this.editText = editText;
    }

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

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

            String cleanString = clearCurrencyToNumber(amount.toString());

            try {

                String 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 (Exception e) {
               //log something
            }
        }
    }

    @Override
    public void afterTextChanged(Editable s) {
    }

    @Override
    public void beforeTextChanged(CharSequence s, int start, int count, int after) {
        String value = s.toString();
        if(!value.equals("")){
            String cleanString = clearCurrencyToNumber(value);
            String formattedAmount = transformToCurrency(cleanString);
            lastAmount = formattedAmount;
            lastCursorPosition = editText.getSelectionStart();
        }
    }

    public static String clearCurrencyToNumber(String currencyValue) {
        String result = null;

        if (currencyValue == null) {
            result = "";
        } else {
            result = currencyValue.replaceAll("[(a-z)|(A-Z)|($,. )]", "");
        }
        return result;
    }

    public static boolean isCurrencyValue(String currencyValue, boolean podeSerZero) {
        boolean result;

        if (currencyValue == null || currencyValue.length() == 0) {
            result = false;
        } else {
            if (!podeSerZero && currencyValue.equals("0,00")) {
                result = false;
            } else {
                result = true;
            }
        }
        return result;
    }

    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)(.,)]", "");
        return formatted;
    }
}

  • @ArthurMelo它,Math.max,Math.min感谢代码,从edittext中删除逗号时看起来很失败. (2认同)

Kay*_*n N 5

尽管这里有很多答案,但我想分享我在这里找到的代码,因为我相信它是最强大和最干净的答案。

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

}
Run Code Online (Sandbox Code Playgroud)

希望能帮助到你。

  • 这是完美的答案!为我工作。天啊,想想我花了多少时间在这些答案上,最后滚动到底部,找到了我想要的答案。 (2认同)