Meilleure façon de formater l'entrée de devise editText?

91

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());
                }
            }
        }
    });
Roger
la source
1
Excusez mon ignorance, mais cet extrait de code provient-il de l'une des méthodes de cycle de vie de l'activité ou est-il dans une classe personnalisée que vous avez créée? Pouvez-vous fournir un exemple de code plus complet s'il vous plaît? Merci!
Argus 9
Cela fonctionne pour moi, j'ai essayé cette lib externe android-arsenal.com/details/1/5374
pravin maske

Réponses:

153

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)
            }
        }
Guilherme Oliveira
la source
36
Il vaudrait peut-être mieux faire ce qui suit plutôt que d'assumer le symbole du dollar: String replaceable = String.format("[%s,.]", NumberFormat.getCurrencyInstance().getCurrency().getSymbol()); String cleanString = s.toString().replaceAll(replaceable, "");
craigp
6
Hmm, j'ai essayé cela moi-même maintenant, le modèle regex de replaceAll devrait ressembler à ceci, pour gérer également les espaces: String replaceable = String.format("[%s,.\\s]", NumberFormat.getCurrencyInstance().getCurrency().getSymbol());
craigp
6
N'est-il pas recommandé de ne pas apporter de modifications dans onTextChanged() and rather to do so in afterTextChanged () `
codinguser
3
Je suis intéressé de savoir pourquoi l'écouteur de texte modifié est supprimé puis ré-ajouté à chaque fois? Pour moi, cela fonctionne si seulement ajouté une fois (et j'ai déplacé les modifications vers afterTextChanged)
Daniel Wilson
5
Je ne fonctionne pas lorsque vous mettez 1 -> 0 -> 0 pour obtenir 1,00. C'est parce que vous arrivez au point où 0,1 est changé en chaîne 010 et 010 en doubleest 10. 10 / 100 = 0,1vous ne pouvez pas le dépasser.
JakubW
30

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);
    }
}
ToddH
la source
J'utilise ceci depuis un certain temps maintenant mais j'ai récemment trouvé un petit problème, si vous maintenez le bouton de suppression sur certains claviers, cela supprime tout le mot / groupe de texte et les causesjava.lang.NumberFormatException: Bad offset/length
BluGeni
1
Cela a parfaitement fonctionné pour moi! Attention à 'editText.setSelection (formatted.length ());' doit être observé à l'instance de propriété 'maxLength' de EditText en question. maxLength == 13; formaté.longueur () == 14; Si «formatted.length» est supérieur à «maxLength», l'erreur suivante se produit: IndexOutOfBoundsException: setSpan (14 ... 14) termine la longueur au-delà de 13 tks
GFPF
1
@BluGeni pour corriger cela, ajoutez simplement une vérification s.isEmpty avant de supprimer l'écouteur de changement de texte if (s.isEmpty ()) return; editText.removeTextChangedListener (this); Également dans la ligne cleanString, s.toString () est redondant
Mike Baglio Jr.
1
la meilleure réponse qu'une seule suggestion est de changer .replaceAll ("[$ ...) pour -> .replaceAll (" [^ \\ d.] "," "); comme dans une autre devise, vous avez d'autres caractères que le $, comme dans mon cas était le R $ (brésilien)
user2582318
1
désolé, la suggestion correcte est celle-ci -> .replaceAll("[^0-9]", ""), celle ci-dessus a une limite de 9.999.999 -_-
user2582318
21

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;

entrez la description de l'image ici

Démo sur github

Phan Van Linh
la source
2
C'est génial!
YTerle
1
J'ai trouvé qu'après avoir tapé pour atteindre le nombre maximum de décimales, essayer de saisir un nombre 5-9 augmentera la dernière décimale de 1 ... c'est arrondi! Ma solution était d'appeler formatter.setRoundingMode(RoundingMode.DOWN);la formatDecimalméthode.
BW
@bwicks merci beaucoup d'avoir trouvé le problème. J'ai approuvé votre montage
Phan Van Linh
comment mettre le symbole de devise instade de VND ??
Mayur Karmur
1
Une autre idée d'amélioration: si l'utilisateur entre $., lorsque nous obtenons la valeur brute .et analysons Double, cela donne NFE. Pour réparer, j'ai fait formatDecimal()revenir prefix + "0.";et changé #,###.à l' #,##0.intérieur formatDecimal(). Cela semble également meilleur lorsque l'utilisateur n'entre que des décimales. Il montre comme $0.25au lieu de $.25.
Gokhan Arik
13

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

