如何在EditText中输入数字时自动添加千位分隔符

75
我正在创建一个转换器应用程序。 我想设置EditText,以便当用户输入要转换的数字时,实时自动添加千位分隔符(),一旦增加了3个数字:千、百万、十亿等。
当擦除到低于4个数字时,数字恢复正常。
需要帮助吗?

@PaulAsiimwe 你找到解决方法了吗?即当用户在输入时将“1000”更改为“1,000”。 - Saim Mehmood
@saim Mehmood,请查看我的个人资料,其中有一个类似的问题,包含“实时编辑”一词。 - Asiimwe
https://dev59.com/6IPba4cB1Zd3GeqPrFx6 - Amir K. Zarini
在这里找到了最佳答案(https://dev59.com/imjWa4cB1Zd3GeqPtr0P),解决了问题并且更多。 - Asiimwe
https://dev59.com/F1HTa4cB1Zd3GeqPVNyk#37187857 - HenryChuang
显示剩余3条评论
16个回答

61
即使已经晚了,也是为未来的访问者准备的。
以下代码的特点:
  1. EditText文本更改时,将千位分隔符放入其中。
  2. 当按下句号(.)时,在首次自动添加0.
  3. 忽略开头的0输入。
只需复制以下类名为NumberTextWatcherForThousand,它实现了TextWatcher
import android.text.Editable;
import android.text.TextWatcher;
import android.widget.EditText;
import java.util.StringTokenizer;

/**
 * Created by skb on 12/14/2015.
 */
public class NumberTextWatcherForThousand implements TextWatcher {

    EditText editText;


    public NumberTextWatcherForThousand(EditText editText) {
        this.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 s) {
        try
        {
            editText.removeTextChangedListener(this);
            String value = editText.getText().toString();


            if (value != null && !value.equals(""))
            {

                if(value.startsWith(".")){
                    editText.setText("0.");
                }
                if(value.startsWith("0") && !value.startsWith("0.")){
                    editText.setText("");

                }


                String str = editText.getText().toString().replaceAll(",", "");
                if (!value.equals(""))
                editText.setText(getDecimalFormattedString(str));
                editText.setSelection(editText.getText().toString().length());
            }
            editText.addTextChangedListener(this);
            return;
        }
        catch (Exception ex)
        {
            ex.printStackTrace();
            editText.addTextChangedListener(this);
        }

    }

    public static String getDecimalFormattedString(String value)
    {
        StringTokenizer lst = new StringTokenizer(value, ".");
        String str1 = value;
        String str2 = "";
        if (lst.countTokens() > 1)
        {
            str1 = lst.nextToken();
            str2 = lst.nextToken();
        }
        String str3 = "";
        int i = 0;
        int j = -1 + str1.length();
        if (str1.charAt( -1 + str1.length()) == '.')
        {
            j--;
            str3 = ".";
        }
        for (int k = j;; k--)
        {
            if (k < 0)
            {
                if (str2.length() > 0)
                    str3 = str3 + "." + str2;
                return str3;
            }
            if (i == 3)
            {
                str3 = "," + str3;
                i = 0;
            }
            str3 = str1.charAt(k) + str3;
            i++;
        }

    }

    public static String trimCommaOfString(String string) {
//        String returnString;
        if(string.contains(",")){
            return string.replace(",","");}
        else {
            return string;
        }

    }
}

在您的EditText上使用此类,如下所示。
editText.addTextChangedListener(new NumberTextWatcherForThousand(editText));

获取纯双精度文本输入

使用同一类的trimCommaOfString方法,像这样:

NumberTextWatcherForThousand.trimCommaOfString(editText.getText().toString())

Git

可以翻译为:

{{链接1:Git}}


1
键删除出现问题。 - Holi Boom
16
对于不使用英语数字格式的人来说,这太糟糕了,包括欧洲、加拿大法语区、南美、亚洲等地区的用户。与其使用硬编码的逗号,你应该查看DecimalFormatSymbols(),并使用getGroupingSeparator()。请记住,虽然美国和英国使用逗号表示千位分隔,小数点则用句号表示,但许多欧洲语言使用句号表示千位分隔,逗号表示小数,其他语言则使用空格分隔千位。请考虑多个本地化而不仅仅是你自己的本地化。 - Phil A
1
非常好的答案!!!谢谢。它绘制了很棒的路径,任何人都可以轻松地进行一些修改后使用它,而不受任何语言环境的限制... - MHSaffari
如何处理一百万的值。例如,1,000,000。 - Axay Prajapati

56
你可以在 TextWatcher 中使用 String.format()。格式字符串中的逗号起到重要作用。但是,对于浮点输入,此方法无效。并且请注意不要使用 TextWatcher 设置无限循环。
public void afterTextChanged(Editable view) {
    String s = null;
    try {
        // The comma in the format specifier does the trick
        s = String.format("%,d", Long.parseLong(view.toString()));
    } catch (NumberFormatException e) {
    }
    // Set s back to the view after temporarily removing the text change listener
}

浮点数已经回来了,但千位分隔符逗号没有显示出来。 - Asiimwe
2
我希望千位分隔符能够实时地出现在我正在输入的EditText上,而不是之后的字符串。 - Asiimwe
4
我不明白你的意思。// 将监听文本更改的监听器暂时移除后,将 s 设置回视图中,请问您能否解释或补充完整这段代码。 - Shivputra N
2
我的手机在运行这段代码时崩溃了,不是应用程序,而是整个操作系统,我不知道发生了什么。 - SinaMN75
如果我想要自定义分隔符,该怎么办? - M.kazem Akhgary
如何将逗号替换为点(.)? - Ahmed Elsayed

39
public static String doubleToStringNoDecimal(double d) {
    DecimalFormat formatter = (DecimalFormat) NumberFormat.getInstance(Locale.US);
    formatter.applyPattern("#,###");
    return formatter.format(d);
}

1
它只会在第一个“千位数”(1,234)处放置分隔符。如果您有百万级别的数字,则分隔符仅分隔第一个千位数(1,23456789)。 - kazimad

11

这个示例应用程序清晰地解构了格式化数字。

总结上面的链接,使用 TextWatcherafterTextChanged() 方法中使用以下逻辑对 EditText 视图进行格式化:

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

1
这很好,但如果您的光标在数字中间而不是末尾进行编辑,则光标会被移动到末尾而不是停留在原地。 - vlazzle
我只是想确认这种方法可行,但会严重影响性能。 - Danish Ansari

7
我知道我来晚了,但对于未来的用户来说这可能非常有用。我的答案是Shree Krishna答案的扩展。
改进:
  1. 千位分隔符和小数点是区域感知型的,即它们根据设备的Locale相应地使用。
  2. 在删除或添加中间元素后光标位置不会改变(在他的答案中,光标被重置到结尾)。
  3. 代码整体质量得到了改善,特别是getDecimalFormattedString方法。
代码:
    import android.text.Editable;
    import android.text.TextWatcher;
    import android.widget.EditText;

    import java.text.DecimalFormat;


    /**
     * Created by srv_twry on 4/12/17.
     * Source: https://dev59.com/8Wct5IYBdhLWcg3wHZ1q#34265406
     * The custom TextWatcher that automatically adds thousand separators in EditText.
     */

    public class ThousandSeparatorTextWatcher implements TextWatcher {

        private DecimalFormat df;
        private EditText editText;
        private static String thousandSeparator;
        private static String decimalMarker;
        private int cursorPosition;

        public ThousandSeparatorTextWatcher(EditText editText) {
            this.editText = editText;
            df = new DecimalFormat("#,###.##");
            df.setDecimalSeparatorAlwaysShown(true);
            thousandSeparator = Character.toString(df.getDecimalFormatSymbols().getGroupingSeparator());
            decimalMarker = Character.toString(df.getDecimalFormatSymbols().getDecimalSeparator());
        }

        @Override
        public void beforeTextChanged(CharSequence charSequence, int start, int count, int after) {
            cursorPosition = editText.getText().toString().length() - editText.getSelectionStart();
        }

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

        @Override
        public void afterTextChanged(Editable s) {
            try {
                editText.removeTextChangedListener(this);
                String value = editText.getText().toString();

                if (value != null && !value.equals("")) {
                    if (value.startsWith(decimalMarker)) {
                        String text = "0" + decimalMarker;
                        editText.setText(text);
                    }
                    if (value.startsWith("0") && !value.startsWith("0" + decimalMarker)) {
                        int index = 0;
                        while (index < value.length() && value.charAt(index) == '0') {
                            index++;
                        }
                        String newValue = Character.toString(value.charAt(0));
                        if (index != 0) {
                            newValue = value.charAt(0) + value.substring(index);
                        }
                        editText.setText(newValue);
                    }
                    String str = editText.getText().toString().replaceAll(thousandSeparator, "");
                    if (!value.equals("")) {
                        editText.setText(getDecimalFormattedString(str));
                    }
                    editText.setSelection(editText.getText().toString().length());
                }

                //setting the cursor back to where it was
                editText.setSelection(editText.getText().toString().length() - cursorPosition);
                editText.addTextChangedListener(this);
            } catch (Exception ex) {
                ex.printStackTrace();
                editText.addTextChangedListener(this);
            }
        }

        private static String getDecimalFormattedString(String value) {

            String[] splitValue = value.split("\\.");
            String beforeDecimal = value;
            String afterDecimal = null;
            String finalResult = "";

            if (splitValue.length == 2) {
                beforeDecimal = splitValue[0];
                afterDecimal = splitValue[1];
            }

            int count = 0;
            for (int i = beforeDecimal.length() - 1; i >= 0 ; i--) {
                finalResult = beforeDecimal.charAt(i) + finalResult;
                count++;
                if (count == 3 && i > 0) {
                    finalResult = thousandSeparator + finalResult;
                    count = 0;
                }
            }

            if (afterDecimal != null) {
                finalResult = finalResult + decimalMarker + afterDecimal;
            }

            return finalResult;
        }

        /*
        * Returns the string after removing all the thousands separators.
        * */
        public static String getOriginalString(String string) {
            return string.replace(thousandSeparator,"");
        }
    }

5
这种解决方案相较于其他答案具有一些优势。例如,即使用户在数字的开头或中间进行编辑,它也可以保留用户的光标位置。而其他解决方案总是将光标跳到数字的末尾。它可以处理小数和整数,以及使用除了“.”作为小数分隔符和“,”作为千位分隔符的本地化设置。
class SeparateThousands(val groupingSeparator: String, val decimalSeparator: String) : TextWatcher {

    private var busy = false

    override fun afterTextChanged(s: Editable?) {
        if (s != null && !busy) {
            busy = true

            var place = 0

            val decimalPointIndex = s.indexOf(decimalSeparator)
            var i = if (decimalPointIndex == -1) {
                s.length - 1
            } else {
                decimalPointIndex - 1
            }
            while (i >= 0) {
                val c = s[i]
                if (c == groupingSeparator[0] ) {
                    s.delete(i, i + 1)
                } else {
                    if (place % 3 == 0 && place != 0) {
                        // insert a comma to the left of every 3rd digit (counting from right to
                        // left) unless it's the leftmost digit
                        s.insert(i + 1, groupingSeparator)
                    }
                    place++
                }
                i--
            }

            busy = false
        }
    }

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

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

然后在xml中:

  <EditText
    android:id="@+id/myNumberField"
    android:digits=",.0123456789"
    android:inputType="numberDecimal"
    .../>

最后注册观察器:

findViewById(R.id.myNumberField).addTextChangedListener(
    SeparateThousands(groupingSeparator, decimalSeparator))

要处理不同地区中的 . 和 ,,请使用 groupingSeparator 和 decimalSeparator,这些可以来自 DecimalFormatSymbols 或本地化字符串。


我知道现在有点晚了,但还是谢谢你。这个对我的项目很有效。然而,当与android:maxLength一起使用时,groupingSeparator会随着输入的内容出现和消失。有没有办法只针对“数字”使用android:maxLength - Kellin Strook

4

我只是希望放置逗号,并且这对我有用:

String.format("%,.2f", myValue);

2
你可以在程序中以多种方式使用此代码,将字符串传递给它,它会从右侧每三个字符分离并在那里放置空格。
private String Spacer(String number){
    StringBuilder strB = new StringBuilder();
    strB.append(number);
    int Three = 0;

    for(int i=number.length();i>0;i--){
        Three++;
        if(Three == 3){
            strB.insert(i-1, " ");
            Three = 0;
        }
    }
    return strB.toString();
}// end Spacer()

您可以稍微更改它并在OnTextChangeListener上使用它。祝你好运。


2
这是我的ThousandNumberEditText类。
public class ThousandNumberEditText extends android.support.v7.widget.AppCompatEditText {
    // TODO: 14/09/2017 change it if you want 
    private static final int MAX_LENGTH = 20;
    private static final int MAX_DECIMAL = 3;

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

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

    public ThousandNumberEditText(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init() {
        addTextChangedListener(new ThousandNumberTextWatcher(this));
        setInputType(InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_FLAG_DECIMAL);
        setFilters(new InputFilter[] { new InputFilter.LengthFilter(MAX_LENGTH) });
        setHint("0"); // TODO: 14/09/2017 change it if you want 
    }

    private static class ThousandNumberTextWatcher implements TextWatcher {

        private EditText mEditText;

        ThousandNumberTextWatcher(EditText editText) {
            mEditText = editText;
        }

        @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) {
            String originalString = editable.toString();
            String cleanString = originalString.replaceAll("[,]", "");
            if (cleanString.isEmpty()) {
                return;
            }
            String formattedString = getFormatString(cleanString);

            mEditText.removeTextChangedListener(this);
            mEditText.setText(formattedString);
            mEditText.setSelection(mEditText.getText().length());
            mEditText.addTextChangedListener(this);
        }

        /**
         * Return the format string
         */
        private String getFormatString(String cleanString) {
            if (cleanString.contains(".")) {
                return formatDecimal(cleanString);
            } else {
                return formatInteger(cleanString);
            }
        }

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

        private String formatDecimal(String str) {
            if (str.equals(".")) {
                return ".";
            }
            BigDecimal parsed = new BigDecimal(str);
            DecimalFormat formatter;
            formatter =
                    new DecimalFormat("#,###." + getDecimalPattern(str)); //example patter #,###.00
            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() - 1 - str.indexOf(".");
            StringBuilder decimalPattern = new StringBuilder();
            for (int i = 0; i < decimalCount && i < MAX_DECIMAL; i++) {
                decimalPattern.append("0");
            }
            return decimalPattern.toString();
        }
    }
}

使用

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

2
您可以使用以下方法:

最初的回答:

myEditText.addTextChangedListener(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) {

                    String input = s.toString();

                    if (!input.isEmpty()) {

                        input = input.replace(",", "");

                        DecimalFormat format = new DecimalFormat("#,###,###");
                        String newPrice = format.format(Double.parseDouble(input));


                        myEditText.removeTextChangedListener(this); //To Prevent from Infinite Loop

                        myEditText.setText(newPrice);
                        myEditText.setSelection(newPrice.length()); //Move Cursor to end of String

                        myEditText.addTextChangedListener(this);
                    }

                }

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

要获取最初的回答,请使用以下方法:

String input = myEditText.getText().toString();
input = input.replace(",", "");

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接