Lepszy sposób formatowania danych wejściowych waluty editText?

91

Mam editText, wartość początkowa to 0,00 USD. Po naciśnięciu 1 zmienia się na 0,01 USD. Naciśnij 4, dojdzie do 0,14 $. Naciśnij 8, 1,48 USD. Naciśnij Backspace, 0,14 USD itp.

To działa, problem polega na tym, że jeśli ktoś ręcznie ustawia kursor, pojawiają się problemy z formatowaniem. Gdyby usunęli ułamek dziesiętny, nie wróci. Jeśli ustawią kursor przed cyfrą dziesiętną i wpiszą 2, wyświetli 02,00 USD zamiast 2,00 USD. Jeśli spróbują usunąć $, spowoduje to na przykład usunięcie cyfry.

Oto kod, którego używam, byłbym wdzięczny za wszelkie sugestie.

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());
                }
            }
        }
    });
zrozumiałem
źródło
1
Przepraszam za moją ignorancję, ale czy ten fragment kodu pochodzi z jednej z metod cyklu życia działania, czy też znajduje się w niestandardowej klasie, którą utworzyłeś? Czy możesz podać bardziej kompletny kod? Dzięki!
Argus9
To działa dla mnie Wypróbowałem tę zewnętrzną bibliotekę android-arsenal.com/details/1/5374
pravin maske

Odpowiedzi:

153

Przetestowałem twoją metodę, ale zawodzi, gdy używam wielkich liczb ... Stworzyłem to:

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

