EditText 中带有 numberDecimal inputType 的小数分隔符逗号 (',')

发布于 2024-09-25 13:07:35 字数 202 浏览 7 评论 0原文

EditText 中的 inputType numberDecimal 使用点 . 作为小数点分隔符。在欧洲,通常使用逗号 , 代替。即使我的语言环境设置为德语,小数点分隔符仍然是 .

有没有办法将逗号作为小数点分隔符?

The inputType numberDecimal in EditText uses the dot . as decimal separator. In Europe it's common to use a comma , instead. Even though my locale is set as german the decimal separator is still the .

Is there a way to get the comma as decimal separator?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(25

醉殇 2024-10-02 13:07:36

此处提供的“数字”解决方案的变体:

char separator = DecimalFormatSymbols.getInstance().getDecimalSeparator();
input.setKeyListener(DigitsKeyListener.getInstance("0123456789" + separator));

考虑区域设置分隔符。

A variation on the 'digit' solutions offered here:

char separator = DecimalFormatSymbols.getInstance().getDecimalSeparator();
input.setKeyListener(DigitsKeyListener.getInstance("0123456789" + separator));

Taking into account the locale separator.

挽清梦 2024-10-02 13:07:36

以下代码用于 EditText ($ 123,125.155)

Xml 布局

  <EditText
    android:inputType="numberDecimal"
    android:layout_height="wrap_content"
    android:layout_width="200dp"
    android:digits="0123456789.,$" />

代码的货币掩码

EditText testFilter=...
testFilter.addTextChangedListener( new TextWatcher() {
        boolean isEdiging;
        @Override public void onTextChanged(CharSequence s, int start, int before, int count) { }
        @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { }

        @Override public void afterTextChanged(Editable s) {
            if(isEdiging) return;
            isEdiging = true;

            String str = s.toString().replaceAll( "[^\\d]", "" );
            double s1 = Double.parseDouble(str);

            NumberFormat nf2 = NumberFormat.getInstance(Locale.ENGLISH);
            ((DecimalFormat)nf2).applyPattern("$ ###,###.###");
            s.replace(0, s.length(), nf2.format(s1));

            isEdiging = false;
        }
    });

Following Code Currency Mask for EditText ($ 123,125.155)

Xml Layout

  <EditText
    android:inputType="numberDecimal"
    android:layout_height="wrap_content"
    android:layout_width="200dp"
    android:digits="0123456789.,$" />

Code

EditText testFilter=...
testFilter.addTextChangedListener( new TextWatcher() {
        boolean isEdiging;
        @Override public void onTextChanged(CharSequence s, int start, int before, int count) { }
        @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { }

        @Override public void afterTextChanged(Editable s) {
            if(isEdiging) return;
            isEdiging = true;

            String str = s.toString().replaceAll( "[^\\d]", "" );
            double s1 = Double.parseDouble(str);

            NumberFormat nf2 = NumberFormat.getInstance(Locale.ENGLISH);
            ((DecimalFormat)nf2).applyPattern("$ ###,###.###");
            s.replace(0, s.length(), nf2.format(s1));

            isEdiging = false;
        }
    });
极致的悲 2024-10-02 13:07:36

这是 Android SDK 中的已知错误
唯一的解决方法是创建您自己的软键盘。您可以找到实现示例

This is a known bug in the Android SDK.
The only workaround is to create your own soft keyboard. You can find an example of implementation here.

夏末 2024-10-02 13:07:36

您可以将以下内容用于不同的区域设置

private void localeDecimalInput(final EditText editText){

    DecimalFormat decFormat = (DecimalFormat) DecimalFormat.getInstance(Locale.getDefault());
    DecimalFormatSymbols symbols=decFormat.getDecimalFormatSymbols();
    final String defaultSeperator=Character.toString(symbols.getDecimalSeparator());

    editText.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) {

        }

        @Override
        public void afterTextChanged(Editable editable) {
            if(editable.toString().contains(defaultSeperator))
                editText.setKeyListener(DigitsKeyListener.getInstance("0123456789"));
            else
                editText.setKeyListener(DigitsKeyListener.getInstance("0123456789" + defaultSeperator));
        }
    });
}

you could use the following for different locales

private void localeDecimalInput(final EditText editText){

    DecimalFormat decFormat = (DecimalFormat) DecimalFormat.getInstance(Locale.getDefault());
    DecimalFormatSymbols symbols=decFormat.getDecimalFormatSymbols();
    final String defaultSeperator=Character.toString(symbols.getDecimalSeparator());

    editText.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) {

        }

        @Override
        public void afterTextChanged(Editable editable) {
            if(editable.toString().contains(defaultSeperator))
                editText.setKeyListener(DigitsKeyListener.getInstance("0123456789"));
            else
                editText.setKeyListener(DigitsKeyListener.getInstance("0123456789" + defaultSeperator));
        }
    });
}
旧时浪漫 2024-10-02 13:07:36

您可以使用以下解决方法将逗号作为有效输入:-

通过 XML:

<EditText
    android:inputType="number"
    android:digits="0123456789.," />

以编程方式:

EditText input = new EditText(THE_CONTEXT);
input.setKeyListener(DigitsKeyListener.getInstance("0123456789.,"));

这样 Android 系统将显示数字键盘并允许输入的逗号。希望这能回答这个问题:)

You can use the following workaround to also include comma as a valid input:-

Through XML:

<EditText
    android:inputType="number"
    android:digits="0123456789.," />

Programmatically:

EditText input = new EditText(THE_CONTEXT);
input.setKeyListener(DigitsKeyListener.getInstance("0123456789.,"));

In this way Android system will show the numbers' keyboard and allow the input of comma. Hope this answers the question :)

那小子欠揍 2024-10-02 13:07:36

如果您以编程方式实例化 EditText,马丁的答案将不起作用。我继续修改了 API 14 中包含的 DigitsKeyListener 类,以允许逗号和句点作为小数分隔符。

要使用此功能,请在 EditText 上调用 setKeyListener(),例如

// Don't allow for signed input (minus), but allow for decimal points
editText.setKeyListener( new MyDigitsKeyListener( false, true ) );

,但是,您仍然需要在 TextChangedListener 中使用 Martin 的技巧,其中替换逗号有句点

import android.text.InputType;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.method.NumberKeyListener;
import android.view.KeyEvent;

class MyDigitsKeyListener extends NumberKeyListener {

