Cách tốt hơn để định dạng đầu vào tiền tệ editText?


91

Tôi có một editText, giá trị bắt đầu là $ 0,00. Khi bạn nhấn phím 1, nó sẽ thay đổi thành 0,01 đô la. Nhấn 4, nó chuyển thành $ 0,14. Nhấn 8, $ 1,48. Nhấn phím xóa lùi, $ 0,14, v.v.

Điều đó hoạt động, vấn đề là, nếu ai đó định vị con trỏ theo cách thủ công, các vấn đề xảy ra trong định dạng. Nếu họ xóa số thập phân, nó sẽ không quay lại. Nếu họ đặt con trỏ ở phía trước số thập phân và nhập 2, nó sẽ hiển thị $ 02,00 thay vì $ 2,00. Ví dụ: nếu họ cố gắng xóa $, nó sẽ xóa một chữ số.

Đây là mã tôi đang sử dụng, tôi đánh giá cao bất kỳ đề xuất nào.

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

1
Xin lỗi vì sự thiếu hiểu biết của tôi, nhưng đoạn mã này thuộc một trong các phương thức vòng đời hoạt động hay chúng nằm trong lớp tùy chỉnh mà bạn đã tạo? Bạn có thể cung cấp mẫu mã hoàn chỉnh hơn được không? Cảm ơn!
Argus9

Công trình này cho tôi, tôi đã cố gắng lib này bên ngoài android-arsenal.com/details/1/5374
Pravin maske

Câu trả lời:


153

Tôi đã thử nghiệm phương pháp của bạn, nhưng nó không thành công khi tôi sử dụng các số lớn ... Tôi đã tạo điều này:

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

Biến thể 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)
            }
        }

36
Có thể là tốt hơn để làm như sau chứ không phải là giả định biểu tượng đô la: String replaceable = String.format("[%s,.]", NumberFormat.getCurrencyInstance().getCurrency().getSymbol()); String cleanString = s.toString().replaceAll(replaceable, "");
craigp

6
Hmm, thực sự đã cố gắng này bản thân mình bây giờ, mô hình regex từ replaceAll sẽ trông như thế này, để không gian xử lý cũng như: String replaceable = String.format("[%s,.\\s]", NumberFormat.getCurrencyInstance().getCurrency().getSymbol());
craigp

