W inputType
numberDecimal
in EditText
uż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?
Odpowiedzi:
Obejściem problemu (do czasu naprawienia tego błędu przez Google) jest użycie
EditText
zandroid:inputType="numberDecimal"
iandroid: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 }
źródło
Odmiana oferowanych tutaj rozwiązań `` cyfrowych '':
char separator = DecimalFormatSymbols.getInstance().getDecimalSeparator(); input.setKeyListener(DigitsKeyListener.getInstance("0123456789" + separator));
Biorąc pod uwagę separator ustawień regionalnych.
źródło
,
w swoich klawiaturach. Przykłady: klawiatura Samsung (KitKat).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; } });
źródło
To znany błąd w Android SDK. Jedynym obejściem jest utworzenie własnej klawiatury miękkiej. Można znaleźć przykład realizacji tutaj .
źródło
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 :)
źródło
Odpowiedź Martinsa nie zadziała, jeśli programistycznie tworzysz instancję EditText. Poszedłem dalej i zmodyfikowałem dołączoną
DigitsKeyListener
klasę z API 14, aby umożliwić stosowanie przecinka i kropki jako separatora dziesiętnego.Aby z tego skorzystać, zadzwoń
setKeyListener()
naEditText
np// 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
TextChangedListener
której zastępujesz przecinki kropkamiimport 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; } } }
źródło
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)); } }); }
źródło
W przypadku rozwiązań Mono (Droid):
decimal decimalValue = decimal.Parse(input.Text.Replace(",", ".") , CultureInfo.InvariantCulture);
źródło
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; }
}
źródło
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)});
źródło
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.
źródło
Wszystkie inne posty miały w sobie duże dziury, więc oto rozwiązanie, które:
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"); } }
źródło
Android ma wbudowany program do formatowania liczb.
Możesz dodać to do swojego,
EditText
aby 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; }
źródło
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
źródło
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 jako
Locale.ENGLISH
a następnie umieścisz go wEditText
(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.
źródło
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.
źródło
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.
źródło
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("")) } } }
źródło
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); } } } }
źródło
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.źródło
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()
źródło
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.
źródło