    /**
     * The characters that are used.
     *
     * @see KeyEvent#getMatch
     * @see #getAcceptedChars
     */
    private static final char[][] CHARACTERS = new char[][] {
        new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' },
        new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-' },
        new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '.', ',' },
        new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '.', ',' },
    };

    private char[] mAccepted;
    private boolean mSign;
    private boolean mDecimal;

    private static final int SIGN = 1;
    private static final int DECIMAL = 2;

    private static MyDigitsKeyListener[] sInstance = new MyDigitsKeyListener[4];

    @Override
    protected char[] getAcceptedChars() {
        return mAccepted;
    }

    /**
     * Allocates a DigitsKeyListener that accepts the digits 0 through 9.
     */
    public MyDigitsKeyListener() {
        this(false, false);
    }

    /**
     * Allocates a DigitsKeyListener that accepts the digits 0 through 9,
     * plus the minus sign (only at the beginning) and/or decimal point
     * (only one per field) if specified.
     */
    public MyDigitsKeyListener(boolean sign, boolean decimal) {
        mSign = sign;
        mDecimal = decimal;

        int kind = (sign ? SIGN : 0) | (decimal ? DECIMAL : 0);
        mAccepted = CHARACTERS[kind];
    }

    /**
     * Returns a DigitsKeyListener that accepts the digits 0 through 9.
     */
    public static MyDigitsKeyListener getInstance() {
        return getInstance(false, false);
    }

    /**
     * Returns a DigitsKeyListener that accepts the digits 0 through 9,
     * plus the minus sign (only at the beginning) and/or decimal point
     * (only one per field) if specified.
     */
    public static MyDigitsKeyListener getInstance(boolean sign, boolean decimal) {
        int kind = (sign ? SIGN : 0) | (decimal ? DECIMAL : 0);

        if (sInstance[kind] != null)
            return sInstance[kind];

        sInstance[kind] = new MyDigitsKeyListener(sign, decimal);
        return sInstance[kind];
    }

    /**
     * Returns a DigitsKeyListener that accepts only the characters
     * that appear in the specified String.  Note that not all characters
     * may be available on every keyboard.
     */
    public static MyDigitsKeyListener getInstance(String accepted) {
        // TODO: do we need a cache of these to avoid allocating?

        MyDigitsKeyListener dim = new MyDigitsKeyListener();

        dim.mAccepted = new char[accepted.length()];
        accepted.getChars(0, accepted.length(), dim.mAccepted, 0);

        return dim;
    }

    public int getInputType() {
        int contentType = InputType.TYPE_CLASS_NUMBER;
        if (mSign) {
            contentType |= InputType.TYPE_NUMBER_FLAG_SIGNED;
        }
        if (mDecimal) {
            contentType |= InputType.TYPE_NUMBER_FLAG_DECIMAL;
        }
        return contentType;
    }

    @Override
    public CharSequence filter(CharSequence source, int start, int end,
                               Spanned dest, int dstart, int dend) {
        CharSequence out = super.filter(source, start, end, dest, dstart, dend);

        if (mSign == false && mDecimal == false) {
            return out;
        }

        if (out != null) {
            source = out;
            start = 0;
            end = out.length();
        }

        int sign = -1;
        int decimal = -1;
        int dlen = dest.length();

        /*
         * Find out if the existing text has '-' or '.' characters.
         */

        for (int i = 0; i < dstart; i++) {
            char c = dest.charAt(i);

            if (c == '-') {
                sign = i;
            } else if (c == '.' || c == ',') {
                decimal = i;
            }
        }
        for (int i = dend; i < dlen; i++) {
            char c = dest.charAt(i);

            if (c == '-') {
                return "";    // Nothing can be inserted in front of a '-'.
            } else if (c == '.' ||  c == ',') {
                decimal = i;
            }
        }

        /*
         * If it does, we must strip them out from the source.
         * In addition, '-' must be the very first character,
         * and nothing can be inserted before an existing '-'.
         * Go in reverse order so the offsets are stable.
         */

        SpannableStringBuilder stripped = null;

        for (int i = end - 1; i >= start; i--) {
            char c = source.charAt(i);
            boolean strip = false;

            if (c == '-') {
                if (i != start || dstart != 0) {
                    strip = true;
                } else if (sign >= 0) {
                    strip = true;
                } else {
                    sign = i;
                }
            } else if (c == '.' || c == ',') {
                if (decimal >= 0) {
                    strip = true;
                } else {
                    decimal = i;
                }
            }

            if (strip) {
                if (end == start + 1) {
                    return "";  // Only one character, and it was stripped.
                }

                if (stripped == null) {
                    stripped = new SpannableStringBuilder(source, start, end);
                }

                stripped.delete(i - start, i + 1 - start);
            }
        }

        if (stripped != null) {
            return stripped;
        } else if (out != null) {
            return out;
        } else {
            return null;
        }
    }
}

Martins answer won't work if you are instantiating the EditText programmatically. I went ahead and modified the included DigitsKeyListener class from API 14 to allow for both comma and period as decimal separator.

To use this, call setKeyListener() on the EditText, e.g.

// Don't allow for signed input (minus), but allow for decimal points
editText.setKeyListener( new MyDigitsKeyListener( false, true ) );

However, you still have to use Martin's trick in the TextChangedListener where you replace commas with periods

import android.text.InputType;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.method.NumberKeyListener;
import android.view.KeyEvent;

class MyDigitsKeyListener extends NumberKeyListener {

    /**
     * The characters that are used.
     *
     * @see KeyEvent#getMatch
     * @see #getAcceptedChars
     */
    private static final char[][] CHARACTERS = new char[][] {
        new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' },
        new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-' },
        new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '.', ',' },
        new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '.', ',' },
    };

    private char[] mAccepted;
    private boolean mSign;
    private boolean mDecimal;

    private static final int SIGN = 1;
    private static final int DECIMAL = 2;

    private static MyDigitsKeyListener[] sInstance = new MyDigitsKeyListener[4];

    @Override
    protected char[] getAcceptedChars() {
        return mAccepted;
    }

    /**
     * Allocates a DigitsKeyListener that accepts the digits 0 through 9.
     */
    public MyDigitsKeyListener() {
        this(false, false);
    }

    /**
     * Allocates a DigitsKeyListener that accepts the digits 0 through 9,
     * plus the minus sign (only at the beginning) and/or decimal point
     * (only one per field) if specified.
     */
    public MyDigitsKeyListener(boolean sign, boolean decimal) {
        mSign = sign;
        mDecimal = decimal;

        int kind = (sign ? SIGN : 0) | (decimal ? DECIMAL : 0);
        mAccepted = CHARACTERS[kind];
    }

    /**
     * Returns a DigitsKeyListener that accepts the digits 0 through 9.
     */
    public static MyDigitsKeyListener getInstance() {
        return getInstance(false, false);
    }

    /**
     * Returns a DigitsKeyListener that accepts the digits 0 through 9,
     * plus the minus sign (only at the beginning) and/or decimal point
     * (only one per field) if specified.
     */
    public static MyDigitsKeyListener getInstance(boolean sign, boolean decimal) {
        int kind = (sign ? SIGN : 0) | (decimal ? DECIMAL : 0);

        if (sInstance[kind] != null)
            return sInstance[kind];

        sInstance[kind] = new MyDigitsKeyListener(sign, decimal);
        return sInstance[kind];
    }

    /**
     * Returns a DigitsKeyListener that accepts only the characters
     * that appear in the specified String.  Note that not all characters
     * may be available on every keyboard.
     */
    public static MyDigitsKeyListener getInstance(String accepted) {
        // TODO: do we need a cache of these to avoid allocating?

        MyDigitsKeyListener dim = new MyDigitsKeyListener();

        dim.mAccepted = new char[accepted.length()];
        accepted.getChars(0, accepted.length(), dim.mAccepted, 0);

        return dim;
    }

    public int getInputType() {
        int contentType = InputType.TYPE_CLASS_NUMBER;
        if (mSign) {
            contentType |= InputType.TYPE_NUMBER_FLAG_SIGNED;
        }
        if (mDecimal) {
            contentType |= InputType.TYPE_NUMBER_FLAG_DECIMAL;
        }
        return contentType;
    }

    @Override
    public CharSequence filter(CharSequence source, int start, int end,
                               Spanned dest, int dstart, int dend) {
        CharSequence out = super.filter(source, start, end, dest, dstart, dend);

        if (mSign == false && mDecimal == false) {
            return out;
        }

        if (out != null) {
            source = out;
            start = 0;
            end = out.length();
        }

        int sign = -1;
        int decimal = -1;
        int dlen = dest.length();

        /*
         * Find out if the existing text has '-' or '.' characters.
         */

        for (int i = 0; i < dstart; i++) {
            char c = dest.charAt(i);

            if (c == '-') {
                sign = i;
            } else if (c == '.' || c == ',') {
                decimal = i;
            }
        }
        for (int i = dend; i < dlen; i++) {
            char c = dest.charAt(i);

            if (c == '-') {
                return "";    // Nothing can be inserted in front of a '-'.
            } else if (c == '.' ||  c == ',') {
                decimal = i;
            }
        }

        /*
         * If it does, we must strip them out from the source.
         * In addition, '-' must be the very first character,
         * and nothing can be inserted before an existing '-'.
         * Go in reverse order so the offsets are stable.
         */

        SpannableStringBuilder stripped = null;

        for (int i = end - 1; i >= start; i--) {
            char c = source.charAt(i);
            boolean strip = false;

            if (c == '-') {
                if (i != start || dstart != 0) {
                    strip = true;
                } else if (sign >= 0) {
                    strip = true;
                } else {
                    sign = i;
                }
            } else if (c == '.' || c == ',') {
                if (decimal >= 0) {
                    strip = true;
                } else {
                    decimal = i;
                }
            }

            if (strip) {
                if (end == start + 1) {
                    return "";  // Only one character, and it was stripped.
                }

                if (stripped == null) {
                    stripped = new SpannableStringBuilder(source, start, end);
                }

                stripped.delete(i - start, i + 1 - start);
            }
        }

        if (stripped != null) {
            return stripped;
        } else if (out != null) {
            return out;
        } else {
            return null;
        }
    }
}
所谓喜欢 2024-10-02 13:07:36

