Separator dziesiętny przecinek („,”) z numberDecimal inputType w EditText

137

W inputType numberDecimalin EditTextużywa kropki „.” jako separator dziesiętny. W Europie często używa się przecinka „,” zamiast tego. Mimo że moje ustawienia narodowe są ustawione na niemiecki, separatorem dziesiętnym jest nadal „.”

Czy istnieje sposób, aby przecinek był separatorem dziesiętnym?

mseo
źródło
1
ten błąd został ostatecznie naprawiony w Androidzie O: issuetracker.google.com/issues/36907764
Lovis
mówią, że jest naprawiony, ale nie mogę tego potwierdzić? czy możesz?
sebastian
7
Mogę potwierdzić, że NIE jest to naprawione, przynajmniej na moim Nexusie 4 z systemem Android 8.1 (aka LineageOS 15.1). Po ustawieniu opcji Ustawienia-> Język na francuski (Francja), EditText z android: inputType = "numberDecimal" oferuje separator „,” (przecinek), ale nadal odmawia przyjęcia przecinka. Oferowane „.” (kropka dziesiętna) jest akceptowana. Minęło ponad 9 lat od pierwszego zgłoszenia tego błędu. Czy to jakiś rekord? Kulawy.
pete,

Odpowiedzi:

105

Obejściem problemu (do czasu naprawienia tego błędu przez Google) jest użycie EditTextz android:inputType="numberDecimal"i android:digits="0123456789.,".

Następnie dodaj TextChangedListener do EditText z następującym 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
}
Jaskółka oknówka
źródło
1
@Zoombie, aby przecinek (,) pojawił się na klawiaturze, zależy od języka ustawionego w urządzeniu. Jeśli dane wejściowe mają typ numberDecimal, a Twoim językiem jest angielski (Stany Zjednoczone), pojawi się na urządzeniach Nexus (informacje). Możliwe, że urządzenia inne niż Nexus nie szanują tego
hcpl
11
Działa, ale pamiętaj, że przepuszcza tekst typu „24,22,55”. Może być konieczne dodanie dodatkowej weryfikacji, aby to naprawić!
dimsuz
8
Czy to wciąż jest droga?
Willi Mentzel
Jeszcze lepiej, użyj funkcji char localizedSeparator = DecimalFormatSymbols.getInstance (). GetDecimalSeparator (); localizedFloatString = localizedFloatString.replace ('.', localizedSeparator);
Southerton
4
Wygląda na to, że to po prostu zamiana jednego błędu na inny. Jak zaimplementowano powyżej, będzie to działać dla lokalizacji, które używają zamiast. kosztem sytuacji odwrotnej, która jest bardziej powszechna na całym świecie. Ulepszenie @ Southerton pomaga w tym, jednak Twoi użytkownicy mogą być zaskoczeni, gdy trafią. i mają, pojawiają się w danych wejściowych.
Nick,
30

Odmiana oferowanych tutaj rozwiązań `` cyfrowych '':

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

Biorąc pod uwagę separator ustawień regionalnych.

dstibbe
źródło
To najczystsza odpowiedź na pierwotne pytanie. Dzięki
peter.bartos
Umieść to w onCreate (), to jest droga, IMHO.
TechNyquist
6
Podoba mi się to, ale bądź ostrożny ... są klawiatury, które nie dbają o lokalizację użytkownika, więc użytkownik, który nie ma klucza ,w swoich klawiaturach. Przykłady: klawiatura Samsung (KitKat).
Brais Gabin
2
Umożliwi to zduplikowane separatory dziesiętne. Zobacz odpowiedź poniżej, aby sobie z tym poradzić: stackoverflow.com/a/45384821/6138589
Esdras Lopez
19

Następująca maska ​​waluty kodu dla EditText (123 125,155 $)

Układ XML

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

Kod

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;
        }
    });
user1269737
źródło
17

To znany błąd w Android SDK. Jedynym obejściem jest utworzenie własnej klawiatury miękkiej. Można znaleźć przykład realizacji tutaj .

EricLarch
źródło
15
Czy są jakieś wiadomości po czterech latach?
Antonio Sesto
Doświadczanie tego również w Xamarin.Forms. Kultura to {se-SV}, a klawiatura numeryczna pokazuje bota „,” (separator dziesiętny) i „”. (separator tysięcy grup), ale po naciśnięciu „,” nic nie jest wpisywane w polu tekstowym i żadne zdarzenie nie jest wywoływane
joacar
Mogę potwierdzić, że błąd nadal istnieje.
Lensflare,
naprawiono w podglądzie programisty Androida O
R00Śr
2
@ R00Widzimy, że ten problem został oznaczony jako rozwiązany w narzędziu do śledzenia problemów, ale nadal widzę ten problem podczas kierowania na API 29. Czy udało Ci się go rozwiązać, czy po prostu potwierdzić, że został oznaczony jako rozwiązany?
masterwok
7