sfratini
la source
6

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 classe publique MoneyTextWatcher implémente TextWatcher {

    privé EditText editText;

    chaîne privée lastAmount = "";

    private int lastCursorPosition = -1;

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

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

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

            Chaîne cleanString = clearCurrencyToNumber (amount.toString ());

            essayez {

                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) {
               // enregistrer quelque chose
            }
        }
    }

    @Passer outre
    public void afterTextChanged (Modifiables) {
    }

    @Passer outre
    public void beforeTextChanged (CharSequence s, int start, int count, int after) {
        Valeur de chaîne = s.toString ();
        if (! value.equals ("")) {
            String cleanString = clearCurrencyToNumber (valeur);
            String formattedAmount = transformToCurrency (cleanString);
            lastAmount = formattedAmount;
            lastCursorPosition = editText.getSelectionStart ();
        }
    }

    public static String clearCurrencyToNumber (String currencyValue) {
        Résultat de la chaîne = null;

        if (currencyValue == null) {
            résultat = "";
        } autre {
            result = currencyValue.replaceAll ("[(az) | (AZ) | ($ ,.)]", "");
        }
        résultat de retour;
    }

    public static boolean isCurrencyValue (String currencyValue, boolean podeSerZero) {
        résultat booléen;

        if (currencyValue == null || currencyValue.length () == 0) {
            résultat = faux;
        } autre {
            if (! podeSerZero && currencyValue.equals ("0,00")) {
                résultat = faux;
            } autre {
                résultat = vrai;
            }
        }
        résultat de retour;
    }

    public static String transformToCurrency (String value) {
        double analysé = Double.parseDouble (valeur);
        Chaîne formatée = NumberFormat.getCurrencyInstance (nouvelle locale ("pt", "BR")). Format ((parsed / 100));
        formaté = formaté.replaceAll ("[^ (0-9) (.,)]", "");
        retour formaté;
    }
}
Henrique Ho
la source
Dans cette ligne "int newCursorOffset = max (0, min (formattedAmount.length (), lastCursorPosition + lengthDelta));" quel genre d'objet est max et min?
Arthur Melo
2
@ArthurMelo Its, Math.max, Math.min Merci le code, et cela ressemble à un échec lors de la suppression de la virgule de l'edittext.
Marcos Vasconcelos
4

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)
        {
        }
    });
genixpro
la source
ça m'aide beaucoup. Merci @genixpro
Harin Kaklotar
j'aime votre idée, mais elle semble plus fluide si vous enregistrez le nombre de chiffres après le curseur, puis setSelection (longueur - après).
Alpha Huang
Très intéressant! L'utilisation de remplaçable fonctionnait sur mon appareil physique, mais cela ne fonctionnait pas sur l'émulateur.
Aliton Oliveira
4

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.

Kayvan N
la source
Cela ne supprimerait-il pas la virgule décimale? Vous ne pourrez donc pas faire la différence entre 100,00 $ et 10 000 $ - à moins que je ne manque quelque chose.
nasch
2
c'est la réponse parfaite! travaillé pour moi. mon, pensez juste au temps que j'ai passé sur ces réponses et j'ai finalement fait défiler vers le bas et j'ai trouvé celle que je veux.
Ge Rong
Je suis content que cela vous ait aidé.
Kayvan N
@nasch Ceci est un TextWatcher et il formate le texte en tant que types d'utilisateurs, ce qui empêche le cas que vous mentionnez.
Kayvan N
@KayvanN Je sais ce qu'est un TextWatcher. 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.
nasch
4

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 (...):

   EditText mAmountView = ....
   mAmountView.setFilters(FILTERS);
   mAmountView.setOnFocusChangeListener(ON_FOCUS);

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

}
Miguelt
la source
Bien que cela puisse théoriquement répondre à la question, nous aimerions que vous incluiez les parties essentielles de l'article lié dans votre réponse et que vous fournissiez le lien pour référence . Si vous ne le faites pas, la réponse est menacée par la pourriture des liens.
Kev
J'obtiens java.lang.NumberFormatException: Double non valide: "12 345,00 $" lorsque le texte modifié perd le focus. Comment le réparer.
Madhan le
4

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:

yourEditText = (EditText) findViewById(R.id.edit_text_your_id);
yourEditText.addCurrencyFormatter()

J'espère que cela aide.

Francisco Junior
la source
3

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));
Nuno Monteiro
la source
2

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());
                    }
                }
            }
Fernando
la source
2

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()});
Mussa
la source
1

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.

s'affaisse
la source
1
Exactement ce dont j'avais besoin. Merci!
Erick Engelhardt
come as 3.1 , 3.15 or just 3. Because json automatically round number fields- cela n'a rien de commun avec l' arrondi !
Marcin Orlowski
1

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é setMaskingMoneypourEditText

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' MyTextWatcherinterface doit être étendue à partir de TextWatcher. Comme nous n'avons besoin que de afterTextChangedméthode, 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 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. ")
Hayi Nukman
la source
1

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)
Thiago Silva
la source
0

Après avoir examiné la plupart des postes StackOverflow sur les différentes façons d' y parvenir à l' aide d' un TextWatcher, InputFilterou d'une bibliothèque comme CurrencyEditText je l' ai réglé sur cette solution simple en utilisant un OnFocusChangeListener.

La logique consiste à analyser le EditTexten 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));
            }
        }
    });
Abtin Gramian
la source
0

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)
            }
Vinicius Lima
la source
0

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

entrez la description de l'image ici

Samet ÖZTOPRAK
la source
0

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);
        }
    }
});
Kat
la source
0

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
            }
Guilherme V.
la source
0

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);
        }
    }
Andrew Trang
la source
0

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"
    }
}
Kourosh
la source
0

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)
            }
        }
    })
Adriatik Gashi
la source
0
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" />
Saeid Mohammadi
la source
Vous pouvez utiliser ce texte d'édition personnalisé pour mettre en forme le texte d'entrée comme vous le souhaitez.
Saeid Mohammadi
J'ai changé cette classe pour accepter les nombres négatifs. Le code est ci-dessous comme réponse.
Michel Fernandes
0

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")))
Michel Fernandes
la source