104 votes

Meilleure façon de formater la saisie de devise dans un editText ?

J'ai un editText, la valeur de départ est de 0,00 $. Lorsque vous appuyez sur 1, cela change à 0,01 $. Appuyez sur 4, cela passe à 0,14 $. Appuyez sur 8, 1,48 $. Appuyez sur la touche de suppression, 0,14 $, etc.

Cela fonctionne, le problème est que si quelqu'un positionne manuellement le curseur, des problèmes surviennent dans le formatage. S'ils suppriment le point décimal, il ne reviendra pas. S'ils placent le curseur devant le point décimal et saisissent 2, il affichera 02,00 $ au lieu de 2,00 $. S'ils essaient de supprimer le $, cela supprimera un chiffre à la place, par exemple.

Voici le code que j'utilise, je serais reconnaissant pour toutes 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());
                }
            }
        }
    });

173voto

Mythos Points 1

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)){
       [votre_edittext].removeTextChangedListener(this);

       String cleanString = s.toString().replaceAll("[$,.]", "");

       double parsed = Double.parseDouble(cleanString);
       String formatted = NumberFormat.getCurrencyInstance().format((parsed/100));

       current = formatted;
       [votre_edittext].setText(formatted);
       [votre_edittext].setSelection(formatted.length());

       [votre_edittext].addTextChangedListener(this);
    }
}

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

36voto

ToddH Points 1134

Basé sur 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 editTextWeakReference;

    public MoneyTextWatcher(EditText editText) {
        editTextWeakReference = new WeakReference(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);
    }
}

27voto

Phan Van Linh Points 16963

Voici mon CurrencyEditText personnalisé

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;

/**
 * Une note 
 * Utilisez toujours la locale US au lieu de la valeur par défaut pour que DecimalFormat fonctionne bien dans toutes les langues
 */
