J'ai un editText, la valeur de départ est de 0,00 $. Lorsque vous appuyez sur 1, il devient 0,01 $. Appuyez sur 4, cela passe à 0,14 $. Appuyez sur 8, 1,48 $. Appuyez sur retour arrière, 0,14 $, etc.
Cela fonctionne, le problème est que si quelqu'un positionne manuellement le curseur, des problèmes se produisent dans le formatage. S'ils devaient supprimer la décimale, elle ne reviendra pas. S'ils placent le curseur devant la virgule et tapez 2, il affichera 02,00 $ au lieu de 2,00 $. S'ils essaient de supprimer le $, il supprimera un chiffre à la place, par exemple.
Voici le code que j'utilise, j'apprécierais toutes les suggestions.
mEditPrice.setRawInputType(Configuration.KEYBOARD_12KEY);
public void priceClick(View view) {
mEditPrice.addTextChangedListener(new TextWatcher(){
DecimalFormat dec = new DecimalFormat("0.00");
@Override
public void afterTextChanged(Editable arg0) {
}
@Override
public void beforeTextChanged(CharSequence s, int start,
int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start,
int before, int count) {
if(!s.toString().matches("^\\$(\\d{1,3}(\\,\\d{3})*|(\\d+))(\\.\\d{2})?$"))
{
String userInput= ""+s.toString().replaceAll("[^\\d]", "");
if (userInput.length() > 0) {
Float in=Float.parseFloat(userInput);
float percen = in/100;
mEditPrice.setText("$"+dec.format(percen));
mEditPrice.setSelection(mEditPrice.getText().length());
}
}
}
});
Réponses:
J'ai testé votre méthode, mais elle échoue lorsque j'utilise de grands nombres ... J'ai créé ceci:
private String current = ""; @Override public void onTextChanged(CharSequence s, int start, int before, int count) { if(!s.toString().equals(current)){ [your_edittext].removeTextChangedListener(this); String cleanString = s.toString().replaceAll("[$,.]", ""); double parsed = Double.parseDouble(cleanString); String formatted = NumberFormat.getCurrencyInstance().format((parsed/100)); current = formatted; [your_edittext].setText(formatted); [your_edittext].setSelection(formatted.length()); [your_edittext].addTextChangedListener(this); } }
Variante Kotlin:
private var current: String = "" override fun onTextChanged( s: CharSequence, start: Int, before: Int, count: Int ) { if (s.toString() != current) { discount_amount_edit_text.removeTextChangedListener(this) val cleanString: String = s.replace("""[$,.]""".toRegex(), "") val parsed = cleanString.toDouble() val formatted = NumberFormat.getCurrencyInstance().format((parsed / 100)) current = formatted discount_amount_edit_text.setText(formatted) discount_amount_edit_text.setSelection(formatted.length) discount_amount_edit_text.addTextChangedListener(this) } }
la source
String replaceable = String.format("[%s,.]", NumberFormat.getCurrencyInstance().getCurrency().getSymbol()); String cleanString = s.toString().replaceAll(replaceable, "");
String replaceable = String.format("[%s,.\\s]", NumberFormat.getCurrencyInstance().getCurrency().getSymbol());
onTextChanged() and rather to do so in
afterTextChanged () `double
est 10.10 / 100 = 0,1
vous ne pouvez pas le dépasser.Sur la base de certaines des réponses ci-dessus, j'ai créé un MoneyTextWatcher que vous utiliseriez comme suit:
priceEditText.addTextChangedListener(new MoneyTextWatcher(priceEditText));
et voici la classe:
public class MoneyTextWatcher implements TextWatcher { private final WeakReference<EditText> editTextWeakReference; public MoneyTextWatcher(EditText editText) { editTextWeakReference = new WeakReference<EditText>(editText); } @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { } @Override public void onTextChanged(CharSequence s, int start, int before, int count) { } @Override public void afterTextChanged(Editable editable) { EditText editText = editTextWeakReference.get(); if (editText == null) return; String s = editable.toString(); if (s.isEmpty()) return; editText.removeTextChangedListener(this); String cleanString = s.replaceAll("[$,.]", ""); BigDecimal parsed = new BigDecimal(cleanString).setScale(2, BigDecimal.ROUND_FLOOR).divide(new BigDecimal(100), BigDecimal.ROUND_FLOOR); String formatted = NumberFormat.getCurrencyInstance().format(parsed); editText.setText(formatted); editText.setSelection(formatted.length()); editText.addTextChangedListener(this); } }
la source
java.lang.NumberFormatException: Bad offset/length
.replaceAll("[^0-9]", "")
, celle ci-dessus a une limite de 9.999.999 -_-Voici ma coutume
CurrencyEditText
import android.content.Context;import android.graphics.Rect;import android.text.Editable;import android.text.InputFilter;import android.text.InputType;import android.text.TextWatcher; import android.util.AttributeSet;import android.widget.EditText;import java.math.BigDecimal;import java.math.RoundingMode; import java.text.DecimalFormat;import java.text.DecimalFormatSymbols; import java.util.Locale; /** * Some note <br/> * <li>Always use locale US instead of default to make DecimalFormat work well in all language</li> */ public class CurrencyEditText extends android.support.v7.widget.AppCompatEditText { private static String prefix = "VND "; private static final int MAX_LENGTH = 20; private static final int MAX_DECIMAL = 3; private CurrencyTextWatcher currencyTextWatcher = new CurrencyTextWatcher(this, prefix); public CurrencyEditText(Context context) { this(context, null); } public CurrencyEditText(Context context, AttributeSet attrs) { this(context, attrs, android.support.v7.appcompat.R.attr.editTextStyle); } public CurrencyEditText(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); this.setInputType(InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_FLAG_DECIMAL); this.setHint(prefix); this.setFilters(new InputFilter[] { new InputFilter.LengthFilter(MAX_LENGTH) }); } @Override protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) { super.onFocusChanged(focused, direction, previouslyFocusedRect); if (focused) { this.addTextChangedListener(currencyTextWatcher); } else { this.removeTextChangedListener(currencyTextWatcher); } handleCaseCurrencyEmpty(focused); } /** * When currency empty <br/> * + When focus EditText, set the default text = prefix (ex: VND) <br/> * + When EditText lose focus, set the default text = "", EditText will display hint (ex:VND) */ private void handleCaseCurrencyEmpty(boolean focused) { if (focused) { if (getText().toString().isEmpty()) { setText(prefix); } } else { if (getText().toString().equals(prefix)) { setText(""); } } } private static class CurrencyTextWatcher implements TextWatcher { private final EditText editText; private String previousCleanString; private String prefix; CurrencyTextWatcher(EditText editText, String prefix) { this.editText = editText; this.prefix = prefix; } @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { // do nothing } @Override public void onTextChanged(CharSequence s, int start, int before, int count) { // do nothing } @Override public void afterTextChanged(Editable editable) { String str = editable.toString(); if (str.length() < prefix.length()) { editText.setText(prefix); editText.setSelection(prefix.length()); return; } if (str.equals(prefix)) { return; } // cleanString this the string which not contain prefix and , String cleanString = str.replace(prefix, "").replaceAll("[,]", ""); // for prevent afterTextChanged recursive call if (cleanString.equals(previousCleanString) || cleanString.isEmpty()) { return; } previousCleanString = cleanString; String formattedString; if (cleanString.contains(".")) { formattedString = formatDecimal(cleanString); } else { formattedString = formatInteger(cleanString); } editText.removeTextChangedListener(this); // Remove listener editText.setText(formattedString); handleSelection(); editText.addTextChangedListener(this); // Add back the listener } private String formatInteger(String str) { BigDecimal parsed = new BigDecimal(str); DecimalFormat formatter = new DecimalFormat(prefix + "#,###", new DecimalFormatSymbols(Locale.US)); return formatter.format(parsed); } private String formatDecimal(String str) { if (str.equals(".")) { return prefix + "."; } BigDecimal parsed = new BigDecimal(str); // example pattern VND #,###.00 DecimalFormat formatter = new DecimalFormat(prefix + "#,###." + getDecimalPattern(str), new DecimalFormatSymbols(Locale.US)); formatter.setRoundingMode(RoundingMode.DOWN); return formatter.format(parsed); } /** * It will return suitable pattern for format decimal * For example: 10.2 -> return 0 | 10.23 -> return 00, | 10.235 -> return 000 */ private String getDecimalPattern(String str) { int decimalCount = str.length() - str.indexOf(".") - 1; StringBuilder decimalPattern = new StringBuilder(); for (int i = 0; i < decimalCount && i < MAX_DECIMAL; i++) { decimalPattern.append("0"); } return decimalPattern.toString(); } private void handleSelection() { if (editText.getText().length() <= MAX_LENGTH) { editText.setSelection(editText.getText().length()); } else { editText.setSelection(MAX_LENGTH); } } } }
Utilisez-le en XML comme
<...CurrencyEditText android:layout_width="match_parent" android:layout_height="wrap_content" />
Vous devez éditer 2 constantes ci-dessous pour convenir à votre projet
private static String prefix = "VND "; private static final int MAX_DECIMAL = 3;
Démo sur github
la source
formatter.setRoundingMode(RoundingMode.DOWN);
laformatDecimal
méthode.$.
, lorsque nous obtenons la valeur brute.
et analysons Double, cela donne NFE. Pour réparer, j'ai faitformatDecimal()
revenirprefix + "0.";
et changé#,###.
à l'#,##0.
intérieurformatDecimal()
. Cela semble également meilleur lorsque l'utilisateur n'entre que des décimales. Il montre comme$0.25
au lieu de$.25
.En fait, la solution fournie auparavant ne fonctionne pas. Cela ne fonctionne pas si vous voulez entrer 100,00.
Remplacer:
double parsed = Double.parseDouble(cleanString); String formato = NumberFormat.getCurrencyInstance().format((parsed/100));
Avec:
BigDecimal parsed = new BigDecimal(cleanString).setScale(2,BigDecimal.ROUND_FLOOR).divide(new BigDecimal(100),BigDecimal.ROUND_FLOOR); String formato = NumberFormat.getCurrencyInstance().format(parsed);
Je dois dire que j'ai apporté quelques modifications à mon code. Le fait est que vous devriez utiliser BigDecimal
la source
Je change la classe avec des outils TextWatcher pour utiliser les formats de devises brésiliennes et ajuster la position du curseur lors de l'édition de la valeur.
la source
J'ai construit sur la réponse de Guilhermes, mais je préserve la position du curseur et traite également les périodes différemment - de cette façon si un utilisateur tape après la période, cela n'affecte pas les nombres avant la période je trouve que cela donne une entrée très fluide .
[yourtextfield].addTextChangedListener(new TextWatcher() { NumberFormat currencyFormat = NumberFormat.getCurrencyInstance(); private String current = ""; @Override public void onTextChanged(CharSequence s, int start, int before, int count) { if(!s.toString().equals(current)) { [yourtextfield].removeTextChangedListener(this); int selection = [yourtextfield].getSelectionStart(); // We strip off the currency symbol String replaceable = String.format("[%s,\\s]", NumberFormat.getCurrencyInstance().getCurrency().getSymbol()); String cleanString = s.toString().replaceAll(replaceable, ""); double price; // Parse the string try { price = Double.parseDouble(cleanString); } catch(java.lang.NumberFormatException e) { price = 0; } // If we don't see a decimal, then the user must have deleted it. // In that case, the number must be divided by 100, otherwise 1 int shrink = 1; if(!(s.toString().contains("."))) { shrink = 100; } // Reformat the number String formated = currencyFormat.format((price / shrink)); current = formated; [yourtextfield].setText(formated); [yourtextfield].setSelection(Math.min(selection, [yourtextfield].getText().length())); [yourtextfield].addTextChangedListener(this); } } @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { } @Override public void afterTextChanged(Editable s) { } });
la source
Même s'il y a beaucoup de réponses ici, j'aimerais partager ce code que j'ai trouvé ici car je pense que c'est la réponse la plus robuste et la plus propre.
class CurrencyTextWatcher implements TextWatcher { boolean mEditing; public CurrencyTextWatcher() { mEditing = false; } public synchronized void afterTextChanged(Editable s) { if(!mEditing) { mEditing = true; String digits = s.toString().replaceAll("\\D", ""); NumberFormat nf = NumberFormat.getCurrencyInstance(); try{ String formatted = nf.format(Double.parseDouble(digits)/100); s.replace(0, s.length(), formatted); } catch (NumberFormatException nfe) { s.clear(); } mEditing = false; } } public void beforeTextChanged(CharSequence s, int start, int count, int after) { } public void onTextChanged(CharSequence s, int start, int before, int count) { } }
J'espère que cela aide.
la source
replaceAll("\\D", "")
supprimera tout ce qui n'est pas un chiffre, donc "100,00 $" et "10 000 $" deviennent tous deux "10000". Il semble que vous comptez sur l'entrée pour inclure les cents. Donc si c'est garanti, c'est parfait mais sinon je pense qu'il va y avoir des problèmes.Ok, voici une meilleure façon de gérer les formats de devises, effacer-en arrière. Le code est basé sur le code @androidcurious 'ci-dessus ... Mais, traite de certains problèmes liés à la suppression vers l'arrière et à quelques exceptions d'analyse: http://miguelt.blogspot.ca/2013/01/textwatcher-for-currency-masksformatting .html [UPDATE] La solution précédente avait quelques problèmes ... Ceci est une meilleure solution: http://miguelt.blogspot.ca/2013/02/update-textwatcher-for-currency.html Et ... voici le détails:
Cette approche est meilleure car elle utilise les mécanismes Android classiques. L'idée est de mettre en forme les valeurs une fois que l'utilisateur existe dans la vue.
Définissez un InputFilter pour restreindre les valeurs numériques - cela est nécessaire dans la plupart des cas car l'écran n'est pas assez grand pour accueillir de longues vues EditText. Cela peut être une classe interne statique ou juste une autre classe ordinaire:
/** Numeric range Filter. */ class NumericRangeFilter implements InputFilter { /** Maximum value. */ private final double maximum; /** Minimum value. */ private final double minimum; /** Creates a new filter between 0.00 and 999,999.99. */ NumericRangeFilter() { this(0.00, 999999.99); } /** Creates a new filter. * @param p_min Minimum value. * @param p_max Maximum value. */ NumericRangeFilter(double p_min, double p_max) { maximum = p_max; minimum = p_min; } @Override public CharSequence filter( CharSequence p_source, int p_start, int p_end, Spanned p_dest, int p_dstart, int p_dend ) { try { String v_valueStr = p_dest.toString().concat(p_source.toString()); double v_value = Double.parseDouble(v_valueStr); if (v_value<=maximum && v_value>=minimum) { // Returning null will make the EditText to accept more values. return null; } } catch (NumberFormatException p_ex) { // do nothing } // Value is out of range - return empty string. return ""; } }
Définissez une classe (statique interne ou juste une classe) qui implémentera View.OnFocusChangeListener. Notez que j'utilise une classe Utils - l'implémentation peut être trouvée dans "Amounts, Taxes".
/** Used to format the amount views. */ class AmountOnFocusChangeListener implements View.OnFocusChangeListener { @Override public void onFocusChange(View p_view, boolean p_hasFocus) { // This listener will be attached to any view containing amounts. EditText v_amountView = (EditText)p_view; if (p_hasFocus) { // v_value is using a currency mask - transfor over to cents. String v_value = v_amountView.getText().toString(); int v_cents = Utils.parseAmountToCents(v_value); // Now, format cents to an amount (without currency mask) v_value = Utils.formatCentsToAmount(v_cents); v_amountView.setText(v_value); // Select all so the user can overwrite the entire amount in one shot. v_amountView.selectAll(); } else { // v_value is not using a currency mask - transfor over to cents. String v_value = v_amountView.getText().toString(); int v_cents = Utils.parseAmountToCents(v_value); // Now, format cents to an amount (with currency mask) v_value = Utils.formatCentsToCurrency(v_cents); v_amountView.setText(v_value); } } }
Cette classe supprimera le format de devise lors de l'édition - en s'appuyant sur des mécanismes standard. Lorsque l'utilisateur quitte, le format de devise est réappliqué.
Il est préférable de définir certaines variables statiques pour minimiser le nombre d'instances:
static final InputFilter[] FILTERS = new InputFilter[] {new NumericRangeFilter()}; static final View.OnFocusChangeListener ON_FOCUS = new AmountOnFocusChangeListener();
Enfin, dans le onCreateView (...):
Vous pouvez réutiliser FILTERS et ON_FOCUS sur n'importe quel nombre de vues EditText.
Voici la classe Utils:
public class Utils { private static final NumberFormat FORMAT_CURRENCY = NumberFormat.getCurrencyInstance(); /** Parses an amount into cents. * @param p_value Amount formatted using the default currency. * @return Value as cents. */ public static int parseAmountToCents(String p_value) { try { Number v_value = FORMAT_CURRENCY.parse(p_value); BigDecimal v_bigDec = new BigDecimal(v_value.doubleValue()); v_bigDec = v_bigDec.setScale(2, BigDecimal.ROUND_HALF_UP); return v_bigDec.movePointRight(2).intValue(); } catch (ParseException p_ex) { try { // p_value doesn't have a currency format. BigDecimal v_bigDec = new BigDecimal(p_value); v_bigDec = v_bigDec.setScale(2, BigDecimal.ROUND_HALF_UP); return v_bigDec.movePointRight(2).intValue(); } catch (NumberFormatException p_ex1) { return -1; } } } /** Formats cents into a valid amount using the default currency. * @param p_value Value as cents * @return Amount formatted using a currency. */ public static String formatCentsToAmount(int p_value) { BigDecimal v_bigDec = new BigDecimal(p_value); v_bigDec = v_bigDec.setScale(2, BigDecimal.ROUND_HALF_UP); v_bigDec = v_bigDec.movePointLeft(2); String v_currency = FORMAT_CURRENCY.format(v_bigDec.doubleValue()); return v_currency.replace(FORMAT_CURRENCY.getCurrency().getSymbol(), "").replace(",", ""); } /** Formats cents into a valid amount using the default currency. * @param p_value Value as cents * @return Amount formatted using a currency. */ public static String formatCentsToCurrency(int p_value) { BigDecimal v_bigDec = new BigDecimal(p_value); v_bigDec = v_bigDec.setScale(2, BigDecimal.ROUND_HALF_UP); v_bigDec = v_bigDec.movePointLeft(2); return FORMAT_CURRENCY.format(v_bigDec.doubleValue()); } }
la source
J'ai utilisé l'implémentation référencée par Nathan Leigh et l'expression régulière suggérée par Kayvan N et user2582318 pour supprimer tous les caractères sauf les chiffres pour créer la version suivante:
fun EditText.addCurrencyFormatter() { // Reference: /programming/5107901/better-way-to-format-currency-input-edittext/29993290#29993290 this.addTextChangedListener(object: TextWatcher { private var current = "" override fun afterTextChanged(s: Editable?) { } override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) { } override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) { if (s.toString() != current) { this@addCurrencyFormatter.removeTextChangedListener(this) // strip off the currency symbol // Reference for this replace regex: /programming/5107901/better-way-to-format-currency-input-edittext/28005836#28005836 val cleanString = s.toString().replace("\\D".toRegex(), "") val parsed = if (cleanString.isBlank()) 0.0 else cleanString.toDouble() // format the double into a currency format val formated = NumberFormat.getCurrencyInstance() .format(parsed / 100) current = formated this@addCurrencyFormatter.setText(formated) this@addCurrencyFormatter.setSelection(formated.length) this@addCurrencyFormatter.addTextChangedListener(this) } } }) }
Il s'agit d'une fonction d'extension dans Kotlin qui ajoute le TextWatcher au TextChangedListener du EditText.
Pour l'utiliser, il suffit de:
J'espère que cela aide.
la source
J'ai obtenu ceci d' ici et l' ai changé pour se conformer au format de devise portugais.
import java.text.NumberFormat; import java.util.Currency; import java.util.Locale; import android.text.Editable; import android.text.TextWatcher; import android.widget.EditText; public class CurrencyTextWatcher implements TextWatcher { private String current = ""; private int index; private boolean deletingDecimalPoint; private final EditText currency; public CurrencyTextWatcher(EditText p_currency) { currency = p_currency; } @Override public void beforeTextChanged(CharSequence p_s, int p_start, int p_count, int p_after) { if (p_after>0) { index = p_s.length() - p_start; } else { index = p_s.length() - p_start - 1; } if (p_count>0 && p_s.charAt(p_start)==',') { deletingDecimalPoint = true; } else { deletingDecimalPoint = false; } } @Override public void onTextChanged(CharSequence s, int start, int before, int count) { } @Override public void afterTextChanged(Editable p_s) { if(!p_s.toString().equals(current)){ currency.removeTextChangedListener(this); if (deletingDecimalPoint) { p_s.delete(p_s.length()-index-1, p_s.length()-index); } // Currency char may be retrieved from NumberFormat.getCurrencyInstance() String v_text = p_s.toString().replace("€","").replace(",", ""); v_text = v_text.replaceAll("\\s", ""); double v_value = 0; if (v_text!=null && v_text.length()>0) { v_value = Double.parseDouble(v_text); } // Currency instance may be retrieved from a static member. NumberFormat numberFormat = NumberFormat.getCurrencyInstance(new Locale("pt", "PT")); String v_formattedValue = numberFormat.format((v_value/100)); current = v_formattedValue; currency.setText(v_formattedValue); if (index>v_formattedValue.length()) { currency.setSelection(v_formattedValue.length()); } else { currency.setSelection(v_formattedValue.length()-index); } // include here anything you may want to do after the formatting is completed. currency.addTextChangedListener(this); } } }
Le layout.xml
<EditText android:id="@+id/edit_text_your_id" ... android:text="0,00 €" android:inputType="numberDecimal" android:digits="0123456789" />
Faites-le fonctionner
yourEditText = (EditText) findViewById(R.id.edit_text_your_id); yourEditText.setRawInputType(Configuration.KEYBOARD_12KEY); yourEditText.addTextChangedListener(new CurrencyTextWatcher(yourEditText));
la source
Pour moi, ça a marché comme ça
public void onTextChanged(CharSequence s, int start, int before, int count) { if(!s.toString().matches("^\\$(\\d{1,3}(\\,\\d{3})*|(\\d+))(\\.\\d{2})?$")) { String userInput= ""+s.toString().replaceAll("[^\\d]", ""); if (userInput.length() > 2) { Float in=Float.parseFloat(userInput); price = Math.round(in); // just to get an Integer //float percen = in/100; String first, last; first = userInput.substring(0, userInput.length()-2); last = userInput.substring(userInput.length()-2); edEx1.setText("$"+first+"."+last); Log.e(MainActivity.class.toString(), "first: "+first + " last:"+last); edEx1.setSelection(edEx1.getText().length()); } } }
la source
Il est préférable d'utiliser l'interface InputFilter. Il est beaucoup plus facile de gérer n'importe quel type d'entrées en utilisant regex. Ma solution pour le format de saisie des devises:
public class CurrencyFormatInputFilter implements InputFilter { Pattern mPattern = Pattern.compile("(0|[1-9]+[0-9]*)(\\.[0-9]{1,2})?"); @Override public CharSequence filter( CharSequence source, int start, int end, Spanned dest, int dstart, int dend) { String result = dest.subSequence(0, dstart) + source.toString() + dest.subSequence(dend, dest.length()); Matcher matcher = mPattern.matcher(result); if (!matcher.matches()) return dest.subSequence(dstart, dend); return null; } }
Valide: 0,00, 0,0, 10,00, 111,1
Invalide: 0, 0,000, 111, 10, 010,00, 01,0
Comment utiliser:
editText.setFilters(new InputFilter[] {new CurrencyFormatInputFilter()});
la source
Je l'ai utilisé pour permettre à l'utilisateur d'entrer la devise et de la convertir de chaîne en int pour stocker dans db et de revenir à nouveau d'int en chaîne
https://github.com/nleigh/Restaurant/blob/master/Restaurant/src/uk/co/nathanleigh/restaurant/CurrencyFormat.java
la source
Si votre champ de devise json est de type numérique (et non de chaîne), il peut être 3.1, 3.15 ou simplement 3. Parce que json arrondit automatiquement les champs numériques.
Dans ce cas, vous devrez peut-être l'arrondir pour un affichage correct (et pour pouvoir utiliser un masque sur un champ de saisie plus tard):
NumberFormat nf = NumberFormat.getCurrencyInstance(); float value = 200 // it can be 200, 200.3 or 200.37, BigDecimal will take care BigDecimal valueAsBD = BigDecimal.valueOf(value); valueAsBD.setScale(2, BigDecimal.ROUND_HALF_UP); String formated = nf.format(valueAsBD);
Pourquoi est-ce nécessaire?
Toutes les réponses pointent vers la suppression des symboles monétaires lors de la saisie, jugeant que vous recevez les cents et ainsi formater dolar + cents / 100 = dolar, cents. Mais si votre champ de devise json est un type de nombre (et non une chaîne), il arrondira vos centimes, il peut être 3, 3,1 ou 3,15.
la source
come as 3.1 , 3.15 or just 3. Because json automatically round number fields
- cela n'a rien de commun avec l' arrondi !une autre approche, mais basée sur la réponse de Guilherme . Cette approche est utile lorsque les paramètres régionaux de votre pays ne sont pas disponibles ou si vous souhaitez utiliser des symboles monétaires personnalisés. Cette implémentation est uniquement pour les positifs non décimaux.
ce code est en Kotlin, premier délégué
setMaskingMoney
pourEditText
fun EditText.setMaskingMoney(currencyText: String) { this.addTextChangedListener(object: MyTextWatcher{ val editTextWeakReference: WeakReference<EditText> = WeakReference<EditText>(this@setMaskingMoney) override fun afterTextChanged(editable: Editable?) { val editText = editTextWeakReference.get() ?: return val s = editable.toString() editText.removeTextChangedListener(this) val cleanString = s.replace("[Rp,. ]".toRegex(), "") val newval = currencyText + cleanString.monetize() editText.setText(newval) editText.setSelection(newval.length) editText.addTextChangedListener(this) } }) }
Ensuite, l'
MyTextWatcher
interface doit être étendue à partir deTextWatcher
. Comme nous n'avons besoin que deafterTextChanged
méthode, les autres méthodes doivent être remplacées dans cette interfaceinterface MyTextWatcher: TextWatcher { override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {} override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {} }
et les méthodes de monétisation sont:
fun String.monetize(): String = if (this.isEmpty()) "0" else DecimalFormat("#,###").format(this.replace("[^\\d]".toRegex(),"").toLong())
Implémentations complètes:
fun EditText.setMaskingMoney(currencyText: String) { this.addTextChangedListener(object: MyTextWatcher{ val editTextWeakReference: WeakReference<EditText> = WeakReference<EditText>(this@setMaskingMoney) override fun afterTextChanged(editable: Editable?) { val editText = editTextWeakReference.get() ?: return val s = editable.toString() editText.removeTextChangedListener(this) val cleanString = s.replace("[Rp,. ]".toRegex(), "") val newval = currencyText + cleanString.monetize() editText.setText(newval) editText.setSelection(newval.length) editText.addTextChangedListener(this) } }) } interface MyTextWatcher: TextWatcher { override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {} override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {} } fun String.monetize(): String = if (this.isEmpty()) "0" else DecimalFormat("#,###").format(this.replace("[^\\d]".toRegex(),"").toLong())
et quelque part sur la méthode onCreate:
yourTextView.setMaskingMoney("Rp. ")
la source
Après trop de recherche et échoue avec les doubles, les BigDecimals et ainsi de suite, j'ai créé ce code. Cela fonctionne plug and play. C'est à kotlin. Alors, pour aider les autres coincés comme moi, allons-y.
Le code est essentiellement une fonction qui placera un textWatcher et ajustera la virgule au bon endroit.
Tout d'abord, créez cette fonction:
fun CurrencyWatcher( editText:EditText) { editText.addTextChangedListener(object : TextWatcher { //this will prevent the loop var changed: Boolean = false override fun afterTextChanged(p0: Editable?) { changed = false } override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) { editText.setSelection(p0.toString().length) } @SuppressLint("SetTextI18n") override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) { if (!changed) { changed = true var str: String = p0.toString().replace(",", "").trim() var element0: String = str.elementAt(0).toString() var element1: String = "x" var element2: String = "x" var element3: String = "x" var element4: String = "x" var element5: String = "x" var element6: String = "x" //this variables will store each elements of the initials data for the case we need to move this numbers like: 0,01 to 0,11 or 0,11 to 0,01 if (str.length >= 2) { element1 = str.elementAt(1).toString() } if (str.length >= 3) { element2 = str.elementAt(2).toString() } editText.removeTextChangedListener(this) //this first block of code will take care of the case //where the number starts with 0 and needs to adjusta the 0 and the "," place if (str.length == 1) { str = "0,0" + str editText.setText(str) } else if (str.length <= 3 && str == "00") { str = "0,00" editText.setText(str) editText.setSelection(str.length) } else if (element0 == "0" && element1 == "0" && element2 == "0") { str = str.replace("000", "") str = "0,0" + str editText.setText(str) } else if (element0 == "0" && element1 == "0" && element2 != "0") { str = str.replace("00", "") str = "0," + str editText.setText(str) } else { //This block of code works with the cases that we need to move the "," only because the value is bigger //lets get the others elements if (str.length >= 4) { element3 = str.elementAt(3).toString() } if (str.length >= 5) { element4 = str.elementAt(4).toString() } if (str.length >= 6) { element5 = str.elementAt(5).toString() } if (str.length == 7) { element6 = str.elementAt(6).toString() } if (str.length >= 4 && element0 != "0") { val sb: StringBuilder = StringBuilder(str) //set the coma in right place sb.insert(str.length - 2, ",") str = sb.toString() } //change the 0,11 to 1,11 if (str.length == 4 && element0 == "0") { val sb: StringBuilder = StringBuilder(str) //takes the initial 0 out sb.deleteCharAt(0); str = sb.toString() val sb2: StringBuilder = StringBuilder(str) sb2.insert(str.length - 2, ",") str = sb2.toString() } //this will came up when its like 11,11 and the user delete one, so it will be now 1,11 if (str.length == 3 && element0 != "0") { val sb: StringBuilder = StringBuilder(str) sb.insert(str.length - 2, ",") str = sb.toString() } //came up when its like 0,11 and the user delete one, output will be 0,01 if (str.length == 2 && element0 == "0") { val sb: StringBuilder = StringBuilder(str) //takes 0 out sb.deleteCharAt(0); str = sb.toString() str = "0,0" + str } //came up when its 1,11 and the user delete, output will be 0,11 if (str.length == 2 && element0 != "0") { val sb: StringBuilder = StringBuilder(str) //retira o 0 da frente sb.insert(0, "0,") str = sb.toString() } editText.setText(str) } //places the selector at the end to increment the number editText.setSelection(str.length) editText.addTextChangedListener(this) } } }) }
Et puis vous appelez cette fonction de cette façon
val etVal:EditText = findViewById(R.id.etValue) CurrencyWatcher(etVal)
la source
Après avoir examiné la plupart des postes StackOverflow sur les différentes façons d' y parvenir à l' aide d' un
TextWatcher
,InputFilter
ou d'une bibliothèque comme CurrencyEditText je l' ai réglé sur cette solution simple en utilisant unOnFocusChangeListener
.La logique consiste à analyser le
EditText
en un nombre lorsqu'il est focalisé et à le formater lorsqu'il perd le focus.amount.setOnFocusChangeListener(new View.OnFocusChangeListener() { @Override public void onFocusChange(View view, boolean hasFocus) { Number numberAmount = 0f; try { numberAmount = Float.valueOf(amount.getText().toString()); } catch (NumberFormatException e1) { e1.printStackTrace(); try { numberAmount = NumberFormat.getCurrencyInstance().parse(amount.getText().toString()); } catch (ParseException e2) { e2.printStackTrace(); } } if (hasFocus) { amount.setText(numberAmount.toString()); } else { amount.setText(NumberFormat.getCurrencyInstance().format(numberAmount)); } } });
la source
J'ai implémenté une version Kotlin + Rx.
C'est pour la monnaie brésilienne (par exemple 1,500,00 - 5,21 - 192,90) mais vous pouvez facilement vous adapter à d'autres formats.
J'espère que quelqu'un d'autre le trouvera utile.
RxTextView .textChangeEvents(fuel_price) // Observe text event changes .filter { it.text().isNotEmpty() } // do not accept empty text when event first fires .flatMap { val onlyNumbers = Regex("\\d+").findAll(it.text()).fold(""){ acc:String,it:MatchResult -> acc.plus(it.value)} Observable.just(onlyNumbers) } .distinctUntilChanged() .map { it.trimStart('0') } .map { when (it.length) { 1-> "00"+it 2-> "0"+it else -> it } } .subscribe { val digitList = it.reversed().mapIndexed { i, c -> if ( i == 2 ) "${c}," else if ( i < 2 ) c else if ( (i-2)%3==0 ) "${c}." else c } val currency = digitList.reversed().fold(""){ acc,it -> acc.toString().plus(it) } fuel_price.text = SpannableStringBuilder(currency) fuel_price.setSelection(currency.length) }
la source
CurrencyTextWatcher.java
public class CurrencyTextWatcher implements TextWatcher { private final static String DS = "."; //Decimal Separator private final static String TS = ","; //Thousands Separator private final static String NUMBERS = "0123456789"; //Numbers private final static int MAX_LENGTH = 13; //Maximum Length private String format; private DecimalFormat decimalFormat; private EditText editText; public CurrencyTextWatcher(EditText editText) { String pattern = "###" + TS + "###" + DS + "##"; decimalFormat = new DecimalFormat(pattern); this.editText = editText; this.editText.setInputType(InputType.TYPE_CLASS_NUMBER); this.editText.setKeyListener(DigitsKeyListener.getInstance(NUMBERS + DS)); this.editText.setFilters(new InputFilter[]{new InputFilter.LengthFilter(MAX_LENGTH)}); } @Override public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) { } @Override public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) { } @Override public void afterTextChanged(Editable editable) { editText.removeTextChangedListener(this); String value = editable.toString(); if (!value.isEmpty()) { value = value.replace(TS, ""); try { format = decimalFormat.format(Double.parseDouble(value)); format = format.replace("0", ""); } catch (Exception e) { System.out.println(e.getMessage()); } editText.setText(format); } editText.addTextChangedListener(this); } }
EditTextCurrency.java
public class EditTextCurrency extends AppCompatEditText { public EditTextCurrency(Context context) { super(context); } public EditTextCurrency(Context context, AttributeSet attrs) { super(context, attrs); addTextChangedListener(new CurrencyTextWatcher(this)); } }
la source
Voici comment j'ai pu afficher une devise dans un EditText qui était facile à mettre en œuvre et fonctionne bien pour l'utilisateur sans le potentiel de symboles fous partout. Cela n'essayera pas d'effectuer de mise en forme tant que le EditText n'aura plus le focus. L'utilisateur peut toujours revenir en arrière et apporter des modifications sans compromettre le formatage. J'utilise la variable «formattedPrice» pour l'affichage uniquement, et la variable «itemPrice» comme valeur que je stocke / utilise pour les calculs.
Cela semble très bien fonctionner, mais je ne suis dans ce domaine que depuis quelques semaines, donc toute critique constructive est absolument la bienvenue!
La vue EditText dans le xml a l'attribut suivant:
android:inputType="numberDecimal"
Variables globales:
private String formattedPrice; private int itemPrice = 0;
Dans la méthode onCreate:
EditText itemPriceInput = findViewById(R.id.item_field_price); itemPriceInput.setOnFocusChangeListener(new View.OnFocusChangeListener() { @Override public void onFocusChange(View v, boolean hasFocus) { String priceString = itemPriceInput.getText().toString(); if (! priceString.equals("")) { itemPrice = Double.parseDouble(priceString.replaceAll("[$,]", "")); formattedPrice = NumberFormat.getCurrencyInstance().format(itemPrice); itemPriceInput.setText(formattedPrice); } } });
la source
Au cas où quelqu'un serait intéressé par un moyen de le faire en utilisant RxBinding et Kotlin:
var isEditing = false RxTextView.textChanges(dollarValue) .filter { !isEditing } .filter { it.isNotBlank() } .map { it.toString().filter { it.isDigit() } } .map { BigDecimal(it).setScale(2, BigDecimal.ROUND_FLOOR).divide(100.toBigDecimal(), BigDecimal.ROUND_FLOOR) } .map { NumberFormat.getCurrencyInstance(Locale("pt", "BR")).format(it) } .subscribe { isEditing = true dollarValue.text = SpannableStringBuilder(it) dollarValue.setSelection(it.length) isEditing = false }
la source
juste un commentaire supplémentaire à la réponse approuvée. Vous pouvez obtenir un plantage lors du déplacement du curseur sur le champ edittext en raison de l'analyse. J'ai fait une instruction try catch, mais implémentez votre propre code.
@Override public void onTextChanged(CharSequence s, int start, int before, int count) { if(!s.toString().equals(current)){ amountEditText.removeTextChangedListener(this); String cleanString = s.toString().replaceAll("[$,.]", ""); try{ double parsed = Double.parseDouble(cleanString); String formatted = NumberFormat.getCurrencyInstance().format((parsed/100)); current = formatted; amountEditText.setText(formatted); amountEditText.setSelection(formatted.length()); } catch (Exception e) { } amountEditText.addTextChangedListener(this); } }
la source
vous pouvez utiliser ces méthodes
import android.text.Editable import android.text.TextWatcher import android.widget.EditText import android.widget.TextView import java.text.NumberFormat import java.util.* fun TextView.currencyFormat() { addTextChangedListener(object : TextWatcher { override fun afterTextChanged(s: Editable?) {} override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {} override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) { removeTextChangedListener(this) text = if (s?.toString().isNullOrBlank()) { "" } else { s.toString().currencyFormat() } if(this@currencyFormat is EditText){ setSelection(text.toString().length) } addTextChangedListener(this) } }) } fun String.currencyFormat(): String { var current = this if (current.isEmpty()) current = "0" return try { if (current.contains('.')) { NumberFormat.getNumberInstance(Locale.getDefault()).format(current.replace(",", "").toDouble()) } else { NumberFormat.getNumberInstance(Locale.getDefault()).format(current.replace(",", "").toLong()) } } catch (e: Exception) { "0" } }
la source
Version Kotlin :
var current = "" editText.addTextChangedListener(object: TextWatcher { override fun afterTextChanged(s: Editable?) {} override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {} override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) { val stringText = s.toString() if(stringText != current) { editText.removeTextChangedListener(this) val locale: Locale = Locale.UK val currency = Currency.getInstance(locale) val cleanString = stringText.replace("[${currency.symbol},.]".toRegex(), "") val parsed = cleanString.toDouble() val formatted = NumberFormat.getCurrencyInstance(locale).format(parsed / 100) current = formatted editText.setText(formatted) editText.setSelection(formatted.length) editText.addTextChangedListener(this) } } })
la source
public class MoneyEditText extends android.support.v7.widget.AppCompatEditText{ public MoneyEditText(Context context) { super(context); addTextChangedListener(MoneySplitter()); } public MoneyEditText(Context context, AttributeSet attrs) { super(context, attrs); addTextChangedListener(MoneySplitter()); } public MoneyEditText(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); addTextChangedListener(MoneySplitter()); } public TextWatcher MoneySplitter() { TextWatcher textWatcher = new TextWatcher() { @Override public void onTextChanged(CharSequence s, int start, int before, int count) { try { removeTextChangedListener(this); String value = s.toString(); if (!value.equals("")) { if(!TextUtils.isEmpty(value)) setText(formatPrice(Double.parseDouble(value))); setSelection(getText().toString().length()); } addTextChangedListener(this); } catch (Exception ex) { ex.printStackTrace(); addTextChangedListener(this); } } @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { } @Override public void afterTextChanged(Editable s) { } }; return textWatcher; } public static String formatPrice(double value){ int DecimalPointNumber = 2; Locale locale = Locale.getDefault(); DecimalFormat myFormatter = (DecimalFormat) NumberFormat.getCurrencyInstance(locale); StringBuilder sb = new StringBuilder(); if(DecimalPointNumber>0){ for (int i = 0; i < DecimalPointNumber; i++) { sb.append("#"); } myFormatter.applyPattern("###,###."+ sb.toString()); }else myFormatter.applyPattern("###,###"+ sb.toString()); return Currency.getInstance(Locale.getDefault()).getSymbol() + myFormatter.format(value); } }
puis utilisez ce bloc comme votre editText
<MoneyEditText android:id="@+id/txtPrice" android:layout_width="match_parent" android:layout_height="64dp" android:digits="0123456789.," android:inputType="numberDecimal" android:selectAllOnFocus="true" android:singleLine="true" />
la source
C'est comme la réponse de Saeid Mohammadi mais j'ai changé pour accepter les nombres négatifs.
package com.example.liberdade.util import android.text.Editable import android.text.TextWatcher import android.widget.EditText import java.lang.ref.WeakReference import java.math.BigDecimal import java.text.NumberFormat import java.util.* class MoneyTextWatcher : TextWatcher { private val editTextWeakReference: WeakReference<EditText?>? private val locale: Locale = Locale("pt", "BR") //private final Locale locale; constructor(editText: EditText?, locale: Locale?) { editTextWeakReference = WeakReference<EditText?>(editText) //this.locale = if (locale != null) locale else Locale.getDefault() } constructor(editText: EditText?) { editTextWeakReference = WeakReference<EditText?>(editText) //locale = Locale.getDefault() } 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(editable: Editable?) { val editText: EditText = editTextWeakReference?.get() ?: return editText.removeTextChangedListener(this) var isNegative = false var editableString = editable.toString() if (editable != null) { if (editableString.contains('-')) { isNegative = true if (editable != null) { editableString = editableString.replace("-","") } } } val parsed: BigDecimal? = parseToBigDecimal(editableString, locale) //val parsed: BigDecimal? = parseToBigDecimal(editable.toString(), locale) var formatted: String = NumberFormat.getCurrencyInstance(locale).format(parsed) if (isNegative && !(formatted.equals("R\$ 0,00") || formatted.equals("-R\$ 0,00"))) formatted = "-${formatted}" editText.setText(formatted) editText.setSelection(formatted.length) editText.addTextChangedListener(this) } private fun parseToBigDecimal(value: String?, locale: Locale?): BigDecimal? { val replaceable = java.lang.String.format( "[%s,.\\s]", NumberFormat.getCurrencyInstance(locale).currency.symbol ) val cleanString = value!!.replace(replaceable.toRegex(), "") return BigDecimal(cleanString).setScale( 2, BigDecimal.ROUND_FLOOR ).divide( BigDecimal(100), BigDecimal.ROUND_FLOOR ) } } //como invocar //binding.editTextValorCaixa.addTextChangedListener(MoneyTextWatcher(binding.editTextValorCaixa, Locale("pt", "BR")))
la source