Wariant 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
źródło
36
Lepiej byłoby zrobić co następuje niż zakładać symbol dolara: String replaceable = String.format("[%s,.]", NumberFormat.getCurrencyInstance().getCurrency().getSymbol()); String cleanString = s.toString().replaceAll(replaceable, "");
craigp
6
Hmm, właściwie próbowałem to sobie teraz, wzór regex z replaceAll powinien wyglądać tak, aby przestrzenie uchwyt, a także: String replaceable = String.format("[%s,.\\s]", NumberFormat.getCurrencyInstance().getCurrency().getSymbol());
craigp
6
Czy nie jest zalecane, aby nie wprowadzać zmian w onTextChanged() and rather to do so in afterTextChanged () `
codinguser
3
Interesuje mnie, dlaczego odbiornik zmiany tekstu jest usuwany, a następnie ponownie dodawany za każdym razem? U mnie to działa, jeśli tylko dodane raz (i przeniosłem zmiany na afterTextChanged)
Daniel Wilson
5
Nie działa, gdy wstawisz 1 -> 0 -> 0, aby uzyskać 1,00. Dzieje się tak, ponieważ dochodzisz do punktu, w którym 0,1 jest zamieniane na ciąg 010, a 010 na doubleto 10. 10 / 100 = 0,1Nie możesz go przekroczyć.
JakubW
30

Na podstawie niektórych z powyższych odpowiedzi utworzyłem MoneyTextWatcher, którego użyjesz w następujący sposób:

priceEditText.addTextChangedListener(new MoneyTextWatcher(priceEditText));

a oto klasa:

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
źródło
Używam tego już od jakiegoś czasu, ale ostatnio znalazłem mały problem, jeśli przytrzymasz przycisk usuwania na niektórych klawiaturach, usuwa całe słowo / grupę tekstu i przyczynyjava.lang.NumberFormatException: Bad offset/length
BluGeni
1
U mnie zadziałało idealnie! Uwaga na 'editText.setSelection (formatted.length ());' należy przestrzegać w przypadku wystąpienia właściwości „maxLength” danego elementu EditText. maxLength == 13; formatted.length () == 14; Jeśli parametr „formatted.length” jest większy niż „maxLength”, pojawia się następujący błąd: IndexOutOfBoundsException: setSpan (14 ... 14) kończy długość powyżej 13 tks
GFPF
1
@BluGeni, aby to naprawić, po prostu dodaj sprawdzenie s.isEmpty przed usunięciem odbiornika zmiany tekstu if (s.isEmpty ()) return; editText.removeTextChangedListener (this); Również w linii cleanString s.toString () jest zbędne
Mike Baglio Jr.
1
najlepszą odpowiedzią niż tylko jedną sugestią jest zmiana .replaceAll ("[$ ...) na -> .replaceAll (" [^ \\ d.] "," "); ponieważ jestem w innej walucie, masz inne znaki niż tylko $, tak jak w moim przypadku był R $ (brazylijski)
user2582318
1
przepraszam, poprawna sugestia to ta -> .replaceAll("[^0-9]", ""), ta powyżej ma limit 9.999.999 -_-
user2582318
21

Oto mój zwyczaj 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);
            }
        }
    }
}

Użyj go w XML jak

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

Powinieneś edytować 2 stałe poniżej, aby były odpowiednie dla twojego projektu

private static String prefix = "VND ";
private static final int MAX_DECIMAL = 3;

wprowadź opis obrazu tutaj

Demo na githubie

Phan Van Linh
źródło
2
To jest genialne!
YTerle
1
Zauważyłem, że po wpisaniu maksymalnej liczby miejsc po przecinku, próba wpisania liczby 5-9 zwiększy ostatnie miejsce po przecinku o 1 ... to zaokrągla w górę! Mój dylemat było zadzwonić formatter.setRoundingMode(RoundingMode.DOWN);w formatDecimalmetodzie.
BW
@bwicks bardzo dziękuję za znalezienie problemu. Zatwierdziłem twoją zmianę
Phan Van Linh
jak umieścić symbol waluty zamiast VND?
Mayur Karmur,
1
Inny pomysł na ulepszenie: jeśli użytkownik wejdzie $., gdy otrzymamy surową wartość as .i parsujemy do Double, daje to NFE. Aby to naprawić, formatDecimal()powróciłem prefix + "0.";i zmieniłem #,###.do #,##0.środka formatDecimal(). Wygląda to również lepiej, gdy użytkownik wprowadza tylko miejsca dziesiętne. Pokazuje się jako $0.25zamiast $.25.
Gokhan Arik
13

W rzeczywistości podane wcześniej rozwiązanie nie działa. Nie działa, jeśli chcesz wprowadzić 100,00.

Zastąpić:

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

Z:

BigDecimal parsed = new BigDecimal(cleanString).setScale(2,BigDecimal.ROUND_FLOOR).divide(new BigDecimal(100),BigDecimal.ROUND_FLOOR);                
String formato = NumberFormat.getCurrencyInstance().format(parsed);

Muszę powiedzieć, że dokonałem pewnych modyfikacji w moim kodzie. Chodzi o to, że powinieneś używać BigDecimal

sfratini
źródło
6

Zmieniam klasę za pomocą narzędzi TextWatcher, aby używać formatów walut Brazylii i dostosowywać pozycję kursora podczas edycji wartości.

public class MoneyTextWatcher implementuje TextWatcher {

    prywatny EditText editText;

    private String lastAmount = "";

    private int lastCursorPosition = -1;

    public MoneyTextWatcher (EditText editText) {
        Wspaniały();
        this.editText = editText;
    }

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

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

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

            próbować {

                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 (wyjątek e) {
               // zarejestruj coś
            }
        }
    }

    @Nadpisanie
    public void afterTextChanged (Edytowalne s) {
    }

    @Nadpisanie
    public void beforeTextChanged (CharSequence s, int start, int count, int after) {
        Wartość ciągu = s.toString ();
        if (! value.equals ("")) {
            String cleanString = clearCurrencyToNumber (wartość);
            String formattedAmount = transformToCurrency (cleanString);
            lastAmount = formattedAmount;
            lastCursorPosition = editText.getSelectionStart ();
        }
    }

    public static String clearCurrencyToNumber (String currencyValue) {
        Wynik w postaci ciągu = null;

        if (currencyValue == null) {
            wynik = "";
        } else {
            wynik = currencyValue.replaceAll ("[(az) | (AZ) | ($ ,.)]", "");
        }
        wynik zwrotu;
    }

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

        if (currencyValue == null || currencyValue.length () == 0) {
            wynik = fałsz;
        } else {
            if (! podeSerZero && currencyValue.equals ("0,00")) {
                wynik = fałsz;
            } else {
                wynik = prawda;
            }
        }
        wynik zwrotu;
    }

    public static String transformToCurrency (wartość ciągu) {
        double parsed = Double.parseDouble (wartość);
        String formatted = NumberFormat.getCurrencyInstance (new Locale ("pt", "BR")). Format ((parsed / 100));
        formatted = formatted.replaceAll ("[^ (0-9) (.,)]", "");
        powrót sformatowany;
    }
}
Henrique Ho
źródło
W tym wierszu "int newCursorOffset = max (0, min (formattedAmount.length (), lastCursorPosition + lengthDelta));" jaki rodzaj obiektu to max i min?
Arthur Melo
2
@ArthurMelo Its, Math.max, Math.min Dzięki kodowi i wygląda na to, że nie udało się usunąć przecinka z pliku edittext.
Marcos Vasconcelos
4

Oparłem się na odpowiedzi Guilhermesa, ale zachowuję pozycję kursora, a także traktuję inaczej kropki - w ten sposób, jeśli użytkownik pisze po kropce, nie wpływa to na liczby przed kropką, uważam, że daje to bardzo płynne wejście .

    [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
źródło
to mi bardzo pomaga. Dziękuję @genixpro
Harin Kaklotar
Podoba mi się twój pomysł, ale wygląda to płynniej, jeśli zapiszesz liczbę cyfr za kursorem, a następnie setSelection (długość - po).
Alpha Huang
Bardzo interesujące! Używanie wymiennego działało na moim fizycznym urządzeniu, ale nie działa na emulatorze.
Aliton Oliveira
4

Mimo że odpowiedzi jest wiele, chciałbym udostępnić ten kod, który tutaj znalazłem , ponieważ uważam, że jest to najbardziej solidna i czysta odpowiedź.

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

}

mam nadzieję, że to pomoże.

Kayvan N
źródło
Czy to nie usunie przecinka dziesiętnego? Więc wtedy nie byłbyś w stanie określić różnicy między 100,00 a 10 000 USD - chyba że czegoś brakuje.
nasch
2
to doskonała odpowiedź! pracował dla mnie. mój, pomyśl tylko, ile czasu poświęciłem na te odpowiedzi, aż w końcu przewinąłem do dołu i znalazłem tę, której szukam.
Ge Rong
Cieszę się, że ci pomogło.
Kayvan N
@nasch To jest TextWatcher i formatuje tekst jako typy użytkownika, co zapobiega wspominanej sprawie.
Kayvan N
@KayvanN Wiem, co to jest TextWatcher. replaceAll("\\D", "")usunie wszystko, co nie jest cyfrą, więc „100,00 USD” i „10 000 USD” zmienią się na „10000”. Wygląda na to, że liczysz na to, że wkład zawiera centy. Więc jeśli to jest gwarantowane, świetnie, ale jeśli nie, myślę, że będą problemy.
nasch
4

Ok, tutaj jest lepszy sposób radzenia sobie z formatami walut, naciśnięcie klawisza do tyłu. Kod jest oparty na powyższym kodzie @androidcurious ... Ale radzi sobie z niektórymi problemami związanymi z usuwaniem wstecznym i niektórymi wyjątkami analizy: http://miguelt.blogspot.ca/2013/01/textwatcher-for-currency-masksformatting .html [AKTUALIZACJA] Poprzednie rozwiązanie miało pewne problemy ... To jest lepsze rozwiązanie: http://miguelt.blogspot.ca/2013/02/update-textwatcher-for-currency.html A oto Detale:

To podejście jest lepsze, ponieważ wykorzystuje konwencjonalne mechanizmy Androida. Chodzi o to, aby sformatować wartości po tym, jak użytkownik istnieje w widoku.

Zdefiniuj InputFilter, aby ograniczyć wartości liczbowe - jest to wymagane w większości przypadków, ponieważ ekran nie jest wystarczająco duży, aby pomieścić długie widoki EditText. Może to być statyczna klasa wewnętrzna lub po prostu inna zwykła klasa:

/** 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 "";
    }
}

Zdefiniuj klasę (wewnętrzną statyczną lub po prostu klasę), która będzie implementować View.OnFocusChangeListener. Zwróć uwagę, że używam klasy Utils - implementację można znaleźć w sekcji „Kwoty, podatki”.

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

Ta klasa usunie format waluty podczas edycji - opierając się na standardowych mechanizmach. Po wyjściu użytkownika format waluty jest ponownie stosowany.

Lepiej jest zdefiniować kilka zmiennych statycznych, aby zminimalizować liczbę instancji:

   static final InputFilter[] FILTERS = new InputFilter[] {new NumericRangeFilter()};
   static final View.OnFocusChangeListener ON_FOCUS = new AmountOnFocusChangeListener();

Wreszcie w onCreateView (...):

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

Możesz ponownie użyć FILTERS i ON_FOCUS w dowolnej liczbie widoków EditText.

Oto klasa 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
źródło
Chociaż może to teoretycznie odpowiadać na pytanie, chcielibyśmy, aby w odpowiedzi zawarli Państwo istotne części połączonego artykułu i podali link do odniesienia . W przeciwnym razie odpowiedź jest zagrożona gniciem linków.
Kev,
Otrzymuję java.lang.NumberFormatException: Nieprawidłowe podwójne: „12 345,00 $”, gdy tekst edycji traci fokus. Jak to naprawić.
Madhan
4

Użyłem implementacji, do której odwołał się Nathan Leigh oraz sugerowanego wyrażenia regularnego Kayvana N i użytkownika2582318, aby usunąć wszystkie znaki oprócz cyfr, aby utworzyć następującą wersję:

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

}

Jest to funkcja rozszerzająca w Kotlinie, która dodaje TextWatcher do TextChangedListener w EditText.

Aby z niego skorzystać, wystarczy:

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

Mam nadzieję, że to pomoże.

Francisco Junior
źródło
3

Mam to stąd i zmieniłem, aby było zgodne z portugalskim formatem waluty.

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

}

Plik layout.xml

<EditText
    android:id="@+id/edit_text_your_id"
    ...
    android:text="0,00 €"
    android:inputType="numberDecimal"
    android:digits="0123456789" />

Zabierz to do pracy

    yourEditText = (EditText) findViewById(R.id.edit_text_your_id);
    yourEditText.setRawInputType(Configuration.KEYBOARD_12KEY);
    yourEditText.addTextChangedListener(new CurrencyTextWatcher(yourEditText));
Nuno Monteiro
źródło
2

U mnie tak to działało

 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
źródło
2

Lepiej jest używać interfejsu InputFilter. O wiele łatwiej jest obsługiwać wszelkiego rodzaju dane wejściowe za pomocą wyrażenia regularnego. Moje rozwiązanie dla formatu wprowadzania waluty:

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

Prawidłowe: 0,00, 0,0, 10,00, 111,1
Nieprawidłowe: 0, 0,000, 111, 10, 010,00, 01,0

Jak używać:

editText.setFilters(new InputFilter[] {new CurrencyFormatInputFilter()});
Mussa
źródło
1

Jeśli twoje pole waluty json jest typu liczbowego (a nie typu String), może mieć postać 3,1, 3,15 lub tylko 3. Ponieważ json automatycznie zaokrągla pola liczbowe.

W takim przypadku może być konieczne zaokrąglenie go w celu prawidłowego wyświetlania (i późniejszego wykorzystania maski w polu wprowadzania):

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

Dlaczego to jest potrzebne?

Wszystkie odpowiedzi wskazują na usunięcie symboli waluty podczas pisania, oceniając, że otrzymujesz centy, tworząc w ten sposób dolar + centy / 100 = dolar, centy. Ale jeśli twoje pole waluty json jest typem liczbowym (a nie ciągiem), zaokrągli twoje centy, może to być 3, 3,1 lub 3,15.

zwisy
źródło
1
Dokładnie to, czego potrzebowałem. Dzięki!
Erick Engelhardt
come as 3.1 , 3.15 or just 3. Because json automatically round number fields- to nie ma nic wspólnego z zaokrąglaniem !
Marcin Orłowski
1

inne podejście, ale oparte na odpowiedzi Guilherme . Takie podejście jest przydatne, gdy ustawienia regionalne Twojego kraju nie są dostępne lub jeśli chcesz użyć niestandardowych symboli walut. Ta implementacja dotyczy tylko liczb dodatnich niedziesiętnych.

ten kod jest w Kotlin, pierwszy delegat setMaskingMoneydlaEditText

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

Następnie MyTextWatcherinterfejs powinien zostać rozszerzony z TextWatcher. Ponieważ potrzebujemy tylko afterTextChangedmetody, inne metody muszą zostać przesłonięte w tym interfejsie

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

a metody zarabiania to:

fun String.monetize(): String = if (this.isEmpty()) "0"
    else DecimalFormat("#,###").format(this.replace("[^\\d]".toRegex(),"").toLong())

Pełne realizacje:

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

i gdzieś w metodzie onCreate:

yourTextView.setMaskingMoney("Rp. ")
Hayi Nukman
źródło
1

Po zbyt wielu poszukiwaniach i niepowodzeniach z Double, BigDecimals i tak dalej, stworzyłem ten kod. Działa na zasadzie plug and play. Jest w Kotlinie. Więc, żeby pomóc innym utknąć jak ja, chodźmy.

Kod jest w zasadzie funkcją, która umieści textWatcher i dostosuje przecinek we właściwym miejscu.

Najpierw utwórz tę funkcję:

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

        }
    })
}

A następnie nazywasz tę funkcję w ten sposób

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

CurrencyWatcher(etVal)
Thiago Silva
źródło
0

Po patrząc na większość stanowisk stackoverflow na różne sposoby, aby to osiągnąć za pomocą TextWatcher, InputFilterlub biblioteka jak CurrencyEditText Mam osiadł na to proste rozwiązanie stosując OnFocusChangeListener.

Logika polega na przeanalizowaniu EditTextliczby, gdy jest skupiona, i sformatowaniu jej z powrotem, gdy straci fokus.

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
źródło
0

Zaimplementowałem wersję Kotlin + Rx.

Dotyczy waluty brazylijskiej (np. 1500,00 - 5,21 - 192,90), ale można ją łatwo dostosować do innych formatów.

Mam nadzieję, że ktoś inny uzna to za pomocne.

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
źródło
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));
    }
}

wprowadź opis obrazu tutaj

Samet ÖZTOPRAK
źródło
0

Oto, w jaki sposób mogłem wyświetlić walutę w EditText, który był łatwy do zaimplementowania i działa dobrze dla użytkownika bez potencjału szalonych symboli w każdym miejscu. To nie będzie próbowało wykonać żadnego formatowania, dopóki EditText nie będzie już fokusem. Użytkownik nadal może wrócić i wprowadzić zmiany bez narażania formatowania. Używam zmiennej „formattedPrice” tylko do wyświetlania, a zmiennej „itemPrice” jako wartości, którą przechowuję / używam do obliczeń.

Wygląda na to, że działa naprawdę dobrze, ale zajmuję się tym dopiero od kilku tygodni, więc jakakolwiek konstruktywna krytyka jest mile widziana!

Widok EditText w XML ma następujący atrybut:

android:inputType="numberDecimal"

Zmienne globalne:

private String formattedPrice;
private int itemPrice = 0;

W metodzie 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
źródło
0

W przypadku, gdy ktoś jest zainteresowany sposobem zrobienia tego za pomocą RxBinding i 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.
źródło
0

tylko dodatkowy komentarz do zatwierdzonej odpowiedzi. Może wystąpić awaria podczas przesuwania kursora w polu edittext z powodu parsowania. Wykonałem instrukcję try catch, ale zaimplementuj swój własny kod.

@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
źródło
0

możesz użyć tych metod

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
źródło
0

Wersja 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
źródło
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);
    }
}

a następnie użyj tego bloku jako swojego 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
źródło
Możesz użyć tego dostosowanego tekstu edycji do formatowania tekstu wejściowego, jak chcesz.
Saeid Mohammadi
Zmieniłem tę klasę, aby akceptować liczby ujemne. Kod jest poniżej jako odpowiedź.
Michel Fernandes
0

To jest jak odpowiedź Saeid Mohammadi, ale zmieniłem, aby zaakceptować liczby ujemne.

  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
źródło