恕我直言,解决这个问题的最佳方法是仅使用输入过滤器。这里有一个很好的要点 DecimalDigitsInputFilter。然后你就可以:

editText.setInputType(TYPE_NUMBER_FLAG_DECIMAL | TYPE_NUMBER_FLAG_SIGNED | TYPE_CLASS_NUMBER)
editText.setKeyListener(DigitsKeyListener.getInstance("0123456789,.-"))
editText.setFilters(new InputFilter[] {new DecimalDigitsInputFilter(5,2)});

IMHO the best approach for this problem is to just use the InputFilter. A nice gist is here DecimalDigitsInputFilter. Then you can just:

editText.setInputType(TYPE_NUMBER_FLAG_DECIMAL | TYPE_NUMBER_FLAG_SIGNED | TYPE_CLASS_NUMBER)
editText.setKeyListener(DigitsKeyListener.getInstance("0123456789,.-"))
editText.setFilters(new InputFilter[] {new DecimalDigitsInputFilter(5,2)});
最后的乘客 2024-10-02 13:07:36

我有一个解决方案,允许用户输入点和逗号(如果键盘上可用),但只显示区域设置默认分隔符。此外,它不允许用户输入超过 1 个分隔符。对 EditText 的引用或无限循环没有问题。它是该线程中几个答案的组合,适合我的需求。

与接受的答案一样,相应地配置 EditText

android:inputType="numberDecimal"
android:digits="0123456789.,"

然后在 EditText 上设置自定义 TextWatcher:

myEditText.addTextChangedListener(FlexibleDecimalSeparatorTextWatcher())

并包含自定义 TextWatcher:

import android.text.Editable
import android.text.SpannableStringBuilder
import android.text.TextWatcher
import android.widget.EditText
import java.text.DecimalFormatSymbols

/**
 * The [FlexibleDecimalSeparatorTextWatcher] allows the user to input both the comma (,) and dot (.) as a decimal separator,
 * and will then automatically convert each entered separator into the locale default separator.
 * If the user were to enter multiple separators - every separator but the first will be removed.
 *
 * To provide comma and dot support, set the [EditText] inputType to 'numberDecimal' and its digits to '0123456789.,'.
 */
class FlexibleDecimalSeparatorTextWatcher : TextWatcher {
companion object {
    private val DECIMAL_SEPARATORS = listOf('.', ',')
    private val LOCALE_DEFAULT_DECIMAL_SEPARATOR = DecimalFormatSymbols.getInstance().decimalSeparator
}

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

override fun afterTextChanged(s: Editable?) {
    if (s != null) {
        val textWithConvertedSeparators = convertSeparatorsToLocaleDefault(s.toString())
        val textWithoutMultipleSeparators = removeAdditionalSeparators(textWithConvertedSeparators)

        // Make the change if required. This only triggers one additional afterTextChanged call if there were changes.
        if(s.toString() != textWithoutMultipleSeparators) {
            s.replace(0, s.length, SpannableStringBuilder(textWithoutMultipleSeparators))
        }
    }
}

/**
 * This function converts all entered separators (in [DECIMAL_SEPARATORS]) to the [LOCALE_DEFAULT_DECIMAL_SEPARATOR].
 */
private fun convertSeparatorsToLocaleDefault(original: String): String {
    var result = original
    DECIMAL_SEPARATORS.forEach { separator ->
        if (separator != LOCALE_DEFAULT_DECIMAL_SEPARATOR && result.contains(separator)) {
            result = result.replace(separator, LOCALE_DEFAULT_DECIMAL_SEPARATOR)
        }
    }
    return result
}

/**
 * Strip out all separators but the first.
 * In this function we assume all separators are already converted to the locale default.
 */
private fun removeAdditionalSeparators(original: String): String {
    var result = original
    var separatorCount = result.count { c -> c == LOCALE_DEFAULT_DECIMAL_SEPARATOR }
    if(separatorCount > 1) {
        // We will reverse the text so we can keep stripping the last (first in reverse) separator off.
        var textReversed = result.reversed()
        val separatorRegex = Regex.fromLiteral(LOCALE_DEFAULT_DECIMAL_SEPARATOR.toString())
        while (separatorCount > 1) {
            textReversed = textReversed.replaceFirst(separatorRegex, "")
            separatorCount--
        }
        // And finally we reverse it back to the original order.
        result = textReversed.reversed()
    }
    return result
}

}

I have a solution that allows the user to enter both the dot and the comma (if available on the keyboard), but only displays the locale default separator. In addition it will not allow the user to enter more than 1 separator. No issues with references to EditText or infinite loops. It is a combination of several answers in this thread suited to my needs.

As with the accepted answer, configure the EditText accordingly:

android:inputType="numberDecimal"
android:digits="0123456789.,"

Then set a custom TextWatcher on the EditText:

myEditText.addTextChangedListener(FlexibleDecimalSeparatorTextWatcher())

And include the custom TextWatcher:

import android.text.Editable
import android.text.SpannableStringBuilder
import android.text.TextWatcher
import android.widget.EditText
import java.text.DecimalFormatSymbols

