La inputType
numberDecimal
dans EditText
utilise le point '.' comme séparateur décimal. En Europe, il est courant d'utiliser une virgule ',' à la place. Même si mes paramètres régionaux sont définis en allemand, le séparateur décimal reste le '.'
Y a-t-il un moyen d'obtenir la virgule comme séparateur décimal?
Une solution de contournement (jusqu'à ce que Google corrige ce bogue) consiste à utiliser un EditText
avec Android:inputType="numberDecimal"
et Android:digits="0123456789.,"
.
Ajoutez ensuite un TextChangedListener à EditText avec l’après 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
}
Une variante des solutions "digit" proposées ici:
char separator = DecimalFormatSymbols.getInstance().getDecimalSeparator();
input.setKeyListener(DigitsKeyListener.getInstance("0123456789" + separator));
Prise en compte du séparateur de paramètres régionaux.
Masque de code monnaie pour EditText (123 125,155 USD)
Mise en page xml
<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;
}
});
La réponse de Martins ne fonctionnera pas si vous instanciez le EditText par programme. Je suis allé de l'avant et j'ai modifié la classe DigitsKeyListener
incluse de l'API 14 pour permettre à la fois une virgule et une période comme séparateur décimal.
Pour utiliser cela, appelez setKeyListener()
sur la EditText
, par exemple.
// Don't allow for signed input (minus), but allow for decimal points
editText.setKeyListener( new MyDigitsKeyListener( false, true ) );
Cependant, vous devez toujours utiliser l'astuce de Martin dans la variable TextChangedListener
où vous remplacez les virgules par des points.
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;
}
}
}
Vous pouvez utiliser la solution de contournement suivante pour inclure également une virgule en tant qu'entrée valide: -
À travers XML:
<EditText
Android:inputType="number"
Android:digits="0123456789.," />
Par programme:
EditText input = new EditText(THE_CONTEXT);
input.setKeyListener(DigitsKeyListener.getInstance("0123456789.,"));
De cette façon, le système Android affichera le clavier des chiffres et autorisera la saisie d'une virgule. J'espère que cela répond à la question :)
vous pouvez utiliser les éléments suivants pour différents lieux
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));
}
});
}
Pour les solutions Mono (Droid):
decimal decimalValue = decimal.Parse(input.Text.Replace(",", ".") , CultureInfo.InvariantCulture);
Vous pouvez faire ce qui suit:
DecimalFormatSymbols d = DecimalFormatSymbols.getInstance(Locale.getDefault());
input.setFilters(new InputFilter[] { new DecimalDigitsInputFilter(5, 2) });
input.setKeyListener(DigitsKeyListener.getInstance("0123456789" + d.getDecimalSeparator()));
Et ensuite, vous pourriez utiliser un filtre d'entrée:
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;
}
}
pour localiser votre utilisation d'entrée:
char sep = DecimalFormatSymbols.getInstance().getDecimalSeparator();
puis ajoutez:
textEdit.setKeyListener(DigitsKeyListener.getInstance("0123456789" + sep));
que ne pas oublier de remplacer "," par "." donc Float ou Double peuvent l’analyser sans erreur.
Ma solution est:
En activité principale:
char separator =DecimalFormatSymbols.getInstance().getDecimalSeparator();
textViewPitchDeadZone.setKeyListener(DigitsKeyListener.getInstance("0123456789" + separator));
En fichier xml:
Android:imeOptions="flagNoFullscreen"
Android:inputType="numberDecimal"
et j'ai pris le double dans le editText comme une chaîne.
Je peux confirmer que les correctifs proposés ne fonctionnent pas sur les IME Samsung (au moins sur les S6 et S9) et peut-être sur LG. Ils affichent toujours un point comme séparateur décimal, indépendamment de la localisation. Le passage à l'IME de Google résout ce problème mais n'est guère une option pour la plupart des développeurs.
Il n’a pas non plus été corrigé dans Oreo pour ces claviers, car c’est un correctif que Samsung et/ou LG doivent faire, puis pousser même leurs anciens combinés.
J'ai plutôt ajouté le projet clavier numérique et ajouté un mode dans lequel il se comporte comme un IME: fork . Voir l'exemple de projet pour plus de détails. Cela a très bien fonctionné pour moi et ressemble à la plupart des faux IME "Saisie du code confidentiel" que vous voyez dans les applications bancaires.
Solution simple, créez un contrôle personnalisé. (Ceci est fait dans Android Xamarin mais devrait porter facilement à 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);
}
}
}
}
Android a un formateur de numéros intégré.
Vous pouvez ajouter ceci à votre EditText
pour permettre les décimales et les virgules: Android:inputType="numberDecimal"
et Android:digits="0123456789.,"
.
Puis quelque part dans votre code, lorsque l'utilisateur clique sur Enregistrer ou après la saisie du texte (utilisez un écouteur).
// 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;
}
IMHO la meilleure approche pour ce problème est d'utiliser simplement le InputFilter. Voici un fichier Nice Gist/ DecimalDigitsInputFilter . Ensuite, vous pouvez simplement:
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)});
Je pense que cette solution est moins complexe que les autres écrites ici:
<EditText
Android:inputType="numberDecimal"
Android:digits="0123456789," />
De cette façon, lorsque vous appuyez sur la touche '.' dans le clavier logiciel rien ne se passe; seuls les chiffres et les virgules sont autorisés.
Tous les autres postes ici présentaient des lacunes majeures. Voici donc une solution qui:
dans le XML:
<EditText
...
Android:inputType="numberDecimal"
... />
Variable de classe:
private boolean isDecimalSeparatorComma = false;
Dans onCreate, recherchez le séparateur utilisé dans les paramètres régionaux actuels:
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 également, utilisez ceci pour le mettre à jour si vous chargez une valeur actuelle:
// 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
également sur Créer, Ajouter les auditeurs
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()
}
}
}
};
exemple: Quelque chose (), conversion en période standard pour la manipulation de données
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");
}
}
J'ai décidé de changer la virgule en point uniquement lors de l'édition. Voici ma solution de contournement simple et complexe:
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 utilisera une virgule ou un point dépendant de la locale
Plus de 8 ans se sont écoulés et je suis surpris que ce problème ne soit pas encore résolu ...
Je me débattais avec ce problème simple depuis La réponse la plus votée par @Martin permet de taper plusieurs séparateurs, c’est-à-dire que l’utilisateur peut taper "12 , 12,1, 21,2,"
En outre, le second problème concerne le fait que, sur certains périphériques, virgule n'apparaît pas sur le clavier numérique (ou nécessite d'appuyer plusieurs fois sur un bouton à pois).
Voici ma solution de contournement, qui résout les problèmes mentionnés et permet à l'utilisateur de taper '.' et ',', mais dans EditText, il verra le seul séparateur décimal qui correspond à la localisation actuelle:
editText.apply { addTextChangedListener(DoubleTextChangedListener(this)) }
Et l'observateur de texte:
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(""))
}
}
}