6
Không phải là nó khuyến cáo không để làm cho những thay đổi trong onTextChanged() and rather to do so in afterTextChanged () `
codinguser

3
Tôi muốn biết tại sao trình nghe đã thay đổi văn bản bị xóa và sau đó được thêm lại mỗi lần? Đối với tôi, nó hoạt động nếu chỉ được thêm một lần (và tôi đã chuyển các thay đổi sang afterTextChanged)
Daniel Wilson

5
Tôi không làm việc khi bạn đặt 1 -> 0 -> 0 để lấy 1,00. Đó là bởi vì bạn đạt đến điểm mà 0,1 được đổi thành chuỗi 010 và 010 thành double10 là 10 / 100 = 0,1bạn không thể vượt qua nó.
JakubW

30

Dựa trên một số câu trả lời ở trên, tôi đã tạo MoneyTextWatcher mà bạn sẽ sử dụng như sau:

priceEditText.addTextChangedListener(new MoneyTextWatcher(priceEditText));

và đây là lớp học:

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

Tôi đã sử dụng tính năng này một thời gian nhưng gần đây đã phát hiện ra một vấn đề nhỏ, nếu bạn giữ nút xóa trên một số bàn phím, nó sẽ xóa toàn bộ từ / nhóm văn bản và nguyên nhânjava.lang.NumberFormatException: Bad offset/length
BluGeni 12/02/15

1
Nó làm việc hoàn hảo cho tôi! Chú ý đến 'editText.setSelection (formatted.length ());' phải được quan sát đối với phiên bản thuộc tính 'maxLength' của EditText được đề cập. maxLength == 13; formatted.length () == 14; Nếu 'formatted.length' lớn hơn 'maxLength' lỗi sau xảy ra: IndexOutOfBoundsException: setSpan (14 ... 14) kết thúc chiều dài vượt quá 13 tks
GFPF

1
@BluGeni để khắc phục điều đó chỉ cần thêm một kiểm tra s.isEmpty trước khi loại bỏ trình nghe thay đổi văn bản if (s.isEmpty ()) return; editText.removeTextChangedListener (this); Cũng trong dòng cleanString, s.toString () là thừa
Mike Baglio Jr.

1
câu trả lời tốt nhất hơn là chỉ một đề xuất là thay đổi .replaceAll ("[$ ...) cho -> .replaceAll (" [^ \\ d.] "," "); vì tôi là đơn vị tiền tệ khác, bạn có các ký tự khác ngoài $, như trong trường hợp của tôi là R $ (Brazil)
user2582318

1
xin lỗi các gợi ý chính xác là thế này -> .replaceAll("[^0-9]", ""), một bên trên có một giới hạn của 9.999.999 -_-
user2582318

21

Đây là tùy chỉnh của tôi 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);
            }
        }
    }
}

Sử dụng nó trong XML như

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

Bạn nên chỉnh sửa 2 hằng số bên dưới cho phù hợp với dự án của bạn

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

nhập mô tả hình ảnh ở đây

Demo trên github


2
Điều này là tuyệt vời!
YTerle

1
Tôi thấy rằng sau khi nhập để đạt đến số chữ số thập phân tối đa, việc cố gắng nhập số 5-9 sẽ tăng chữ số thập phân cuối cùng lên 1 ... nó làm tròn lên! Cách khắc phục của tôi là gọi formatter.setRoundingMode(RoundingMode.DOWN);trong formatDecimalphương thức.
BW

@bwicks cảm ơn bạn rất nhiều vì đã tìm ra vấn đề. Tôi đã chấp nhận chỉnh sửa của bạn
Phan Văn Linh

làm thế nào để đặt ký hiệu tiền tệ trên instade VND ??
Mayur Karmur

1
Một ý tưởng cải tiến khác: Nếu người dùng nhập $., khi chúng tôi nhận được giá trị thô là .và phân tích cú pháp thành Double, nó sẽ cho NFE. Để khắc phục, tôi đã làm formatDecimal()để trở lại prefix + "0.";và thay đổi #,###.để #,##0.bên trong formatDecimal(). Điều này cũng trông đẹp hơn khi người dùng chỉ nhập các vị trí thập phân. Nó hiển thị như $0.25thay vì $.25.
Gokhan Arik

13

Trên thực tế, giải pháp được cung cấp trước đó không hoạt động. Nó không hoạt động nếu bạn muốn nhập 100,00.

Thay thế:

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

Với:

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

Tôi phải nói rằng tôi đã thực hiện một số sửa đổi cho mã của mình. Vấn đề là bạn nên sử dụng BigDecimal's


6

Tôi thay đổi lớp với các triển khai TextWatcher để sử dụng các định dạng tiền tệ Brasil và điều chỉnh vị trí con trỏ khi chỉnh sửa giá trị.

lớp công khai MoneyTextWatcher triển khai TextWatcher {

    riêng EditText editText;

    private String lastAmount = "";

    private int lastCursorPosition = -1;

    công khai MoneyTextWatcher (EditText editText) {
        siêu();
        this.editText = editText;
    }

    @Ghi đè
    public void onTextChanged (Số lượng CharSequence, int start, int before, int count) {

        if (! amount.toString (). bằng (lastAmount)) {

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

            thử {

                String formattedAmount = biến đổiToCurrency (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 (Ngoại lệ e) {
               // ghi lại cái gì đó
            }
        }
    }

    @Ghi đè
    public void afterTextChanged (Có thể chỉnh sửa) {
    }

    @Ghi đè
    public void beforeTextChanged (CharSequence s, int start, int count, int after) {
        Giá trị chuỗi = s.toString ();
        if (! value.equals ("")) {
            String cleanString = clearCurrencyToNumber (giá trị);
            String formattedAmount = biến đổiToCurrency (cleanString);
            lastAmount = formattedAmount;
            lastCursorPosition = editText.getSelectionStart ();
        }
    }

    public static String clearCurrencyToNumber (String currencyValue) {
        Kết quả chuỗi = null;

        if (currencyValue == null) {
            kết quả = "";
        } khác {
            result = currencyValue.replaceAll ("[(az) | (AZ) | ($,.)]", "");
        }
        trả về kết quả;
    }

    public static boolean isCurrencyValue (String currencyValue, boolean podeSerZero) {
        kết quả boolean;

        if (currencyValue == null || currencyValue.length () == 0) {
            kết quả = sai;
        } khác {
            if (! podeSerZero && currencyValue.equals ("0,00")) {
                kết quả = sai;
            } khác {
                kết quả = true;
            }
        }
        trả về kết quả;
    }

    public static String variableToCurrency (Giá trị chuỗi) {
        phân tích cú pháp kép = Double.parseDouble (giá trị);
        String formatted = NumberFormat.getCurrencyInstance (new Locale ("pt", "BR")). Format ((parsed / 100));
        formatted = formatted.replaceAll ("[^ (0-9) (.,)]", "");
        trả về định dạng;
    }
}

Trong dòng này "int newCursorOffset = max (0, min (formattedAmount.length (), lastCursorPosition + lengthDelta));" vật có cực đại và cực tiểu là bao nhiêu?
Arthur Melo

2
@ArthurMelo Its, Math.max, Math.min Cảm ơn mã, và có vẻ như thất bại khi xóa dấu phẩy khỏi văn bản.
Marcos Vasconcelos

4

Tôi đã xây dựng câu trả lời Guilhermes, nhưng tôi giữ nguyên vị trí của con trỏ và cũng xử lý các dấu chấm khác nhau - theo cách này nếu người dùng đang nhập sau dấu chấm, điều này không ảnh hưởng đến các số trước dấu chấm. Tôi thấy rằng điều này cho phép nhập liệu rất mượt mà .

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

nó giúp tôi rất nhiều. Cảm ơn bạn @genixpro
Harin Kaklotar

Tôi thích ý tưởng của bạn, nhưng nó trông mượt mà hơn nếu bạn lưu số chữ số sau con trỏ, sau đó setSelection (chiều dài - sau).
Alpha Huang

Rất thú vị! Sử dụng có thể thay thế đã hoạt động trên thiết bị vật lý của tôi, nhưng nó không hoạt động trên trình giả lập.
Aliton Oliveira

4

Mặc dù có rất nhiều câu trả lời ở đây, tôi muốn chia sẻ đoạn mã này mà tôi tìm thấy ở đây vì tôi tin rằng nó là câu trả lời rõ ràng và mạnh mẽ nhất.

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

}

hy vọng nó giúp.


Điều đó sẽ không loại bỏ dấu thập phân? Vì vậy, bạn sẽ không thể phân biệt được giữa $ 100.00 và $ 10.000 - trừ khi tôi thiếu thứ gì đó.
nasch

2
đây là câu trả lời hoàn hảo! đã làm cho tôi. của tôi, chỉ cần nghĩ xem tôi đã dành bao nhiêu thời gian cho những câu trả lời đó và cuối cùng cuộn xuống dưới cùng và tìm thấy câu tôi muốn.
Ge Rong

Tôi rất vui vì nó đã giúp bạn.
Kayvan N

@nasch Đây là một TextWatcher và nó định dạng văn bản dưới dạng các kiểu người dùng, ngăn trường hợp bạn đề cập.
Kayvan N

@KayvanN Tôi biết TextWatcher là gì. replaceAll("\\D", "")sẽ xóa mọi thứ không phải là chữ số, do đó, "$ 100,00" và "$ 10.000" đều trở thành "10000". Có vẻ như bạn đang tin tưởng vào đầu vào để bao gồm xu. Vì vậy, nếu điều đó được đảm bảo, tuyệt vời nhưng nếu không, tôi nghĩ sẽ có vấn đề.
nasch

4

Ok, đây là một cách tốt hơn để xử lý các định dạng Tiền tệ, tổ hợp phím xóa lùi. Mã dựa trên mã @androidcurious ở trên ... Tuy nhiên, giải quyết một số vấn đề liên quan đến xóa ngược và một số ngoại lệ phân tích cú pháp: http://miguelt.blogspot.ca/2013/01/textwatcher-for-currency-masksformatting .html [CẬP NHẬT] Giải pháp trước đó có một số vấn đề ... Đây là giải pháp tốt hơn: http://miguelt.blogspot.ca/2013/02/update-textwatcher-for-currency.html Và ... đây là chi tiết:

Cách tiếp cận này tốt hơn vì nó sử dụng các cơ chế Android thông thường. Ý tưởng là định dạng các giá trị sau khi người dùng tồn tại Chế độ xem.

Xác định InputFilter để hạn chế các giá trị số - điều này là bắt buộc trong hầu hết các trường hợp vì màn hình không đủ lớn để chứa các dạng xem EditText dài. Đây có thể là một lớp bên trong tĩnh hoặc chỉ một lớp đơn giản khác:

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

Xác định một lớp (tĩnh bên trong hoặc chỉ một lớp) sẽ triển khai View.OnFocusChangeListener. Lưu ý rằng tôi đang sử dụng lớp Utils - việc triển khai có thể được tìm thấy tại "Số tiền, Thuế".

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

Lớp này sẽ loại bỏ định dạng tiền tệ khi chỉnh sửa - dựa trên các cơ chế tiêu chuẩn. Khi người dùng thoát, định dạng tiền tệ sẽ được áp dụng lại.

Tốt hơn nên xác định một số biến tĩnh để giảm thiểu số lượng trường hợp:

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

Cuối cùng, trong onCreateView (...):

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

Bạn có thể sử dụng lại FILTERS và ON_FOCUS trên bất kỳ số lượng chế độ xem EditText nào.

Đây là lớp 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());
   }

}

Mặc dù điều này về mặt lý thuyết có thể trả lời câu hỏi, chúng tôi muốn bạn bao gồm các phần thiết yếu của bài viết được liên kết trong câu trả lời của bạn và cung cấp liên kết để tham khảo . Không làm được điều đó khiến câu trả lời có nguy cơ bị thối liên kết.
Kev

Tôi nhận được java.lang.NumberFormatException: Double không hợp lệ: "$ 12.345.00" khi văn bản chỉnh sửa bị mất tiêu điểm. Cách khắc phục.
Madhan

4

Tôi đã sử dụng cách triển khai được Nathan Leigh tham chiếu và regex được đề xuất của Kayvan N và user2582318 để xóa tất cả các ký tự ngoại trừ các chữ số để tạo phiên bản sau:

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

}

Đây là một chức năng mở rộng trong Kotlin để thêm TextWatcher vào TextChangedListener của EditText.

Để sử dụng nó, chỉ cần:

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

Tôi hy vọng nó sẽ giúp.


3

Tôi lấy nó từ đây và đã thay đổi nó để tuân theo định dạng tiền tệ của Bồ Đào Nha.

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

}

Layout.xml

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

Làm cho nó hoạt động

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

2

Đối với tôi nó hoạt động như thế này

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

2

Tốt hơn là sử dụng giao diện InputFilter. Dễ dàng hơn nhiều để xử lý bất kỳ loại đầu vào nào bằng cách sử dụng regex. Giải pháp của tôi cho định dạng nhập tiền tệ:

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

Hợp lệ: 0.00, 0.0, 10.00, 111.1
Không hợp lệ: 0, 0.000, 111, 10, 010.00, 01.0

Cách sử dụng:

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

1

Tôi đã sử dụng điều này để cho phép người dùng nhập tiền tệ và chuyển đổi nó từ chuỗi thành int để lưu trữ trong db và thay đổi lại từ int thành chuỗi

https://github.com/nleigh/Restaurant/blob/master/Restaurant/src/uk/co/nathanleigh/restaurant/CurrencyFormat.java


Bạn có thể đưa các khái niệm chính vào câu trả lời của mình không? Nếu không, đây khá nhiều là câu trả lời chỉ có liên kết và dễ bị xóa ...
Alexander Vogt

1

Nếu trường tiền tệ json của bạn thuộc loại số (chứ không phải Chuỗi) thì nó có thể có dạng 3,1, 3,15 hoặc chỉ là 3. Vì json tự động làm tròn các trường số.

Trong trường hợp này, bạn có thể cần phải làm tròn nó để hiển thị thích hợp (và để có thể sử dụng mặt nạ trên trường nhập liệu sau này):

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

Tại sao điều này là cần thiết?

Tất cả các câu trả lời đều chỉ ra việc loại bỏ các mô phỏng tiền tệ khi gõ rung bạn đang nhận được xu và do đó định dạng dolar + xu / 100 = dolar, xu. Nhưng nếu trường tiền tệ json của bạn là một loại số (chứ không phải Chuỗi) thì nó sẽ làm tròn số xu của bạn, nó có thể là 3, 3,1 hoặc 3,15.


1
Chính xác những gì tôi cần. Cảm ơn!
Erick Engelhardt

come as 3.1 , 3.15 or just 3. Because json automatically round number fields- điều này không có gì phổ biến với việc làm tròn !
Marcin Orlowski

1

một cách tiếp cận khác, nhưng dựa trên câu trả lời của Guilherme . Cách tiếp cận này hữu ích khi ngôn ngữ quốc gia của bạn không có sẵn hoặc nếu bạn muốn sử dụng các ký hiệu tiền tệ tùy chỉnh. Việc triển khai này chỉ dành cho số dương không thập phân.

mã này nằm trong Kotlin, đại biểu đầu tiên setMaskingMoneychoEditText

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

Sau đó, MyTextWatchergiao diện sẽ được mở rộng từ TextWatcher. Vì chúng ta chỉ cần afterTextChangedphương thức, các phương thức khác cần ghi đè trong giao diện này

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

và các phương pháp kiếm tiền là:

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

Triển khai đầy đủ:

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

và ở đâu đó trên phương thức onCreate:

yourTextView.setMaskingMoney("Rp. ")

1

Sau quá nhiều tìm kiếm và không thành công với Double, BigDecimals, v.v., tôi đã tạo mã này. Nó hoạt động plug And Play. Của nó trong kotlin. Vì vậy, để giúp những người khác bị mắc kẹt như tôi, hãy đi.

Về cơ bản, mã này là một hàm sẽ đặt một textWatcher và điều chỉnh hôn mê đến đúng vị trí.

Đầu tiên, hãy tạo hàm này:

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

        }
    })
}

Và sau đó bạn gọi hàm này theo cách này

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

CurrencyWatcher(etVal)

0

Sau khi xem xét hầu hết các bài viết StackOverflow trên cách khác nhau để đạt được điều này bằng cách sử dụng TextWatcher, InputFilterhoặc thư viện như CurrencyEditText Tôi đã giải quyết trên giải pháp đơn giản này sử dụng một OnFocusChangeListener.

Logic là phân tích cú pháp EditTextthành một số khi nó được lấy nét và định dạng lại khi nó mất tiêu điểm.

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

0

Tôi đã triển khai phiên bản Kotlin + Rx.

Nó dành cho đơn vị tiền tệ của Brazil (ví dụ: 1.500,00 - 5,21 - 192,90) nhưng bạn có thể dễ dàng điều chỉnh cho các định dạng khác.

Hy vọng ai đó khác thấy nó hữu ích.

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

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

nhập mô tả hình ảnh ở đây


0

Đây là cách tôi có thể hiển thị một đơn vị tiền tệ trong một EditText dễ triển khai và hoạt động tốt cho người dùng mà không có khả năng xuất hiện các biểu tượng điên rồ ở khắp nơi. Điều này sẽ không cố gắng thực hiện bất kỳ định dạng nào cho đến khi EditText không còn tiêu điểm. Người dùng vẫn có thể quay lại và thực hiện bất kỳ chỉnh sửa nào mà không gây nguy hiểm cho việc định dạng. Tôi sử dụng biến 'formattedPrice' để chỉ hiển thị và biến 'itemPrice' làm giá trị mà tôi lưu trữ / sử dụng để tính toán.

Có vẻ như nó đang hoạt động rất tốt, nhưng tôi chỉ mới làm việc này được vài tuần, vì vậy mọi lời chỉ trích mang tính xây dựng đều hoàn toàn được hoan nghênh!

Chế độ xem EditText trong xml có thuộc tính sau:

android:inputType="numberDecimal"

Các biến toàn cục:

private String formattedPrice;
private int itemPrice = 0;

Trong phương thức 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);
        }
    }
});

0

Trong trường hợp ai đó quan tâm đến cách thực hiện bằng RxBinding và 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
            }

0

chỉ là một nhận xét bổ sung cho câu trả lời đã được phê duyệt. Bạn có thể gặp sự cố khi di chuyển con trỏ trên trường văn bản do phân tích cú pháp. Tôi đã thử một câu lệnh bắt, nhưng hãy triển khai mã của riêng bạn.

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

0

bạn có thể sử dụng những phương pháp này

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

0

Phiên bản 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)
            }
        }
    })

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

và sau đó sử dụng khối này làm văn bản chỉnh sửa của bạn

   <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" />

Bạn có thể sử dụng văn bản chỉnh sửa tùy chỉnh này để định dạng văn bản đầu vào như bạn muốn.
Mohammadi nói

Tôi đã thay đổi lớp này để chấp nhận số âm. Đoạn mã dưới đây là một câu trả lời.
Michel Fernandes

0

Đây giống như câu trả lời của Saeid Mohammadi nhưng tôi đã thay đổi để chấp nhận số âm.

  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")))
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.