/**
 * The [FlexibleDecimalSeparatorTextWatcher] allows the user to input both the comma (,) and dot (.) as a decimal separator,
 * and will then automatically convert each entered separator into the locale default separator.
 * If the user were to enter multiple separators - every separator but the first will be removed.
 *
 * To provide comma and dot support, set the [EditText] inputType to 'numberDecimal' and its digits to '0123456789.,'.
 */
class FlexibleDecimalSeparatorTextWatcher : TextWatcher {
companion object {
    private val DECIMAL_SEPARATORS = listOf('.', ',')
    private val LOCALE_DEFAULT_DECIMAL_SEPARATOR = DecimalFormatSymbols.getInstance().decimalSeparator
}

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

override fun afterTextChanged(s: Editable?) {
    if (s != null) {
        val textWithConvertedSeparators = convertSeparatorsToLocaleDefault(s.toString())
        val textWithoutMultipleSeparators = removeAdditionalSeparators(textWithConvertedSeparators)

        // Make the change if required. This only triggers one additional afterTextChanged call if there were changes.
        if(s.toString() != textWithoutMultipleSeparators) {
            s.replace(0, s.length, SpannableStringBuilder(textWithoutMultipleSeparators))
        }
    }
}

/**
 * This function converts all entered separators (in [DECIMAL_SEPARATORS]) to the [LOCALE_DEFAULT_DECIMAL_SEPARATOR].
 */
private fun convertSeparatorsToLocaleDefault(original: String): String {
    var result = original
    DECIMAL_SEPARATORS.forEach { separator ->
        if (separator != LOCALE_DEFAULT_DECIMAL_SEPARATOR && result.contains(separator)) {
            result = result.replace(separator, LOCALE_DEFAULT_DECIMAL_SEPARATOR)
        }
    }
    return result
}

/**
 * Strip out all separators but the first.
 * In this function we assume all separators are already converted to the locale default.
 */
private fun removeAdditionalSeparators(original: String): String {
    var result = original
    var separatorCount = result.count { c -> c == LOCALE_DEFAULT_DECIMAL_SEPARATOR }
    if(separatorCount > 1) {
        // We will reverse the text so we can keep stripping the last (first in reverse) separator off.
        var textReversed = result.reversed()
        val separatorRegex = Regex.fromLiteral(LOCALE_DEFAULT_DECIMAL_SEPARATOR.toString())
        while (separatorCount > 1) {
            textReversed = textReversed.replaceFirst(separatorRegex, "")
            separatorCount--
        }
        // And finally we reverse it back to the original order.
        result = textReversed.reversed()
    }
    return result
}

}

千と千尋 2024-10-02 13:07:36

对于 Mono(Droid) 解决方案:

decimal decimalValue = decimal.Parse(input.Text.Replace(",", ".") , CultureInfo.InvariantCulture);

For Mono(Droid) solutions:

decimal decimalValue = decimal.Parse(input.Text.Replace(",", ".") , CultureInfo.InvariantCulture);
潦草背影 2024-10-02 13:07:36

这里的所有其他帖子都有重大漏洞,所以这里有一个解决方案:

  • 根据区域强制使用逗号或句号,不会让您键入相反的逗号或句号。
  • 如果 EditText 以某个值开头,它将根据需要替换正确的分隔符。

在XML中:

<EditText
    ...
    android:inputType="numberDecimal" 
    ... />

类变量:

private boolean isDecimalSeparatorComma = false;

在onCreate中,找到当前语言环境中使用的分隔符:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
    NumberFormat nf = NumberFormat.getInstance();
    if (nf instanceof DecimalFormat) {
        DecimalFormatSymbols sym = ((DecimalFormat) nf).getDecimalFormatSymbols();
        char decSeparator = sym.getDecimalSeparator();
        isDecimalSeparatorComma = Character.toString(decSeparator).equals(",");
    }
}

还有onCreate,用这个来更新如果您加载当前值,则为:

// Replace editText with commas or periods as needed for viewing
String editTextValue = getEditTextValue(); // load your current value
if (editTextValue.contains(".") && isDecimalSeparatorComma) {
    editTextValue = editTextValue.replaceAll("\\.",",");
} else if (editTextValue.contains(",") && !isDecimalSeparatorComma) {
    editTextValue = editTextValue.replaceAll(",",".");
}
setEditTextValue(editTextValue); // override your current value

此外,在创建时添加侦听器

editText.addTextChangedListener(editTextWatcher);

if (isDecimalSeparatorComma) {
    editText.setKeyListener(DigitsKeyListener.getInstance("0123456789,"));
} else {
    editText.setKeyListener(DigitsKeyListener.getInstance("0123456789."));
}

editTextWatcher

TextWatcher editTextWatcher = 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) {
        String editTextValue = s.toString();

        // Count up the number of commas and periods
        Pattern pattern = Pattern.compile("[,.]");
        Matcher matcher = pattern.matcher(editTextValue);
        int count = 0;
        while (matcher.find()) {
            count++;
        }

        // Don't let it put more than one comma or period
        if (count > 1) {
            s.delete(s.length()-1, s.length());
        } else {
            // If there is a comma or period at the end the value hasn't changed so don't update
            if (!editTextValue.endsWith(",") && !editTextValue.endsWith(".")) {
                doSomething()
            }
        }
    }
};

doSomething() 示例,转换为数据的标准周期操纵

private void doSomething() {
    try {
        String editTextStr = editText.getText().toString();
        if (isDecimalSeparatorComma) {
            editTextStr = editTextStr.replaceAll(",",".");
        }
        float editTextFloatValue = editTextStr.isEmpty() ?
                0.0f :
                Float.valueOf(editTextStr);

        ... use editTextFloatValue
    } catch (NumberFormatException e) {
        Log.e(TAG, "Error converting String to Double");
    }
}

All the other posts here had major holes in them, so here's a solution that will:

  • Enforce commas or periods based on region, will not let you type the opposite one.
  • If the EditText starts with some value, it replaces the correct separator as needed.

In the XML:

<EditText
    ...
    android:inputType="numberDecimal" 
    ... />

Class variable:

private boolean isDecimalSeparatorComma = false;

In onCreate, find the separator used in the current locale:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
    NumberFormat nf = NumberFormat.getInstance();
    if (nf instanceof DecimalFormat) {
        DecimalFormatSymbols sym = ((DecimalFormat) nf).getDecimalFormatSymbols();
        char decSeparator = sym.getDecimalSeparator();
        isDecimalSeparatorComma = Character.toString(decSeparator).equals(",");
    }
}

Also onCreate, Use this to update it if you're loading in a current value:

// Replace editText with commas or periods as needed for viewing
String editTextValue = getEditTextValue(); // load your current value
if (editTextValue.contains(".") && isDecimalSeparatorComma) {
    editTextValue = editTextValue.replaceAll("\\.",",");
} else if (editTextValue.contains(",") && !isDecimalSeparatorComma) {
    editTextValue = editTextValue.replaceAll(",",".");
}
setEditTextValue(editTextValue); // override your current value

Also onCreate, Add the Listeners

editText.addTextChangedListener(editTextWatcher);

if (isDecimalSeparatorComma) {
    editText.setKeyListener(DigitsKeyListener.getInstance("0123456789,"));
} else {
    editText.setKeyListener(DigitsKeyListener.getInstance("0123456789."));
}