public class CurrencyEditText extends android.support.v7.widget.AppCompatEditText {
    private static String préfixe = "VND ";
    private static final int LONGUEUR_MAX = 20;
    private static final int MAX_DECIMAL = 3;
    private CurrencyTextWatcher currencyTextWatcher = new CurrencyTextWatcher(this, préfixe);

    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(préfixe);
        this.setFilters(new InputFilter[] { new InputFilter.LengthFilter(LONGUEUR_MAX) });
    }

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

    /**
     * Lorsque la devise est vide 
     * + Lorsque EditText est en focus, définissez le texte par défaut = préfixe (ex : VND) 
     * + Lorsque EditText perd le focus, définissez le texte par défaut = "", EditText affichera le hint (ex : VND)
     */
    private void handleCaseCurrencyEmpty(boolean focused) {
        if (focused) {
            if (getText().toString().isEmpty()) {
                setText(préfixe);
            }
        } else {
            if (getText().toString().equals(préfixe)) {
                setText("");
            }
        }
    }

    private static class CurrencyTextWatcher implements TextWatcher {
        private final EditText editText;
        private String previousCleanString;
        private String préfixe;

        CurrencyTextWatcher(EditText editText, String préfixe) {
            this.editText = editText;
            this.préfixe = préfixe;
        }

        @Override
        public void beforeTextChanged(CharSequence s, int start, int count, int after) {
            // ne rien faire
        }

        @Override
        public void onTextChanged(CharSequence s, int start, int before, int count) {
            // ne rien faire
        }

        @Override
        public void afterTextChanged(Editable editable) {
            String str = editable.toString();
            if (str.length() < préfixe.length()) {
                editText.setText(préfixe);
                editText.setSelection(préfixe.length());
                return;
            }
            if (str.equals(préfixe)) {
                return;
            }
            // cleanString est la chaîne qui ne contient pas de préfixe et de virgules
            String cleanString = str.replace(préfixe, "").replaceAll("[,]", "");
            // pour éviter l'appel récursif de afterTextChanged
            if (cleanString.equals(previousCleanString) || cleanString.isEmpty()) {
                return;
            }
            previousCleanString = cleanString;

            String formattedString;
            if (cleanString.contains(".")) {
                formattedString = formatDecimal(cleanString);
            } else {
                formattedString = formatInteger(cleanString);
            }
            editText.removeTextChangedListener(this); // Supprimer le listener
            editText.setText(formattedString);
            handleSelection();
            editText.addTextChangedListener(this); // Ajouter à nouveau le listener
        }

        private String formatInteger(String str) {
            BigDecimal parsed = new BigDecimal(str);
            DecimalFormat formatter =
                    new DecimalFormat(préfixe + "#,###", new DecimalFormatSymbols(Locale.US));
            return formatter.format(parsed);
        }

        private String formatDecimal(String str) {
            if (str.equals(".")) {
                return préfixe + ".";
            }
            BigDecimal parsed = new BigDecimal(str);
            // modèle exemple VND #,###.00
            DecimalFormat formatter = new DecimalFormat(préfixe + "#,###." + getDecimalPattern(str),
                    new DecimalFormatSymbols(Locale.US));
            formatter.setRoundingMode(RoundingMode.DOWN);
            return formatter.format(parsed);
        }

        /**
         * Cela renverra un motif approprié pour formater le décimal
         * Par exemple: 10.2 -> retourne 0 | 10.23 -> retourne 00, | 10.235 -> retourne 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() <= LONGUEUR_MAX) {
                editText.setSelection(editText.getText().length());
            } else {
                editText.setSelection(LONGUEUR_MAX);
            }
        }
    }
}

Utilisez-le dans XML comme

 <...CurrencyEditText
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        />

Vous devriez modifier les 2 constantes ci-dessous pour convenir à votre projet

private static String préfixe = "VND ";
private static final int MAX_DECIMAL = 3;

description de l'image

Démo sur github

14voto

sfratini Points 1814

En fait, la solution fournie précédemment ne fonctionne pas. Cela ne fonctionne pas si vous voulez entrer 100.00.

Remplacez :

double parsed = Double.parseDouble(cleanString);
String formato = NumberFormat.getCurrencyInstance().format((parsed/100));

Par :

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. La chose est que vous devriez utiliser BigDecimal

8voto

Henrique Ho Points 81

Je change la classe avec implémente TextWatcher pour utiliser les formats de devises du Brésil et ajuster la position du curseur lors de l'édition de la valeur.

public class MoneyTextWatcher implements TextWatcher {

    private EditText editText;

    private String lastAmount = "";

    private int lastCursorPosition = -1;

    public MoneyTextWatcher(EditText editText) {
        super();
        this.editText = editText;
    }

    @Override
    public void onTextChanged(CharSequence amount, int start, int before, int count) {

        if (!amount.toString().equals(lastAmount)) {

            String cleanString = clearCurrencyToNumber(amount.toString());

            try {

                String formattedAmount = transformToCurrency(cleanString);
                editText.removeTextChangedListener(this);
                editText.setText(formattedAmount);
                editText.setSelection(formattedAmount.length());
                editText.addTextChangedListener(this);

                if (lastCursorPosition != lastAmount.length() && lastCursorPosition != -1) {
                    int lengthDelta = formattedAmount.length() - lastAmount.length();
                    int newCursorOffset = max(0, min(formattedAmount.length(), lastCursorPosition + lengthDelta));
                    editText.setSelection(newCursorOffset);
                }
            } catch (Exception e) {
               //log something
            }
        }
    }

    @Override
    public void afterTextChanged(Editable s) {
    }

    @Override
    public void beforeTextChanged(CharSequence s, int start, int count, int after) {
        String value = s.toString();
        if(!value.equals("")){
            String cleanString = clearCurrencyToNumber(value);
            String formattedAmount = transformToCurrency(cleanString);
            lastAmount = formattedAmount;
            lastCursorPosition = editText.getSelectionStart();
        }
    }

    public static String clearCurrencyToNumber(String currencyValue) {
        String result = null;

        if (currencyValue == null) {
            result = "";
        } else {
            result = currencyValue.replaceAll("\[(a-z)|(A-Z)|($,. )\]", "");
        }
        return result;
    }

    public static boolean isCurrencyValue(String currencyValue, boolean podeSerZero) {
        boolean result;

        if (currencyValue == null || currencyValue.length() == 0) {
            result = false;
        } else {
            if (!podeSerZero && currencyValue.equals("0,00")) {
                result = false;
            } else {
                result = true;
            }
        }
        return result;
    }

    public static String transformToCurrency(String value) {
        double parsed = Double.parseDouble(value);
        String formatted = NumberFormat.getCurrencyInstance(new Locale("pt", "BR")).format((parsed / 100));
        formatted = formatted.replaceAll("\[^(0-9)(.,)\]", "");
        return formatted;
    }
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X