Besserer Weg zum Formatieren der Währungseingabe editText?

88

Ich habe einen editText, der Startwert ist $ 0.00. Wenn Sie 1 drücken, ändert sich der Wert auf 0,01 USD. Drücken Sie 4, es geht auf 0,14 $. Drücken Sie 8, 1,48 $. Drücken Sie die Rücktaste, 0,14 USD usw.

Das funktioniert, das Problem ist, wenn jemand den Cursor manuell positioniert, treten Probleme bei der Formatierung auf. Wenn sie die Dezimalstelle löschen, wird sie nicht zurückgegeben. Wenn sie den Cursor vor die Dezimalstelle setzen und 2 eingeben, wird $ 02.00 anstelle von $ 2.00 angezeigt. Wenn sie versuchen, das $ zu löschen, wird stattdessen beispielsweise eine Ziffer gelöscht.

Hier ist Code, den ich verwende. Ich würde mich über Vorschläge freuen.

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
quelle
1
Entschuldigen Sie meine Unwissenheit, aber ist dieser Code-Ausschnitt aus einer der Methoden des Aktivitätslebenszyklus oder gehören sie zu einer benutzerdefinierten Klasse, die Sie erstellt haben? Können Sie bitte ein vollständigeres Codebeispiel bereitstellen? Vielen Dank!
Argus9
Dies funktioniert für mich Ich habe versucht, diese externe lib android-arsenal.com/details/1/5374
Pravin Maske

Antworten:

148