editTextWatcher

TextWatcher editTextWatcher = 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) {
        String editTextValue = s.toString();

        // Count up the number of commas and periods
        Pattern pattern = Pattern.compile("[,.]");
        Matcher matcher = pattern.matcher(editTextValue);
        int count = 0;
        while (matcher.find()) {
            count++;
        }

        // Don't let it put more than one comma or period
        if (count > 1) {
            s.delete(s.length()-1, s.length());
        } else {
            // If there is a comma or period at the end the value hasn't changed so don't update
            if (!editTextValue.endsWith(",") && !editTextValue.endsWith(".")) {
                doSomething()
            }
        }
    }
};

doSomething() example, convert to standard period for data manipulation

private void doSomething() {
    try {
        String editTextStr = editText.getText().toString();
        if (isDecimalSeparatorComma) {
            editTextStr = editTextStr.replaceAll(",",".");
        }
        float editTextFloatValue = editTextStr.isEmpty() ?
                0.0f :
                Float.valueOf(editTextStr);

        ... use editTextFloatValue
    } catch (NumberFormatException e) {
        Log.e(TAG, "Error converting String to Double");
    }
}
断念 2024-10-02 13:07:36

您可以执行以下操作:

DecimalFormatSymbols d = DecimalFormatSymbols.getInstance(Locale.getDefault());
input.setFilters(new InputFilter[] { new DecimalDigitsInputFilter(5, 2) });
input.setKeyListener(DigitsKeyListener.getInstance("0123456789" + d.getDecimalSeparator()));

然后您可以使用输入过滤器:

    public class DecimalDigitsInputFilter implements InputFilter {

Pattern mPattern;

public DecimalDigitsInputFilter(int digitsBeforeZero, int digitsAfterZero) {
    DecimalFormatSymbols d = new DecimalFormatSymbols(Locale.getDefault());
    String s = "\\" + d.getDecimalSeparator();
    mPattern = Pattern.compile("[0-9]{0," + (digitsBeforeZero - 1) + "}+((" + s + "[0-9]{0," + (digitsAfterZero - 1) + "})?)||(" + s + ")?");
}

@Override
public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {

    Matcher matcher = mPattern.matcher(dest);
    if (!matcher.matches())
        return "";
    return null;
}

}

You could do the following:

DecimalFormatSymbols d = DecimalFormatSymbols.getInstance(Locale.getDefault());
input.setFilters(new InputFilter[] { new DecimalDigitsInputFilter(5, 2) });
input.setKeyListener(DigitsKeyListener.getInstance("0123456789" + d.getDecimalSeparator()));

And then you could use an input filter:

    public class DecimalDigitsInputFilter implements InputFilter {

Pattern mPattern;

public DecimalDigitsInputFilter(int digitsBeforeZero, int digitsAfterZero) {
    DecimalFormatSymbols d = new DecimalFormatSymbols(Locale.getDefault());
    String s = "\\" + d.getDecimalSeparator();
    mPattern = Pattern.compile("[0-9]{0," + (digitsBeforeZero - 1) + "}+((" + s + "[0-9]{0," + (digitsAfterZero - 1) + "})?)||(" + s + ")?");
}

@Override
public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {

    Matcher matcher = mPattern.matcher(dest);
    if (!matcher.matches())
        return "";
    return null;
}

}

囍笑 2024-10-02 13:07:36

要本地化您的输入,请使用:

char sep = DecimalFormatSymbols.getInstance().getDecimalSeparator();

然后添加:

textEdit.setKeyListener(DigitsKeyListener.getInstance("0123456789" + sep));

不要忘记将“,”替换为“。”所以 Float 或 Double 可以毫无错误地解析它。

to get localize your input use:

char sep = DecimalFormatSymbols.getInstance().getDecimalSeparator();

and then add:

textEdit.setKeyListener(DigitsKeyListener.getInstance("0123456789" + sep));

than don't forget to replace "," with "." so Float or Double can parse it without errors.

把人绕傻吧 2024-10-02 13:07:36

我可以确认建议的修复程序不适用于三星 IME(至少在 S6 和 S9 上),也许还有 LG。无论区域设置如何,它们仍然显示一个点作为小数分隔符。切换到 Google 的 IME 可以解决这个问题,但对于大多数开发人员来说这并不是一个选择。

Oreo 中也没有修复这些键盘的问题,因为这是三星和/或 LG 必须做的修复,然后甚至推送到他们古老的手机上。

相反,我分叉了 number-keyboard 项目 并添加了一种其行为类似于 IME 的模式: 分叉。有关详细信息,请参阅项目示例。这对我来说效果很好,类似于您在银行应用程序中看到的许多“PIN 输入”假 IME。

示例应用程序截图

I can confirm that the fixes proposed do not work on Samsung IMEs (at least on S6 and S9) and maybe LG. They still show a dot as decimal separator regardless of locale. Switching to Google's IME fixes this but is hardly an option for most developers.

It also has not been fixed in Oreo for these keyboards since it is a fix that Samsung and/or LG have to do and then to push even to their ancient handsets.

I have instead forked the number-keyboard project and added a mode where it behaves like an IME: fork. See the project sample for details. This has worked quite well for me and is similar to many of the "PIN entry" fake IMEs you see in banking apps.

Sample app screenshot

千纸鹤带着心事 2024-10-02 13:07:36

您可以使用 inputType="phone",但是在这种情况下,您将必须处理多个 ,. 存在,因此需要进行额外的验证将是必要的。

You could use inputType="phone", however in that case you would have to deal with multiple , or . being present, so additional validation would be necessary.

忘年祭陌 2024-10-02 13:07:36

我对 KOTLIN 的修复

遇到了同样的错误,我用以下方法修复了它:

val separator = DecimalFormatSymbols.getInstance().decimalSeparator
mEditText.keyListener = DigitsKeyListener.getInstance("0123456789$separator")

并且效果很好。 !但!在三星键盘上,不显示分隔符,因此您无法输入十进制数字。

所以我必须通过检查是否使用三星键盘来解决这个问题:

    val x = Settings.Secure.getString(getContentResolver(), Settings.Secure.DEFAULT_INPUT_METHOD);
    if (x.toLowerCase().contains("samsung")) {}
  

但是你仍然有“。”作为小数点分隔符。因此,如果分隔符是逗号,则必须用逗号替换点:

val separator: Char = DecimalFormatSymbols.getInstance().decimalSeparator
 
  if (separator == ',') {
     mEditText.addTextChangedListener(object : TextWatcher {
                        override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) = Unit
    
                        override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) = Unit
    
                        override fun afterTextChanged(s: Editable?) {
    
                            if (!s.isNullOrEmpty()) {
                                
                                    if (s.toString().contains(".")) {
                                        val replaced = s.toString().replace('.', separator)
                                        mEditText.setText(replaced)
                                        mEditText.setSelection(replaced.length)
                                    }
    
                                
                            }
                        }
                    })
                }

但是,您必须检查是否没有人在 EditText 字段中键入更多“,”。这可以通过正则表达式来完成。

我的整个解决方案:

