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

1voto

Ha Yi Points 77

Une autre approche, mais basée sur la réponse de Guilherme. Cette approche est utile lorsque la locale de votre pays n'est pas disponible ou si vous souhaitez utiliser des symboles de devise personnalisés. Cette implémentation est uniquement pour des nombres entiers positifs.

ce code est en Kotlin, tout d'abord, déléguer setMaskingMoney pour EditText

fun EditText.setMaskingMoney(currencyText: String) {
    this.addTextChangedListener(object: MyTextWatcher{
        val editTextWeakReference: WeakReference = WeakReference(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'interface MyTextWatcher devrait être étendue à partir de TextWatcher. Puisque nous avons seulement besoin de la méthode afterTextChanged, les autres méthodes doivent être remplacées dans cette interface.

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

et la méthode monetize est:

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 = WeakReference(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 dans la méthode onCreate:

votreTextView.setMaskingMoney("Rp. ")

1voto

Thiago Silva Points 51

Après trop de recherches et d'échecs avec les Doubles, BigDecimals et ainsi de suite, j'ai fait ce code. Il fonctionne plug And Play. Son en kotlin. Donc, pour aider les autres bloqués comme moi, allons-y.

Le code est essentiellement une fonction qui placera un textWatcher et ajustera la virgule à la bonne place.

Tout d'abord, créez cette fonction :

fun CurrencyWatcher( editText:EditText) {

    editText.addTextChangedListener(object : TextWatcher {
        //cela empêchera la boucle
        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"

                //ces variables stockeront chaque élément des données initiales dans le cas où nous devons déplacer ces nombres comme : 0,01 à 0,11 ou 0,11 à 0,01
                if (str.length >= 2) {
                    element1 = str.elementAt(1).toString()
                }
                if (str.length >= 3) {
                    element2 = str.elementAt(2).toString()
                }

                editText.removeTextChangedListener(this)

                //ce premier bloc de code s'occupera du cas
                //où le nombre commence par 0 et doit ajuster le 0 et la virgule
                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 {

                    //Ce bloc de code fonctionne avec les cas où nous devons déplacer la virgule uniquement car la valeur est plus grande
                    //obtenons les autres éléments
                    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)
                        //placez la virgule à la bonne place
                        sb.insert(str.length - 2, ",")
                        str = sb.toString()
                    }

                    //change le 0,11 en 1,11
                    if (str.length == 4 && element0 == "0") {

                        val sb: StringBuilder = StringBuilder(str)
                        //enlève le 0 initial
                        sb.deleteCharAt(0);
                        str = sb.toString()

                        val sb2: StringBuilder = StringBuilder(str)
                        sb2.insert(str.length - 2, ",")
                        str = sb2.toString()
                    }

                    //cela se produira quand c'est comme 11,11 et que l'utilisateur en supprime un, donc ce sera maintenant 1,11
                    if (str.length == 3 && element0 != "0") {
                        val sb: StringBuilder = StringBuilder(str)
                        sb.insert(str.length - 2, ",")
                        str = sb.toString()
                    }

                    //se produit lorsque c'est comme 0,11 et que l'utilisateur en supprime un, la sortie sera 0,01
                    if (str.length == 2 && element0 == "0") {
                        val sb: StringBuilder = StringBuilder(str)
                        //prend 0
                        sb.deleteCharAt(0);
                        str = sb.toString()

                        str = "0,0" + str

                    }

                    //se produit lorsque c'est 1,11 et que l'utilisateur supprime, la sortie sera 0,11
                    if (str.length == 2 && element0 != "0") {
                        val sb: StringBuilder = StringBuilder(str)
                        //enlève le 0 initial
                        sb.insert(0, "0,")
                        str = sb.toString()

                    }

                    editText.setText(str)
                }

                //place le sélecteur à la fin pour augmenter le nombre
                editText.setSelection(str.length)
                editText.addTextChangedListener(this)
            }

        }
    })
}

Ensuite, vous appelez cette fonction de cette manière

val etVal:EditText = findViewById(R.id.etValue)

CurrencyWatcher(etVal)

0voto

Vinicius Lima Points 891

J'ai mis en place une version Kotlin + Rx.

C'est pour la monnaie brésilienne (par exemple 1 500,00 - 5,21 - 192,90) mais vous pouvez facilement l'adapter pour d'autres formats.

J'espère que quelqu'un d'autre trouvera cela utile.

RxTextView
            .textChangeEvents(fuel_price) // Observer les changements d'événements de texte
            .filter { it.text().isNotEmpty() } // ne pas accepter le texte vide lorsque l'événement se déclenche
            .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)
            }

0voto

Kat Points 1

Voici comment j'ai pu afficher une devise dans un EditText de façon facile à implémenter et bien fonctionnelle pour l'utilisateur, sans risque de voir des symboles étranges apparaître partout. Cela ne tentera pas de formater tant que l'EditText a le focus. L'utilisateur peut toujours revenir en arrière et apporter des modifications sans compromettre le formatage. J'utilise la variable 'formattedPrice' uniquement pour l'affichage, et la variable 'itemPrice' comme valeur que je stocke/utilise pour les calculs.

Ça semble vraiment bien fonctionner, mais je ne fais ça que depuis quelques semaines, donc toute critique constructive est la bienvenue !

La vue EditText dans le fichier 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);
        }
    }
});

0voto

Guilherme Silva Points 160

Si quelqu'un est intéressé par une façon 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
            }

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