Ich habe Ihre Methode getestet, aber sie schlägt fehl, wenn ich große Zahlen verwende ... Ich habe Folgendes erstellt:

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);
    }
}
Guilherme Oliveira
quelle
34
Könnte besser sein, das Folgende zu tun, als das String replaceable = String.format("[%s,.]", NumberFormat.getCurrencyInstance().getCurrency().getSymbol()); String cleanString = s.toString().replaceAll(replaceable, "");
Dollarsymbol
6
Hmm, habe das jetzt tatsächlich selbst versucht, das Regex-Muster von replaceAll sollte so aussehen, um auch Leerzeichen zu handhaben: String replaceable = String.format("[%s,.\\s]", NumberFormat.getCurrencyInstance().getCurrency().getSymbol());
craigp
6
Ist es nicht empfehlenswert, keine Änderungen in onTextChanged() and rather to do so in afterTextChanged () `
codinguser
3
Ich bin interessiert zu wissen, warum der vom Text geänderte Listener entfernt und jedes Mal neu hinzugefügt wird. Für mich funktioniert es, wenn es nur einmal hinzugefügt wird (und ich habe die Änderungen auf afterTextChanged verschoben)
Daniel Wilson
5
Ich arbeite nicht, wenn Sie 1 -> 0 -> 0 setzen, um 1,00 zu erhalten. Das liegt daran, dass Sie an den Punkt gelangen, an dem 0,1 in 010-Zeichenfolge und 010 in double10 geändert wird. 10 / 100 = 0,1Sie können nicht daran vorbei.
JakubW
28

Basierend auf einigen der obigen Antworten habe ich einen MoneyTextWatcher erstellt, den Sie wie folgt verwenden würden:

priceEditText.addTextChangedListener(new MoneyTextWatcher(priceEditText));

und hier ist die Klasse:

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
quelle
Ich benutze dies jetzt schon eine Weile, habe aber kürzlich ein kleines Problem festgestellt. Wenn Sie die Löschtaste auf einigen Tastaturen gedrückt halten, wird das gesamte Wort / die gesamte Textgruppe und die Ursachen java.lang.NumberFormatException: Bad offset/length
gelöscht
1
Es hat perfekt für mich funktioniert! Achtung an 'editText.setSelection (formatted.length ());' muss für die Eigenschaft 'maxLength' Instanz von EditText in Frage gestellt werden. maxLength == 13; formatated.length () == 14; Wenn 'formatted.length' größer als 'maxLength' ist, tritt der folgende Fehler auf: IndexOutOfBoundsException: setSpan (14 ... 14) beendet die Länge über 13 tks hinaus
GFPF
1
@BluGeni, um das zu beheben, fügen Sie einfach eine s.isEmpty-Prüfung hinzu, bevor Sie den Textänderungs-Listener entfernen, wenn (s.isEmpty ()) return; editText.removeTextChangedListener (this); Auch in der cleanString-Zeile ist s.toString () überflüssig
Mike Baglio Jr.
1
Die beste Antwort als nur ein Vorschlag ist, .replaceAll ("[$ ...) für -> .replaceAll (" [^ \\ d.] "," ") zu ändern, da Sie in einer anderen Währung andere Zeichen als nur das haben $, wie in meinem Fall war der R $ (brasilianisch)
user2582318
1
Entschuldigung, der richtige Vorschlag ist dieser -> .replaceAll("[^0-9]", ""), der obige hat ein Limit von 9.999.999 -_-
user2582318
20

Hier ist mein Brauch 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);
            }
        }
    }
}

Verwenden Sie es in XML wie

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

Sie sollten unten 2 Konstanten bearbeiten, um sie für Ihr Projekt zu verwenden

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

Geben Sie hier die Bildbeschreibung ein

Demo auf Github

Phan Van Linh
quelle
2
Das ist brilliant!
YTerle
1
Ich habe festgestellt, dass nach dem Tippen, um die maximale Anzahl von Dezimalstellen zu erreichen, der Versuch, eine Zahl von 5 bis 9 einzugeben, die letzte Dezimalstelle um 1 erhöht ... es wird aufgerundet! Mein Fix war, formatter.setRoundingMode(RoundingMode.DOWN);die formatDecimalMethode aufzurufen .
BW
@bwicks Vielen Dank, dass Sie das Problem gefunden haben. Ich habe Ihre Bearbeitung genehmigt
Phan Van Linh
Wie setzt man ein Währungssymbol anstelle von VND?
Mayur Karmur
1
Eine weitere Verbesserungsidee: Wenn der Benutzer eingibt $., wenn wir den Rohwert als erhalten .und auf Double analysieren, wird NFE angezeigt. Um das Problem zu beheben, kehrte ich formatDecimal()zurück prefix + "0.";und wechselte #,###.nach #,##0.innen formatDecimal(). Dies sieht auch besser aus, wenn der Benutzer nur Dezimalstellen eingibt. Es zeigt als $0.25statt $.25.
Gokhan Arik
12

Tatsächlich funktioniert die zuvor bereitgestellte Lösung nicht. Es funktioniert nicht, wenn Sie 100.00 eingeben möchten.

Ersetzen:

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

Mit:

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

Ich muss sagen, dass ich einige Änderungen an meinem Code vorgenommen habe. Die Sache ist, dass Sie BigDecimal verwenden sollten

sfratini
quelle
6

Ich ändere die Klasse mit implementiertem TextWatcher, um brasilianische Währungsformate zu verwenden und die Cursorposition beim Bearbeiten des Werts anzupassen.

öffentliche Klasse MoneyTextWatcher implementiert TextWatcher {

    private EditText editText;

    private String lastAmount = "";

    private int lastCursorPosition = -1;

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

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

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

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

            Versuchen {

                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 (Ausnahme e) {
               // etwas protokollieren
            }}
        }}
    }}

    @Override
    public void afterTextChanged (Editable s) {
    }}

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

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

        if (rencyValue == null) {
            Ergebnis = "";
        } else {
            Ergebnis = CurrencyValue.replaceAll ("[(az) | (AZ) | ($,.)]", "");
        }}
        Ergebnis zurückgeben;
    }}

    public static boolean isCurrencyValue (StringrencyValue, boolean podeSerZero) {
        boolesches Ergebnis;

        if (WährungWert == null || Währungswert.Länge () == 0) {
            Ergebnis = falsch;
        } else {
            if (! podeSerZero &&rencyValue.equals ("0,00")) {
                Ergebnis = falsch;
            } else {
                Ergebnis = wahr;
            }}
        }}
        Ergebnis zurückgeben;
    }}

    public static String transformToCurrency (String-Wert) {
        double parsed = Double.parseDouble (Wert);
        Zeichenfolge formatiert = NumberFormat.getCurrencyInstance (neues Gebietsschema ("pt", "BR")). Format ((analysiert / 100));
        formatted = formatted.replaceAll ("[^ (0-9) (.,)]", "");
        Rückgabe formatiert;
    }}
}}
Henrique Ho
quelle
In dieser Zeile "int newCursorOffset = max (0, min (formattedAmount.length (), lastCursorPosition + lengthDelta));" Was für ein Objekt ist max und min?
Arthur Melo
2
@ArthurMelo Its, Math.max, Math.min Vielen Dank an den Code, und es sieht so aus, als ob beim Entfernen des Kommas aus dem Edittext ein Fehler aufgetreten ist.
Marcos Vasconcelos
4

Ich habe auf Guilhermes Antwort aufgebaut, aber ich behalte die Position des Cursors bei und behandle die Punkte auch anders. Wenn ein Benutzer nach dem Punkt tippt, wirkt sich dies nicht auf die Zahlen vor dem Zeitraum aus, bei dem ich finde, dass dies eine sehr reibungslose Eingabe ergibt .

    [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
quelle
Es hilft mir sehr. Vielen Dank, dass Sie @genixpro
Harin Kaklotar
Ich mag Ihre Idee, aber es sieht flüssiger aus, wenn Sie die Anzahl der Ziffern nach dem Cursor speichern und dann setSelection (Länge - nachher) setzen.
Alpha Huang
Sehr interessant! Die Verwendung von austauschbar funktionierte auf meinem physischen Gerät, aber nicht auf dem Emulator.
Aliton Oliveira
4

Auch wenn es hier viele Antworten sind, würde Ich mag diesen Code teilen , dass ich gefunden hier , da ich seine die robusteste und saubere Antwort glauben.

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

}

ich hoffe es hilft.

Kayvan N.
quelle
Würde das nicht den Dezimalpunkt entfernen? Dann könnten Sie den Unterschied zwischen 100,00 und 10.000 US-Dollar nicht erkennen - es sei denn, ich vermisse etwas.
Nasch
2
Das ist die perfekte Antwort! arbeitete für mich. Denken Sie nur daran, wie viel Zeit ich für diese Antworten aufgewendet habe, und scrollen Sie schließlich nach unten und finden Sie die gewünschte.
Ge Rong
Ich bin froh, dass es dir geholfen hat.
Kayvan N
@nasch Dies ist ein TextWatcher und formatiert den Text als Benutzertypen, wodurch der von Ihnen erwähnte Fall verhindert wird.
Kayvan N
@ KayvanN Ich weiß, was ein TextWatcher ist. replaceAll("\\D", "")entfernt alles, was keine Ziffer ist, so dass "$ 100.00" und "$ 10.000" beide zu "10000" werden. Es scheint, dass Sie auf die Eingabe zählen, um Cent einzuschließen. Also, wenn das garantiert ist, großartig, aber wenn nicht, denke ich, wird es Probleme geben.
Nasch
4

Ok, hier ist ein besserer Weg, um mit Währungsformaten umzugehen, Rückwärts-Tastendruck löschen. Der Code basiert auf dem obigen @ androidcurious-Code ... Es werden jedoch einige Probleme im Zusammenhang mit dem Löschen von Rückwärtsbewegungen und einige Analyse-Ausnahmen behandelt: http://miguelt.blogspot.ca/2013/01/textwatcher-for-currency-masksformatting .html [UPDATE] Die vorherige Lösung hatte einige Probleme ... Dies ist eine bessere Lösung: http://miguelt.blogspot.ca/2013/02/update-textwatcher-for-currency.html Und ... hier sind die Einzelheiten:

Dieser Ansatz ist besser, da er die herkömmlichen Android-Mechanismen verwendet. Die Idee ist, Werte zu formatieren, nachdem der Benutzer die Ansicht existiert.

Definieren Sie einen InputFilter, um die numerischen Werte einzuschränken. Dies ist in den meisten Fällen erforderlich, da der Bildschirm nicht groß genug ist, um lange EditText-Ansichten aufzunehmen. Dies kann eine statische innere Klasse oder nur eine andere einfache Klasse sein:

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

Definieren Sie eine Klasse (innere statische oder nur eine Klasse), die View.OnFocusChangeListener implementiert. Beachten Sie, dass ich eine Utils-Klasse verwende. Die Implementierung finden Sie unter "Beträge, Steuern".

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

Diese Klasse entfernt das Währungsformat beim Bearbeiten - basierend auf Standardmechanismen. Beim Beenden des Benutzers wird das Währungsformat erneut angewendet.

Es ist besser, einige statische Variablen zu definieren, um die Anzahl der Instanzen zu minimieren:

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

Schließlich in der onCreateView (...):

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

Sie können FILTERS und ON_FOCUS für eine beliebige Anzahl von EditText-Ansichten wiederverwenden.

Hier ist die Utils-Klasse:

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
quelle
Obwohl dies theoretisch die Frage beantworten kann, möchten wir, dass Sie die wesentlichen Teile des verlinkten Artikels in Ihre Antwort aufnehmen und den Link als Referenz bereitstellen . Andernfalls ist die Antwort durch Link Rot gefährdet.
Kev
Ich erhalte java.lang.NumberFormatException: Ungültiges Doppel: "$ 12,345.00", wenn der Bearbeitungstext den Fokus verliert. Wie man es repariert.
Madhan
4

Ich habe die Implementierung verwendet, auf die Nathan Leigh verwiesen hat, und Kayvan Ns und user2582318s vorgeschlagenen regulären Ausdruck, um alle Zeichen außer Ziffern zu entfernen und die folgende Version zu erstellen:

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

}

Dies ist eine Erweiterungsfunktion in Kotlin, die den TextWatcher zum TextChangedListener des EditText hinzufügt.

Um es zu benutzen, einfach:

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

Ich hoffe, es hilft.

Francisco Junior
quelle
3

Ich habe dies von hier erhalten und es geändert, um dem portugiesischen Währungsformat zu entsprechen.

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

}

Die layout.xml

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

Bring es zum Laufen

    yourEditText = (EditText) findViewById(R.id.edit_text_your_id);
    yourEditText.setRawInputType(Configuration.KEYBOARD_12KEY);
    yourEditText.addTextChangedListener(new CurrencyTextWatcher(yourEditText));
Nuno Monteiro
quelle
2

Bei mir hat es so funktioniert

 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
quelle
2

Es ist besser, die InputFilter-Schnittstelle zu verwenden. Mit Regex ist es viel einfacher, Eingaben jeglicher Art zu verarbeiten. Meine Lösung für das Währungseingabeformat:

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

Gültig: 0,00, 0,0, 10,00, 111,1
Ungültig: 0, 0,000, 111, 10, 010,00, 01,0

Wie benutzt man:

editText.setFilters(new InputFilter[] {new CurrencyFormatInputFilter()});
Mussa
quelle
1

Wenn Ihr JSON-Währungsfeld vom Nummerntyp (und nicht vom String) ist, kann es 3.1, 3.15 oder nur 3 sein. Da JSON Zahlenfelder automatisch rundet.

In diesem Fall müssen Sie es möglicherweise für eine ordnungsgemäße Anzeige runden (und später eine Maske für ein Eingabefeld verwenden können):

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

Warum wird das benötigt?

Alle Antworten deuten darauf hin, dass Währungssymbole entfernt werden, wenn Sie eingeben, dass Sie die Cent erhalten, und so Dolar + Cent / 100 = Dolar, Cent formatieren. Wenn Ihr json-Währungsfeld jedoch ein Zahlentyp (und kein String) ist, rundet es Ihre Cent ab. Es kann 3, 3,1 oder 3,15 sein.

Sagits
quelle
1
Genau das, was ich brauchte. Vielen Dank!
Erick Engelhardt
come as 3.1 , 3.15 or just 3. Because json automatically round number fields- das hat nichts mit Rundung zu tun !
Marcin Orlowski
1

ein anderer Ansatz, aber basierend auf Guilherme Antwort . Dieser Ansatz ist nützlich, wenn das Gebietsschema Ihres Landes nicht verfügbar ist oder wenn Sie benutzerdefinierte Währungssymbole verwenden möchten. Diese Implementierung gilt nur für positive Nicht-Dezimalzahlen.

Dieser Code befindet sich in Kotlin, dem ersten Delegierten setMaskingMoneyfürEditText

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

Dann MyTextWatchersollte die Schnittstelle von erweitert werden TextWatcher. Da wir nur afterTextChangedMethoden benötigen, müssen die anderen Methoden in dieser Schnittstelle überschrieben werden

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

und die Monetarisierungsmethoden sind:

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

Vollständige Implementierungen:

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

und irgendwo auf der onCreate-Methode:

yourTextView.setMaskingMoney("Rp. ")
Hayi Nukman
quelle
1

Nach zu viel Suche und Fehlschlägen mit Doubles, BigDecimals usw. habe ich diesen Code erstellt. Es funktioniert Plug And Play. Es ist in Kotlin. Also, um anderen zu helfen, die wie ich festsitzen, lass uns gehen.

Der Code ist im Grunde eine Funktion, die einen TextWatcher platziert und das Koma an die richtige Stelle bringt.

Erstellen Sie zunächst diese Funktion:

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

        }
    })
}

Und dann rufen Sie diese Funktion so auf

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

CurrencyWatcher(etVal)
Thiago Silva
quelle
0

Nachdem ich mir die meisten StackOverflow-Beiträge auf verschiedene Arten angesehen habe, um dies mit einem ,, oder einer Bibliothek wie CurrencyEditText zu erreichen TextWatcher, habe ich mich für diese einfache Lösung mit einem entschieden .InputFilterOnFocusChangeListener

Die Logik besteht darin, die EditTextZahl zu analysieren , wenn sie fokussiert ist, und sie zurück zu formatieren, wenn sie den Fokus verliert.

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
quelle
0

Ich habe eine Kotlin + Rx-Version implementiert.

Es ist für die brasilianische Währung (z. B. 1.500,00 - 5,21 - 192,90), aber Sie können es problemlos für andere Formate anpassen.

Hoffe, jemand anderes findet es hilfreich.

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

Geben Sie hier die Bildbeschreibung ein

Samet ÖZTOPRAK
quelle
0

So konnte ich eine Währung in einem EditText anzeigen, der einfach zu implementieren war und für den Benutzer gut funktioniert, ohne dass überall verrückte Symbole auftreten könnten. Dadurch wird keine Formatierung durchgeführt, bis der EditText keinen Fokus mehr hat. Der Benutzer kann weiterhin zurückgehen und Änderungen vornehmen, ohne die Formatierung zu gefährden. Ich verwende die Variable 'formattedPrice' nur zur Anzeige und die Variable 'itemPrice' als Wert, den ich speichere / für Berechnungen verwende.

Es scheint wirklich gut zu funktionieren, aber ich bin erst seit ein paar Wochen dabei, daher ist jede konstruktive Kritik absolut willkommen!

Die EditText-Ansicht in der XML hat das folgende Attribut:

android:inputType="numberDecimal"

Globale Variablen:

private String formattedPrice;
private int itemPrice = 0;

In der onCreate-Methode:

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
quelle
0

Falls jemand daran interessiert ist, dies mit RxBinding und Kotlin zu tun:

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.
quelle
0

nur ein zusätzlicher Kommentar zur genehmigten Antwort. Beim Bewegen des Cursors auf dem Edittext-Feld kann es aufgrund des Parsens zu einem Absturz kommen. Ich habe eine try catch-Anweisung ausgeführt, aber Ihren eigenen Code implementiert.

@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
quelle
0

Sie können diese Methoden verwenden

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
quelle
0

Kotlin- Version:

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

und verwenden Sie diesen Block dann als 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
quelle
Sie können diesen benutzerdefinierten Bearbeitungstext verwenden, um den Eingabetext nach Ihren Wünschen zu formatieren.
Saeid Mohammadi