val x = Settings.Secure.getString(getContentResolver(), Settings.Secure.DEFAULT_INPUT_METHOD);
  if (x.toLowerCase().contains("samsung")) {
         val Number_REGEX: Pattern =  Pattern.compile("^([1-9])*([.,]{1}[0-9]{0,10})?$")
         val separator: Char = DecimalFormatSymbols.getInstance().decimalSeparator
         if (separator == ',') {
            mEditText.addTextChangedListener(object : TextWatcher {
                override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) = Unit

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

                override fun afterTextChanged(s: Editable?) {

                    if (!s.isNullOrEmpty()) {
                        val matcherMail = Number_REGEX.matcher(s.toString())
                        if (!matcherMail.matches()) {
                            val length: Int = s.length

                            s.delete(length - 1, length);
                        } else {
                            if (s.toString().contains(".")) {
                                val replaced = s.toString().replace('.', separator)
                                mEditText.setText(replaced)
                                mEditText.setSelection(replaced.length)
                            }

                        }
                    }
                }
            })
        }
    } else {
        val separator = DecimalFormatSymbols.getInstance().decimalSeparator
        mEditText.keyListener = DigitsKeyListener.getInstance("0123456789$separator")
       
    }

xml文件:

 <com.google.android.material.textfield.TextInputEditText
        android:id="@+id/tEditText"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="Input"
        android:inputType="numberDecimal"
        android:imeOptions="actionDone"/>

如果你想使用号码,请确保获得正确的格式:

val x = NumberFormat.getInstance().parse(mEditText.text.toString()).toDouble()

My fix for KOTLIN

I came across the same bug, which i fixed with:

val separator = DecimalFormatSymbols.getInstance().decimalSeparator
mEditText.keyListener = DigitsKeyListener.getInstance("0123456789$separator")

and this works quite fine. !BUT! on Samsung Keyboards, the separator is NOT shown, so you cannot type in decimal numbers.

so i had to fix this issue with checking, if Samsung Keyboard is used:

    val x = Settings.Secure.getString(getContentResolver(), Settings.Secure.DEFAULT_INPUT_METHOD);
    if (x.toLowerCase().contains("samsung")) {}
  

But then you still have the "." as decimal separator. Therefore you have to replace the dot with comma, if the separator is comma:

val separator: Char = DecimalFormatSymbols.getInstance().decimalSeparator
 
  if (separator == ',') {
     mEditText.addTextChangedListener(object : TextWatcher {
                        override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) = Unit
    
                        override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) = Unit
    
                        override fun afterTextChanged(s: Editable?) {
    
                            if (!s.isNullOrEmpty()) {
                                
                                    if (s.toString().contains(".")) {
                                        val replaced = s.toString().replace('.', separator)
                                        mEditText.setText(replaced)
                                        mEditText.setSelection(replaced.length)
                                    }
    
                                
                            }
                        }
                    })
                }

But then you have to check that nobody types more "," in the EditTextfield. This can be done with a Regex.

My whole solution:

val x = Settings.Secure.getString(getContentResolver(), Settings.Secure.DEFAULT_INPUT_METHOD);
  if (x.toLowerCase().contains("samsung")) {
         val Number_REGEX: Pattern =  Pattern.compile("^([1-9])*([.,]{1}[0-9]{0,10})?
quot;)
         val separator: Char = DecimalFormatSymbols.getInstance().decimalSeparator
         if (separator == ',') {
            mEditText.addTextChangedListener(object : TextWatcher {
                override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) = Unit

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

                override fun afterTextChanged(s: Editable?) {

                    if (!s.isNullOrEmpty()) {
                        val matcherMail = Number_REGEX.matcher(s.toString())
                        if (!matcherMail.matches()) {
                            val length: Int = s.length

                            s.delete(length - 1, length);
                        } else {
                            if (s.toString().contains(".")) {
                                val replaced = s.toString().replace('.', separator)
                                mEditText.setText(replaced)
                                mEditText.setSelection(replaced.length)
                            }

                        }
                    }
                }
            })
        }
    } else {
        val separator = DecimalFormatSymbols.getInstance().decimalSeparator
        mEditText.keyListener = DigitsKeyListener.getInstance("0123456789$separator")
       
    }

xml file:

 <com.google.android.material.textfield.TextInputEditText
        android:id="@+id/tEditText"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="Input"
        android:inputType="numberDecimal"
        android:imeOptions="actionDone"/>

If you want to use the number, make sure to get the right format:

val x = NumberFormat.getInstance().parse(mEditText.text.toString()).toDouble()
℉絮湮 2024-10-02 13:07:36

Android 有一个内置的数字格式化程序。

您可以将其添加到 EditText 以允许小数点和逗号:
android:inputType="numberDecimal"android:digits="0123456789.,"

然后在代码中的某个位置,当用户单击保存时或输入文本后(使用听众)。

// Format the number to the appropriate double
try { 
    Number formatted = NumberFormat.getInstance().parse(editText.getText().toString());
    cost = formatted.doubleValue();
} catch (ParseException e) {
    System.out.println("Error parsing cost string " + editText.getText().toString());
    cost = 0.0;
}

Android has a built in number formatter.

You can add this to your EditText to allow decimals and commas:
android:inputType="numberDecimal" and android:digits="0123456789.,"

Then somewhere in your code, either when user clicks save or after text is entered (use a listener).

// Format the number to the appropriate double
try { 
    Number formatted = NumberFormat.getInstance().parse(editText.getText().toString());
    cost = formatted.doubleValue();
} catch (ParseException e) {
    System.out.println("Error parsing cost string " + editText.getText().toString());
    cost = 0.0;
}
碍人泪离人颜 2024-10-02 13:07:36

我决定仅在编辑时将逗号更改为点。这是我棘手且相对简单的解决方法:

    editText.setOnFocusChangeListener(new View.OnFocusChangeListener() {
        @Override
        public void onFocusChange(View v, boolean hasFocus) {
            EditText editText = (EditText) v; 
            String text = editText.getText().toString();
            if (hasFocus) {
                editText.setText(text.replace(",", "."));
            } else {
                if (!text.isEmpty()) {
                    Double doubleValue = Double.valueOf(text.replace(",", "."));
                    editText.setText(someDecimalFormatter.format(doubleValue));
                }
            }
        }
    });

someDecimalFormatter 将使用逗号或点取决于区域设置

I decided to change comma to dot only while editing. Here is my tricky and relative simple workaround:

    editText.setOnFocusChangeListener(new View.OnFocusChangeListener() {
        @Override
        public void onFocusChange(View v, boolean hasFocus) {
            EditText editText = (EditText) v; 
            String text = editText.getText().toString();
            if (hasFocus) {
                editText.setText(text.replace(",", "."));
            } else {
                if (!text.isEmpty()) {
                    Double doubleValue = Double.valueOf(text.replace(",", "."));
                    editText.setText(someDecimalFormatter.format(doubleValue));
                }
            }
        }
    });

someDecimalFormatter will use comma or dot depends on Locale

∞琼窗梦回ˉ 2024-10-02 13:07:36

我的解决方案是:

  • 在主要活动中:

    字符分隔符 =DecimalFormatSymbols.getInstance().getDecimalSeparator();
    textViewPitchDeadZone.setKeyListener(DigitsKeyListener.getInstance("0123456789" +分隔符));

  • 在xml文件中:
    <代码>
    android:imeOptions="flagNoFullscreen"
    android:inputType="numberDecimal"