Możesz użyć następującego obejścia, aby również uwzględnić przecinek jako prawidłowe dane wejściowe: -

Poprzez XML:

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

Programowo:

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

W ten sposób system Android pokaże klawiaturę cyfr i pozwoli na wpisanie przecinka. Mam nadzieję, że to odpowiada na pytanie :)

MiaN KhaLiD
źródło
Dzięki temu rozwiązaniu po dotknięciu „,”, ale tekst edycji pokazuje „”.
Mara Jimenez
6

Odpowiedź Martinsa nie zadziała, jeśli programistycznie tworzysz instancję EditText. Poszedłem dalej i zmodyfikowałem dołączoną DigitsKeyListenerklasę z API 14, aby umożliwić stosowanie przecinka i kropki jako separatora dziesiętnego.

Aby z tego skorzystać, zadzwoń setKeyListener()na EditTextnp

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

Jednak nadal musisz używać sztuczki Martina, w TextChangedListenerktórej zastępujesz przecinki kropkami

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;
        }
    }
}
JesperB
źródło
from doc: KeyListener powinien być używany tylko w przypadkach, gdy aplikacja ma własną klawiaturę ekranową i chce również przetwarzać zdarzenia klawiatury twardej, aby ją dopasować. developer.android.com/reference/android/text/method/…
Loda,
6

możesz użyć następujących dla różnych ustawień regionalnych

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));
        }
    });
}
Ismail
źródło
To dla mnie najlepsze rozwiązanie, ale jest problem z niektórymi telefonami, np. Samsung, które nie pokazują "," na klawiaturze. Więc zmieniłem to, aby umożliwić zarówno komę, jak i kropkę, ale potem podmieniłem zgodnie z lokalizacją
Riccardo Casatta
2

W przypadku rozwiązań Mono (Droid):

decimal decimalValue = decimal.Parse(input.Text.Replace(",", ".") , CultureInfo.InvariantCulture);
Ludwo
źródło
1

Możesz wykonać następujące czynności:

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

A potem możesz użyć filtra wejściowego:

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

}

Pablo
źródło
może być spacja między tysiącami a setkami, ten wzorzec odrzucałby sformatowane dane wejściowe
Eric Zhao
1

IMHO najlepszym rozwiązaniem tego problemu jest użycie filtra InputFilter. Ładne streszczenie jest tutaj DecimalDigitsInputFilter . Wtedy możesz po prostu:

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)});
Arkadiusz Cieśliński
źródło
To zadziałało jak urok, dziękuję! (po tylu błędnych rozwiązaniach powyżej ... :() Ale mam pytanie: jak mogę osiągnąć ten przecinek (",") wyświetlany na ekranie, a nie kropkę ("."), ponieważ na Węgrzech używamy przecinka jako separatora dziesiętnego .
Abigail La'Fay
1
android: digits = "0123456789," ustawienie można dodać do EditText. Ponadto zamiast zwracać wartość null w DecimalDigitsInputFilter, możesz zwrócić source.replace (".", ",") Zgodnie z odpowiedzią stackoverflow.com/a/40020731/1510222, nie ma możliwości ukrycia kropki na standardowej klawiaturze
Arkadiusz Cieśliński
1

aby zlokalizować swoje dane wejściowe, użyj:

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

a następnie dodaj:

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

niż nie zapomnij zamienić „,” na „”. więc Float lub Double mogą analizować go bez błędów.

Milos Simic Simo
źródło
1
To rozwiązanie pozwala na wprowadzenie wielu przecinków
Leo Droidcoder
1

Wszystkie inne posty miały w sobie duże dziury, więc oto rozwiązanie, które:

  • Wymuszaj przecinki lub kropki w zależności od regionu, nie pozwoli ci wpisać przeciwnego.
  • Jeśli EditText zaczyna się od jakiejś wartości, w razie potrzeby zastępuje właściwy separator.

W XML:

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

Zmienna klasy:

private boolean isDecimalSeparatorComma = false;

W onCreate znajdź separator używany w bieżącej lokalizacji:

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(",");
    }
}

Również onCreate, użyj tego, aby zaktualizować go, jeśli ładujesz bieżącą wartość:

// 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

Również onCreate, Dodaj detektory

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

Przykład doSomething (), konwertuj na standardowy okres do manipulacji danymi

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");
    }
}
Elijah Fry
źródło
0

Android ma wbudowany program do formatowania liczb.

Możesz dodać to do swojego, EditTextaby zezwolić na ułamki dziesiętne i przecinki: android:inputType="numberDecimal"iandroid:digits="0123456789.,"

Następnie gdzieś w kodzie, albo gdy użytkownik kliknie przycisk Zapisz, albo po wprowadzeniu tekstu (użyj nasłuchiwania).

// 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;
}
Luis
źródło
0