我将 editText 中的双精度数作为字符串。

My solution is:

  • In main activity:

    char separator =DecimalFormatSymbols.getInstance().getDecimalSeparator();
    textViewPitchDeadZone.setKeyListener(DigitsKeyListener.getInstance("0123456789" + separator));

  • In xml file:

    android:imeOptions="flagNoFullscreen"
    android:inputType="numberDecimal"

and I took the double in the editText as a String.

汹涌人海 2024-10-02 13:07:36

8 年多过去了,我很惊讶这个问题还没有解决...
我在这个简单的问题上遇到了困难,因为@Martin 得到最多支持的答案允许输入多个分隔符,即用户可以输入“12,,,,,,12,1,,21,2,”< br>
另外,第二个问题是,在某些设备上,数字键盘上不显示逗号(或需要多次按点按钮)

这是我的解决方案,它解决了上述问题和让用户输入“.”和 ',',但在 EditText 中他将看到与当前语言环境相对应的唯一小数分隔符:

editText.apply { addTextChangedListener(DoubleTextChangedListener(this)) }

以及文本观察器:

  open class DoubleTextChangedListener(private val et: EditText) : TextWatcher {

    init {
        et.inputType = InputType.TYPE_CLASS_NUMBER or InputType.TYPE_NUMBER_FLAG_DECIMAL
        et.keyListener = DigitsKeyListener.getInstance("0123456789.,")
    }

    private val separator = DecimalFormatSymbols.getInstance().decimalSeparator

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

    @CallSuper
    override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
        et.run {
            removeTextChangedListener(this@DoubleTextChangedListener)
            val formatted = toLocalizedDecimal(s.toString(), separator)
            setText(formatted)
            setSelection(formatted.length)
            addTextChangedListener(this@DoubleTextChangedListener)
        }
    }

    override fun afterTextChanged(s: Editable?) {
        // empty
    }

    /**
     * Formats input to a decimal. Leaves the only separator (or none), which matches [separator].
     * Examples:
     * 1. [s]="12.12", [separator]=',' -> result= "12,12"
     * 2. [s]="12.12", [separator]='.' -> result= "12.12"
     * 4. [s]="12,12", [separator]='.' -> result= "12.12"
     * 5. [s]="12,12,,..,,,,,34..,", [separator]=',' -> result= "12,1234"
     * 6. [s]="12.12,,..,,,,,34..,", [separator]='.' -> result= "12.1234"
     * 7. [s]="5" -> result= "5"
     */
    private fun toLocalizedDecimal(s: String, separator: Char): String {
        val cleared = s.replace(",", ".")
        val splitted = cleared.split('.').filter { it.isNotBlank() }
        return when (splitted.size) {
            0 -> s
            1 -> cleared.replace('.', separator).replaceAfter(separator, "")
            2 -> splitted.joinToString(separator.toString())
            else -> splitted[0]
                    .plus(separator)
                    .plus(splitted.subList(1, splitted.size - 1).joinToString(""))
        }
    }
}

It's more than 8 years passed and I am surprised, this issue isn't fixed yet...
I struggled with this simple issue since the most upvoted answer by @Martin lets typing multiple separators, i.e. user can type in "12,,,,,,12,1,,21,2,"
Also, the second concern is that on some devices comma is not shown on the numerical keyboard (or requires multiple pressing of a dot button)

Here is my workaround solution, which solves the mentioned problems and lets user typing '.' and ',', but in EditText he will see the only decimal separator which corresponds to current locale:

editText.apply { addTextChangedListener(DoubleTextChangedListener(this)) }

And the text watcher:

  open class DoubleTextChangedListener(private val et: EditText) : TextWatcher {

    init {
        et.inputType = InputType.TYPE_CLASS_NUMBER or InputType.TYPE_NUMBER_FLAG_DECIMAL
        et.keyListener = DigitsKeyListener.getInstance("0123456789.,")
    }

    private val separator = DecimalFormatSymbols.getInstance().decimalSeparator

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

    @CallSuper
    override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
        et.run {
            removeTextChangedListener(this@DoubleTextChangedListener)
            val formatted = toLocalizedDecimal(s.toString(), separator)
            setText(formatted)
            setSelection(formatted.length)
            addTextChangedListener(this@DoubleTextChangedListener)
        }
    }

    override fun afterTextChanged(s: Editable?) {
        // empty
    }

    /**
     * Formats input to a decimal. Leaves the only separator (or none), which matches [separator].
     * Examples:
     * 1. [s]="12.12", [separator]=',' -> result= "12,12"
     * 2. [s]="12.12", [separator]='.' -> result= "12.12"
     * 4. [s]="12,12", [separator]='.' -> result= "12.12"
     * 5. [s]="12,12,,..,,,,,34..,", [separator]=',' -> result= "12,1234"
     * 6. [s]="12.12,,..,,,,,34..,", [separator]='.' -> result= "12.1234"
     * 7. [s]="5" -> result= "5"
     */
    private fun toLocalizedDecimal(s: String, separator: Char): String {
        val cleared = s.replace(",", ".")
        val splitted = cleared.split('.').filter { it.isNotBlank() }
        return when (splitted.size) {
            0 -> s
            1 -> cleared.replace('.', separator).replaceAfter(separator, "")
            2 -> splitted.joinToString(separator.toString())
            else -> splitted[0]
                    .plus(separator)
                    .plus(splitted.subList(1, splitted.size - 1).joinToString(""))
        }
    }
}
离不开的别离 2024-10-02 13:07:36

简单的解决方案,制作自定义控件。 (这是在 Xamarin android 中制作的,但应该可以轻松移植到 java)

public class EditTextDecimalNumber:EditText
{
    readonly string _numberFormatDecimalSeparator;

    public EditTextDecimalNumber(Context context, IAttributeSet attrs) : base(context, attrs)
    {
        InputType = InputTypes.NumberFlagDecimal;
        TextChanged += EditTextDecimalNumber_TextChanged;
        _numberFormatDecimalSeparator = System.Threading.Thread.CurrentThread.CurrentUICulture.NumberFormat.NumberDecimalSeparator;

        KeyListener = DigitsKeyListener.GetInstance($"0123456789{_numberFormatDecimalSeparator}");
    }

    private void EditTextDecimalNumber_TextChanged(object sender, TextChangedEventArgs e)
    {
        int noOfOccurence = this.Text.Count(x => x.ToString() == _numberFormatDecimalSeparator);
        if (noOfOccurence >=2)
        {
            int lastIndexOf = this.Text.LastIndexOf(_numberFormatDecimalSeparator,StringComparison.CurrentCulture);
            if (lastIndexOf!=-1)
            {
                this.Text = this.Text.Substring(0, lastIndexOf);
                this.SetSelection(this.Text.Length);
            }

        }
    }
}

Simple solution, make a custom control. (this is made in Xamarin android but should port easily to java)

public class EditTextDecimalNumber:EditText
{
    readonly string _numberFormatDecimalSeparator;