Postanowiłem zmienić przecinek na kropkę tylko podczas edycji. Oto moje trudne i względnie proste obejście:

    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 użyje przecinka lub kropki w zależności od ustawień regionalnych

KaMyLL
źródło
0

Nie wiem, dlaczego twoje odpowiedzi są tak skomplikowane. Jeśli występuje błąd w SDK, musisz go zastąpić lub obejść.

Wybrałem drugi sposób rozwiązania tego problemu. Jeśli sformatujesz swój ciąg jakoLocale.ENGLISH a następnie umieścisz go w EditText(nawet jako pusty ciąg). Przykład:

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

W pogoni za tym rozwiązaniem Twój wynik jest zgodny z pokazaną klawiaturą. Wtedy liczby zmiennoprzecinkowe i podwójne działają w typowy dla języków programowania sposób z kropką zamiast przecinkiem.

Dominik
źródło
0

Moje rozwiązanie to:

  • W głównej działalności:

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

  • W pliku xml: android:imeOptions="flagNoFullscreen" android:inputType="numberDecimal"

i podwoiłem w editText jako ciąg.

Lseb
źródło
0

Mogę potwierdzić, że proponowane poprawki nie działają na IME Samsunga (przynajmniej na S6 i S9) i być może LG. Nadal pokazują kropkę jako separator dziesiętny, niezależnie od ustawień regionalnych. Przejście na IME Google rozwiązuje ten problem, ale nie jest opcją dla większości programistów.

Nie został również naprawiony w Oreo dla tych klawiatur, ponieważ jest to poprawka, którą Samsung i / lub LG muszą zrobić, a następnie naciskać nawet na ich starożytne telefony.

Zamiast tego rozwidliłem projekt klawiatury numerycznej i dodałem tryb, w którym zachowuje się jak IME: fork . Zobacz przykładowy projekt, aby uzyskać szczegółowe informacje. W moim przypadku zadziałało to całkiem nieźle i jest podobne do wielu fałszywych edytorów IME „wprowadzania kodu PIN”, które można zobaczyć w aplikacjach bankowych.

Przykładowy zrzut ekranu aplikacji

Kevin Read
źródło
0

Minęło ponad 8 lat i jestem zaskoczony, ten problem nie został jeszcze rozwiązany ... Walczyłem
z tym prostym problemem, ponieważ najbardziej pozytywna odpowiedź @Martin pozwala na wpisanie wielu separatorów, tj. Użytkownik może wpisać „12 ,,, ,,, 12,1,, 21,2, "
Drugim problemem jest również to, że w niektórych urządzeniach przecinek nie jest wyświetlany na klawiaturze numerycznej (lub wymaga wielokrotnego naciśnięcia przycisku z kropką)

Oto moje rozwiązanie tymczasowe, które rozwiązuje wspomniane problemy i pozwala użytkownikowi wpisać „”. i ',', ale w EditText zobaczy jedyny separator dziesiętny, który odpowiada bieżącej lokalizacji:

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

I obserwator tekstu:

  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(""))
        }
    }
}
Leo Droidcoder
źródło
0

Proste rozwiązanie, zrób niestandardową kontrolę. (jest to zrobione w Xamarin Android, ale powinno łatwo przenieść się do 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);
            }

        }
    }
}
Nicolai
źródło
0

Możesz użyć inputType="phone", jednak w takim przypadku będziesz musiał radzić sobie z wieloma ,lub .obecnymi, więc konieczna byłaby dodatkowa walidacja.

jack's medulla oblongata
źródło
0

Moja poprawka dla KOTLIN

Natknąłem się na ten sam błąd, który naprawiłem:

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

i to działa całkiem nieźle. !ALE! na klawiaturach Samsung separator NIE jest wyświetlany, więc nie można wpisywać liczb dziesiętnych.

więc musiałem rozwiązać ten problem, sprawdzając, czy używana jest klawiatura Samsung:

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

Ale nadal masz znak „.” jako separator dziesiętny. Dlatego musisz zamienić kropkę na przecinek, jeśli separatorem jest przecinek:

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

Ale potem musisz sprawdzić, czy nikt nie wpisuje więcej "," w polu EditText. Można to zrobić za pomocą Regex.

Moje całe rozwiązanie:

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

plik 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"/>

Jeśli chcesz użyć numeru, upewnij się, że masz odpowiedni format:

val x = NumberFormat.getInstance().parse(mEditText.text.toString()).toDouble()
Alex K.
źródło
-3

Myślę, że to rozwiązanie jest mniej złożone niż inne napisane tutaj:

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

W ten sposób po naciśnięciu „.” w miękkiej klawiaturze nic się nie dzieje; dozwolone są tylko cyfry i przecinki.

Jorge Alcolea Coronel
źródło
5
jeśli to zrobisz, po prostu zepsujesz wszystkie ustawienia regionalne, które używają znaku '.' zamiast.
Nick,