    public EditTextDecimalNumber(Context context, IAttributeSet attrs) : base(context, attrs)
    {
        InputType = InputTypes.NumberFlagDecimal;
        TextChanged += EditTextDecimalNumber_TextChanged;
        _numberFormatDecimalSeparator = System.Threading.Thread.CurrentThread.CurrentUICulture.NumberFormat.NumberDecimalSeparator;

        KeyListener = DigitsKeyListener.GetInstance($"0123456789{_numberFormatDecimalSeparator}");
    }

    private void EditTextDecimalNumber_TextChanged(object sender, TextChangedEventArgs e)
    {
        int noOfOccurence = this.Text.Count(x => x.ToString() == _numberFormatDecimalSeparator);
        if (noOfOccurence >=2)
        {
            int lastIndexOf = this.Text.LastIndexOf(_numberFormatDecimalSeparator,StringComparison.CurrentCulture);
            if (lastIndexOf!=-1)
            {
                this.Text = this.Text.Substring(0, lastIndexOf);
                this.SetSelection(this.Text.Length);
            }

        }
    }
}
蓝礼 2024-10-02 13:07:36

我必须想出一个解决办法,它由不同的答案组成。
这将允许使用“,”或“.”。最多一位小数

这是我的 editText:

val separator: Char = DecimalFormatSymbols.getInstance().decimalSeparator
editTextBox.filters = arrayOf<InputFilter>(DecimalDigitsInputFilter(5, 1, separator))
editTextBox.keyListener = DigitsKeyListener.getInstance("0123456789$separator")

和我处理特定正则表达式的类:

class DecimalDigitsInputFilter(
    digitsBeforeZero: Int, 
    digitsAfterZero: Int, 
    separator: Char
) : InputFilter {
    private val mPattern: Pattern =
        Pattern.compile("[0-9]{0," + (digitsBeforeZero - 1) + "}+((\\$separator[0-9]{0," + (digitsAfterZero - 1) + "})?)||(\\$separator)?")

    override fun filter(source: CharSequence, start: Int, end: Int, dest: Spanned, dstart: Int, dend: Int): CharSequence? {
        val matcher = mPattern.matcher(dest)
        return if (!matcher.matches()) "" else null
    }
}

I had to come up with a work around on my side that it consisted of a mix of different answers.
This will allow for "," or "." for up to one decimal

This is my editText:

val separator: Char = DecimalFormatSymbols.getInstance().decimalSeparator
editTextBox.filters = arrayOf<InputFilter>(DecimalDigitsInputFilter(5, 1, separator))
editTextBox.keyListener = DigitsKeyListener.getInstance("0123456789$separator")

and my class to handle specific regex:

class DecimalDigitsInputFilter(
    digitsBeforeZero: Int, 
    digitsAfterZero: Int, 
    separator: Char
) : InputFilter {
    private val mPattern: Pattern =
        Pattern.compile("[0-9]{0," + (digitsBeforeZero - 1) + "}+((\\$separator[0-9]{0," + (digitsAfterZero - 1) + "})?)||(\\$separator)?")

    override fun filter(source: CharSequence, start: Int, end: Int, dest: Spanned, dstart: Int, dend: Int): CharSequence? {
        val matcher = mPattern.matcher(dest)
        return if (!matcher.matches()) "" else null
    }
}
悟红尘 2024-10-02 13:07:36
EditText.inputType =
    TYPE_NUMBER_FLAG_DECIMAL or TYPE_NUMBER_FLAG_SIGNED or TYPE_CLASS_NUMBER

EditText.keyListener = DigitsKeyListener.getInstance("0123456789,-")

EditText.addTextChangedListener(object : TextWatcher {
    var beforeText = ""

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

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

    override fun afterTextChanged(s: Editable?) {
        if (s != null) {
            if (s.length > 1 && s.last() == '-') {
                s.replace(0, s.length, beforeText)
            }

            if (s.length == 1 && s.last() == ',') {
                s.replace(0, s.length, beforeText)
            }

            if (s.length > 1 && beforeText.length < s.length && beforeText.contains(',') && s.last() == ',') {
                s.replace(0, s.length, beforeText)
            }
        }
    }
})
EditText.inputType =
    TYPE_NUMBER_FLAG_DECIMAL or TYPE_NUMBER_FLAG_SIGNED or TYPE_CLASS_NUMBER

EditText.keyListener = DigitsKeyListener.getInstance("0123456789,-")

EditText.addTextChangedListener(object : TextWatcher {
    var beforeText = ""

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

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

    override fun afterTextChanged(s: Editable?) {
        if (s != null) {
            if (s.length > 1 && s.last() == '-') {
                s.replace(0, s.length, beforeText)
            }

            if (s.length == 1 && s.last() == ',') {
                s.replace(0, s.length, beforeText)
            }

            if (s.length > 1 && beforeText.length < s.length && beforeText.contains(',') && s.last() == ',') {
                s.replace(0, s.length, beforeText)
            }
        }
    }
})
暮凉 2024-10-02 13:07:36

我不知道为什么你的答案这么复杂。如果 SDK 中存在错误,您必须覆盖它或绕过它。

我选择了第二种方法来解决这个问题。如果将字符串格式化为 Locale.ENGLISH,然后将其放入 EditText(即使是空字符串)。示例:

String.format(Locale.ENGLISH,"%.6f", yourFloatNumber);

追求该解决方案,您的结果与显示的键盘兼容。然后浮点数和双精度数以典型的编程语言方式工作,使用点而不是逗号。

I don't know why your answers are so complicated. If there's a bug in SDK you must override it or go around.

I have chosen the second way to solve that problem. If you format your string as Locale.ENGLISH and then put it to the EditText (even as an empty string). Example:

String.format(Locale.ENGLISH,"%.6f", yourFloatNumber);

Chasing that solution your result are compatible with the shown keyboard. Then float and double numbers work in typical for programming languages manner with dot instead of comma.

把回忆走一遍 2024-10-02 13:07:36

我认为这个解决方案比这里写的其他解决方案简单:

<EditText
    android:inputType="numberDecimal"
    android:digits="0123456789," />

这样当你按“.”时在软键盘中没有任何反应;只允许使用数字和逗号。

I think this solution is less complex than the others written here:

<EditText
    android:inputType="numberDecimal"
    android:digits="0123456789," />

This way when you press the '.' in the soft keyboard nothing happens; only numbers and comma are allowed.

娇柔作态 2024-10-02 13:07:35

解决方法(直到 Google 修复此错误)是使用 EditTextandroid:inputType="numberDecimal"android:digits="0123456789.,"< /代码>。

然后使用以下 afterTextChanged 将 TextChangedListener 添加到 EditText:

public void afterTextChanged(Editable s) {
    double doubleValue = 0;
    if (s != null) {
        try {
            doubleValue = Double.parseDouble(s.toString().replace(',', '.'));
        } catch (NumberFormatException e) {
            //Error
        }
    }
    //Do something with doubleValue
}

A workaround (until Google fix this bug) is to use an EditText with android:inputType="numberDecimal" and android:digits="0123456789.,".

Then add a TextChangedListener to the EditText with the following afterTextChanged:

public void afterTextChanged(Editable s) {
    double doubleValue = 0;
    if (s != null) {
        try {
            doubleValue = Double.parseDouble(s.toString().replace(',', '.'));
        } catch (NumberFormatException e) {
            //Error
        }
    }
    //Do something with doubleValue
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文