วิธีที่ดีกว่าในการจัดรูปแบบการป้อนข้อมูลสกุลเงิน editText?


92

ฉันมี EditText ค่าเริ่มต้นคือ $ 0.00 เมื่อคุณกด 1 มันจะเปลี่ยนเป็น $ 0.01 กด 4 ไปที่ 0.14 ดอลลาร์ กด 8, $ 1.48 กด backspace 0.14 เหรียญเป็นต้น

วิธีนี้ได้ผลปัญหาคือถ้าใครบางคนวางตำแหน่งเคอร์เซอร์ด้วยตนเองปัญหาจะเกิดขึ้นในการจัดรูปแบบ ถ้าพวกเขาจะลบทศนิยมมันจะไม่กลับมา หากพวกเขาวางเคอร์เซอร์ไว้หน้าทศนิยมและพิมพ์ 2 จะแสดง $ 02.00 แทน $ 2.00 ถ้าพวกเขาพยายามลบ $ มันจะลบตัวเลขแทนเช่น

นี่คือรหัสที่ฉันใช้ฉันขอขอบคุณสำหรับข้อเสนอแนะใด ๆ

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
ขอโทษที่ฉันไม่รู้ แต่ข้อมูลโค้ดนี้มาจากหนึ่งในวิธีวงจรชีวิตของกิจกรรมหรืออยู่ในคลาสแบบกำหนดเองที่คุณสร้างขึ้น? คุณสามารถให้ตัวอย่างโค้ดที่สมบูรณ์มากขึ้นได้หรือไม่? ขอบคุณ!
Argus9

สิ่งนี้ใช้ได้กับฉันฉันได้ลอง lib ภายนอกแล้วandroid-arsenal.com/details/1/5374
pravin maske

คำตอบ:


156

ฉันทดสอบวิธีของคุณแล้ว แต่มันล้มเหลวเมื่อฉันใช้ตัวเลขจำนวนมาก ... ฉันสร้างสิ่งนี้:

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

ตัวแปร 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
อาจจะดีกว่าที่จะทำสิ่งต่อไปนี้แทนที่จะใช้สัญลักษณ์ดอลลาร์: String replaceable = String.format("[%s,.]", NumberFormat.getCurrencyInstance().getCurrency().getSymbol()); String cleanString = s.toString().replaceAll(replaceable, "");
Craigp

6
อืมลองทำด้วยตัวเองแล้วตอนนี้รูปแบบ regex จาก replaceAll ควรมีลักษณะเช่นนี้เพื่อจัดการกับช่องว่างเช่นกัน: String replaceable = String.format("[%s,.\\s]", NumberFormat.getCurrencyInstance().getCurrency().getSymbol());
craigp

6
มันจะไม่แนะนำไม่ได้มีการเปลี่ยนแปลงทำให้ในonTextChanged() and rather to do so in afterTextChanged () `
codinguser

3
ฉันสนใจที่จะทราบว่าเหตุใดข้อความที่เปลี่ยนฟังจึงถูกลบออกแล้วเพิ่มใหม่ทุกครั้ง? สำหรับฉันมันใช้งานได้ถ้าเพิ่มเพียงครั้งเดียว (และฉันย้ายการเปลี่ยนแปลงไปที่ afterTextChanged)
Daniel Wilson

6
ฉันไม่ทำงานเมื่อคุณใส่ 1 -> 0 -> 0 เพื่อรับ 1,00 เป็นเพราะคุณไปถึงจุดที่ 0,1 เปลี่ยนเป็นสตริง 010 และ 010 doubleเป็น 10 10 / 100 = 0,1คุณไม่สามารถผ่านมันไปได้
JakubW

30

จากคำตอบข้างต้นฉันได้สร้าง MoneyTextWatcher ซึ่งคุณจะใช้ดังต่อไปนี้:

priceEditText.addTextChangedListener(new MoneyTextWatcher(priceEditText));

และนี่คือชั้นเรียน:

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

ฉันใช้สิ่งนี้มาระยะหนึ่งแล้ว แต่เมื่อเร็ว ๆ นี้พบปัญหาเล็กน้อยหากคุณกดปุ่มลบบนแป้นพิมพ์บางตัวมันจะลบคำ / กลุ่มข้อความและสาเหตุทั้งหมดjava.lang.NumberFormatException: Bad offset/length
BluGeni

1
มันทำงานได้อย่างสมบูรณ์แบบสำหรับฉัน! ความสนใจไปที่ 'editText.setSelection (formatted.length ());' จะต้องปฏิบัติตามคุณสมบัติ 'maxLength' อินสแตนซ์ของ EditText ที่เป็นปัญหา maxLength == 13; formatted.length () == 14; หาก 'formatted.length' มากกว่า 'maxLength' ข้อผิดพลาดต่อไปนี้จะเกิดขึ้น: IndexOutOfBoundsException: setSpan (14 ... 14) สิ้นสุดความยาวเกิน 13 tks
GFPF

1
@BluGeni เพื่อแก้ไขว่าเพียงแค่เพิ่มการตรวจสอบ s.isEmpty ก่อนที่จะลบตัวฟังการเปลี่ยนแปลงข้อความถ้า (s.isEmpty ()) กลับ editText.removeTextChangedListener (นี้); นอกจากนี้ในบรรทัด cleanString s.toString () ยังซ้ำซ้อน
Mike Baglio Jr.

1
คำตอบที่ดีที่สุดมากกว่าคำแนะนำเดียวคือการเปลี่ยน. replaceAll ("[$ ... ) for -> .replaceAll (" [^ \\ d.] "," ") เนื่องจากในสกุลเงินอื่นคุณมีอักขระอื่นที่ไม่ใช่แค่ $ เช่นในกรณีของฉันคือ R $ (บราซิล)
user2582318

1
ขออภัยข้อเสนอแนะที่ถูกต้องคือคำแนะนำนี้ -> ข้อเสนอแนะ.replaceAll("[^0-9]", "")ด้านบนมีขีด จำกัด 9.999.999 -_-
user2582318

22

นี่คือประเพณีของฉัน 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);
            }
        }
    }
}

ใช้ใน XML เช่น

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

คุณควรแก้ไข 2 ค่าคงที่ด้านล่างเพื่อให้เหมาะกับโครงการของคุณ

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

ป้อนคำอธิบายภาพที่นี่

สาธิตใน github


2
ยอดเยี่ยมมาก!
YTerle

1
ฉันพบว่าหลังจากพิมพ์ไปถึงจำนวนทศนิยมสูงสุดแล้วการพยายามป้อนตัวเลข 5-9 จะทำให้ทศนิยมตำแหน่งสุดท้ายเพิ่มขึ้น 1 ... มันจะปัดเศษขึ้น! การแก้ไขของฉันคือการเรียกformatter.setRoundingMode(RoundingMode.DOWN);ใช้formatDecimalวิธีการ
BW

@bwicks ขอบคุณมากสำหรับการค้นหาปัญหา ฉันได้อนุมัติการแก้ไขของคุณแล้ว
Phan Van Linh

วิธีใส่สัญลักษณ์สกุลเงิน VND ??
Mayur Karmur

1
แนวคิดในการปรับปรุงอีกประการหนึ่ง: หากผู้ใช้ป้อน$.เมื่อเราได้รับค่าดิบเป็น.และแยกวิเคราะห์เป็น Double จะให้ NFE ในการแก้ไขปัญหาที่ผมทำformatDecimal()เพื่อผลตอบแทนprefix + "0.";และเปลี่ยน#,###.ไปภายใน#,##0. formatDecimal()นอกจากนี้ยังดูดีขึ้นเมื่อผู้ใช้ป้อนตำแหน่งทศนิยมเท่านั้น มันแสดงให้เห็นว่าเป็นแทน$0.25 $.25
Gokhan Arik

13

อันที่จริงวิธีแก้ปัญหาที่ให้ไว้ก่อนหน้านี้ไม่ทำงาน ใช้ไม่ได้ถ้าคุณต้องการเข้า 100.00

แทนที่:

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

ด้วย:

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

ฉันต้องบอกว่าฉันได้ทำการแก้ไขโค้ดของฉัน สิ่งนี้คือคุณควรใช้ BigDecimal


6

ฉันเปลี่ยนคลาสโดยใช้ TextWatcher เพื่อใช้รูปแบบสกุลเงิน Brasil และปรับตำแหน่งเคอร์เซอร์เมื่อแก้ไขค่า

MoneyTextWatcher ระดับสาธารณะใช้ TextWatcher {

    EditText ส่วนตัว editText;

    สตริงส่วนตัว lastAmount = "";

    int ส่วนตัว lastCursorPosition = -1;

    MoneyTextWatcher สาธารณะ (EditText editText) {
        ซุปเปอร์ ();
        this.editText = editText;
    }

    @แทนที่
    โมฆะสาธารณะ onTextChanged (จำนวน CharSequence, int start, int ก่อน, int count) {

        ถ้า (! amount.toString (). เท่ากับ (lastAmount)) {

            สตริง cleanString = clearCurrencyToNumber (amount.toString ());

            ลอง {

                สตริง formattedAmount = transformToCurrency (cleanString);
                editText.removeTextChangedListener (นี้);
                editText.setText (formattedAmount);
                editText.setSelection (formattedAmount.length ());
                editText.addTextChangedListener (นี้);

                if (lastCursorPosition! = lastAmount.length () && lastCursorPosition! = -1) {
                    int lengthDelta = formattedAmount.length () - lastAmount.length ();
                    int newCursorOffset = สูงสุด (0, นาที (formattedAmount.length (), lastCursorPosition + lengthDelta));
                    editText.setSelection (newCursorOffset);
                }
            } catch (ข้อยกเว้นจ) {
               // บันทึกบางอย่าง
            }
        }
    }

    @แทนที่
    โมฆะสาธารณะ afterTextChanged (แก้ไขได้) {
    }

    @แทนที่
    โมฆะสาธารณะ beforeTextChanged (CharSequence s, int start, int count, int after) {
        ค่าสตริง = s.toString ();
        ถ้า (! value.equals ("")) {
            สตริง cleanString = clearCurrencyToNumber (ค่า);
            สตริง formattedAmount = transformToCurrency (cleanString);
            lastAmount = formattedAmount;
            lastCursorPosition = editText.getSelectionStart ();
        }
    }

    สาธารณะคง String clearCurrencyToNumber (String currencyValue) {
        ผลลัพธ์สตริง = null;

        ถ้า (currencyValue == null) {
            ผลลัพธ์ = "";
        } else {
            ผลลัพธ์ = currencyValue.replaceAll ("[(az) | (AZ) | ($ ,. )]", "");
        }
        ผลตอบแทน;
    }

    บูลีนแบบคงที่สาธารณะ isCurrencyValue (String currencyValue บูลีน podeSerZero) {
        ผลบูลีน

        ถ้า (currencyValue == null || currencyValue.length () == 0) {
            ผลลัพธ์ = เท็จ;
        } else {
            ถ้า (! podeSerZero && currencyValue.equals ("0,00")) {
                ผลลัพธ์ = เท็จ;
            } else {
                ผลลัพธ์ = จริง;
            }
        }
        ผลตอบแทน;
    }

    สาธารณะคง String transformToCurrency (ค่าสตริง) {
        แยกวิเคราะห์สองครั้ง = Double.parseDouble (ค่า);
        รูปแบบสตริง = NumberFormat.getCurrencyInstance (สถานที่ใหม่ ("pt", "BR")) รูปแบบ ((แยกวิเคราะห์ / 100));
        formatted = formatted.replaceAll ("[^ (0-9) (.,)]", "");
        ส่งคืนรูปแบบ;
    }
}

ในบรรทัดนี้ "int newCursorOffset = max (0, min (formattedAmount.length (), lastCursorPosition + lengthDelta));" ชนิดของวัตถุคือสูงสุดและต่ำสุด?
Arthur Melo

2
@ArthurMelo Its, Math.max, Math.min ขอบคุณรหัสและดูเหมือนว่าล้มเหลวเมื่อลบเครื่องหมายจุลภาคออกจากข้อความแก้ไข
Marcos Vasconcelos

4

ฉันสร้างขึ้นจากคำตอบของ Guilhermes แต่ฉันรักษาตำแหน่งของเคอร์เซอร์และปฏิบัติต่อช่วงเวลาที่แตกต่างกันด้วยวิธีนี้หากผู้ใช้พิมพ์หลังจากช่วงเวลาดังกล่าวจะไม่มีผลต่อตัวเลขก่อนช่วงเวลาที่ฉันพบว่าสิ่งนี้ให้การป้อนข้อมูลที่ราบรื่นมาก .

    [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
Harin Kaklotar

ฉันชอบความคิดของคุณ แต่จะดูราบรื่นกว่าถ้าคุณบันทึกจำนวนหลักไว้หลังเคอร์เซอร์จากนั้น setSelection (ความยาว - หลัง)
Alpha Huang

น่าสนใจมาก! การใช้การถอดเปลี่ยนได้ทำงานบนอุปกรณ์จริงของฉัน แต่ไม่สามารถใช้งานได้กับโปรแกรมจำลอง
Aliton Oliveira

4

แม้ว่าจะมีคำตอบมากมายที่นี่ แต่ฉันก็อยากจะแบ่งปันรหัสนี้ที่ฉันพบในที่นี้เพราะฉันเชื่อว่ามันเป็นคำตอบที่ชัดเจนและชัดเจนที่สุด

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

}

หวังว่ามันจะช่วยได้


นั่นจะไม่ตัดจุดทศนิยมออกไปหรือ? ดังนั้นคุณจะไม่สามารถบอกความแตกต่างระหว่าง $ 100.00 ถึง $ 10,000 ได้เว้นแต่ฉันจะพลาดอะไรไป
nasch

2
นี่คือคำตอบที่สมบูรณ์แบบ! ทำงานให้ฉัน ของฉันแค่คิดว่าฉันใช้เวลากับคำตอบเหล่านั้นมากแค่ไหนและในที่สุดก็เลื่อนลงไปด้านล่างและพบคำตอบที่ฉันต้องการ
Ge Rong

ฉันดีใจที่ช่วยคุณได้
Kayvan N

@nasch นี่คือ TextWatcher และจัดรูปแบบข้อความเป็นประเภทผู้ใช้ซึ่งป้องกันกรณีที่คุณพูดถึง
Kayvan N

@KayvanN ฉันรู้ว่า TextWatcher คืออะไร replaceAll("\\D", "")จะลบทุกอย่างที่ไม่ใช่ตัวเลขดังนั้น "$ 100.00" และ "$ 10,000" จึงกลายเป็น "10000" ดูเหมือนว่าคุณกำลังวางใจในการป้อนข้อมูลเพื่อรวมเซ็นต์ ดังนั้นถ้ามันรับประกันได้ดี แต่ถ้าไม่ฉันคิดว่าจะมีปัญหา
nasch

4

ตกลงนี่เป็นวิธีที่ดีกว่าในการจัดการกับรูปแบบสกุลเงินการกดแป้นพิมพ์แบบลบย้อนกลับ รหัสนี้อ้างอิงจากโค้ด @androidcurious ด้านบน ... แต่เกี่ยวข้องกับปัญหาบางอย่างที่เกี่ยวข้องกับการลบย้อนกลับและข้อยกเว้นการแยกวิเคราะห์บางอย่าง: http://miguelt.blogspot.ca/2013/01/textwatcher-for-currency-masksformatting .html [UPDATE] วิธีแก้ปัญหาก่อนหน้านี้มีปัญหา ... นี่คือ solutoin ที่ดีกว่า: http://miguelt.blogspot.ca/2013/02/update-textwatcher-for-currency.html และ ... นี่คือ รายละเอียด:

วิธีนี้ดีกว่าเนื่องจากใช้กลไก Android ทั่วไป แนวคิดคือการจัดรูปแบบค่าหลังจากที่ผู้ใช้มีมุมมอง

กำหนด InputFilter เพื่อ จำกัด ค่าตัวเลขซึ่งจำเป็นในกรณีส่วนใหญ่เนื่องจากหน้าจอมีขนาดไม่ใหญ่พอที่จะรองรับมุมมอง EditText แบบยาวได้ นี่อาจเป็นคลาสภายในแบบคงที่หรือคลาสธรรมดาอื่นก็ได้:

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

กำหนดคลาส (คงที่ภายในหรือเป็นเพียงคลาส) ที่จะใช้ View OnFocusChangeListener โปรดทราบว่าฉันใช้คลาส Utils ซึ่งสามารถดูการใช้งานได้ที่ "Amounts, Taxes"

/** Used to format the amount views. */
class AmountOnFocusChangeListener implements View.OnFocusChangeListener {
    @Override
    public void onFocusChange(View p_view, boolean p_hasFocus) {
        // This listener will be attached to any view containing amounts.
        EditText v_amountView = (EditText)p_view;
        if (p_hasFocus) {
            // v_value is using a currency mask - transfor over to cents.
            String v_value = v_amountView.getText().toString();
            int v_cents = Utils.parseAmountToCents(v_value);
            // Now, format cents to an amount (without currency mask)
            v_value = Utils.formatCentsToAmount(v_cents);
            v_amountView.setText(v_value);
            // Select all so the user can overwrite the entire amount in one shot.
            v_amountView.selectAll();
        } else {
            // v_value is not using a currency mask - transfor over to cents.
            String v_value = v_amountView.getText().toString();
            int v_cents = Utils.parseAmountToCents(v_value);
            // Now, format cents to an amount (with currency mask)
            v_value = Utils.formatCentsToCurrency(v_cents);
            v_amountView.setText(v_value);
        }
    }
}

คลาสนี้จะลบรูปแบบสกุลเงินออกเมื่อทำการแก้ไข - อาศัยกลไกมาตรฐาน เมื่อผู้ใช้ออกจากระบบจะใช้รูปแบบสกุลเงินอีกครั้ง

การกำหนดตัวแปรคงที่จะดีกว่าเพื่อลดจำนวนอินสแตนซ์:

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

สุดท้ายภายใน onCreateView (... ):

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

คุณสามารถใช้ FILTERS และ ON_FOCUS ซ้ำในมุมมอง EditText จำนวนเท่าใดก็ได้

นี่คือคลาส 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());
   }

}

ขณะนี้ในทางทฤษฎีอาจจะตอบคำถามที่เราอยากให้คุณรวมถึงชิ้นส่วนที่สำคัญของบทความที่เชื่อมโยงในคำตอบของคุณและให้การเชื่อมโยงสำหรับการอ้างอิง การไม่ทำเช่นนั้นทำให้คำตอบมีความเสี่ยงจากลิงก์เน่า
Kev

ฉันได้รับ java.lang.NumberFormatException: คู่ที่ไม่ถูกต้อง: "$ 12,345.00" เมื่อข้อความแก้ไขสูญเสียโฟกัส จะแก้ไขอย่างไร.
Madhan

4

ฉันใช้การใช้งาน Nathan Leigh ที่อ้างถึงและ regex ที่แนะนำของ Kayvan N และ user2582318 เพื่อลบอักขระทั้งหมดยกเว้นตัวเลขเพื่อสร้างเวอร์ชันต่อไปนี้:

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

}

นี่คือฟังก์ชันส่วนขยายใน Kotlin ซึ่งเพิ่ม TextWatcher ไปยัง TextChangedListener ของ EditText

ในการใช้งานเพียง:

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

ฉันหวังว่ามันจะช่วยได้


3

ฉันได้รับสิ่งนี้จากที่นี่และเปลี่ยนให้เป็นไปตามรูปแบบสกุลเงินโปรตุเกส

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

ใช้งานได้จริง

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

2

สำหรับฉันมันทำงานแบบนี้

 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

ควรใช้อินเทอร์เฟซ InputFilter ง่ายกว่ามากในการจัดการอินพุตทุกประเภทโดยใช้ regex โซลูชันของฉันสำหรับรูปแบบการป้อนสกุลเงิน:

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

Valid: 0.00, 0.0, 10.00, 111.1
ไม่ถูกต้อง: 0, 0.000, 111, 10, 010.00, 01.0

วิธีใช้:

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

1

ฉันใช้สิ่งนี้เพื่ออนุญาตให้ผู้ใช้ป้อนสกุลเงินและแปลงจากสตริงเป็น int เพื่อเก็บใน db และเปลี่ยนกลับจาก int เป็นสตริงอีกครั้ง

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


คุณสามารถใส่แนวคิดหลักในคำตอบของคุณได้หรือไม่? มิฉะนั้นนี่เป็นคำตอบแบบลิงก์เท่านั้นและมีแนวโน้มที่จะถูกลบ ...
Alexander Vogt

1

หากฟิลด์สกุลเงิน json ของคุณเป็นประเภทตัวเลข (ไม่ใช่สตริง) อาจเป็น 3.1, 3.15 หรือเพียง 3 เนื่องจาก json จะปัดเศษฟิลด์ตัวเลขโดยอัตโนมัติ

ในกรณีนี้คุณอาจต้องปัดเศษเพื่อการแสดงผลที่เหมาะสม (และเพื่อให้สามารถใช้มาสก์บนช่องป้อนข้อมูลได้ในภายหลัง):

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

ทำไมจึงจำเป็น?

คำตอบทั้งหมดชี้ไปที่การลบซิมโบลของสกุลเงินเมื่อพิมพ์ตัดสินว่าคุณได้รับเซนต์และรูปแบบ dolar + cents / 100 = dolar, cents แต่ถ้าฟิลด์สกุลเงิน json ของคุณเป็นประเภทตัวเลข (ไม่ใช่สตริง) มันจะปัดเศษเซ็นต์ของคุณอาจเป็น 3, 3.1 หรือ 3.15


1
สิ่งที่ฉันต้องการ ขอบคุณ!
Erick Engelhardt

come as 3.1 , 3.15 or just 3. Because json automatically round number fields- สิ่งนี้ไม่มีอะไรเหมือนกันกับการปัดเศษ !
Marcin Orlowski

1

หลังจากค้นหามากเกินไปและล้มเหลวด้วย Doubles, BigDecimals และอื่น ๆ ฉันได้สร้างรหัสนี้ ใช้งานได้ Plug And Play มันอยู่ใน kotlin ดังนั้นเพื่อช่วยคนอื่น ๆ ที่ติดอยู่เช่นฉันไปกันเถอะ

รหัสโดยทั่วไปเป็นฟังก์ชันที่จะวาง textWatcher และปรับโคม่าไปยังตำแหน่งที่ถูกต้อง

ขั้นแรกให้สร้างฟังก์ชันนี้:

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

        }
    })
}

แล้วคุณเรียกใช้ฟังก์ชันนี้ด้วยวิธีนี้

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

CurrencyWatcher(etVal)

1

อีกแนวทางหนึ่ง แต่ขึ้นอยู่กับคำตอบของ Guilhermeคำตอบวิธีนี้มีประโยชน์เมื่อไม่มีภาษาของประเทศของคุณหรือหากคุณต้องการใช้สัญลักษณ์สกุลเงินที่กำหนดเอง การใช้งานนี้ใช้สำหรับค่าบวกที่ไม่ใช่ทศนิยมเท่านั้น

รหัสนี้อยู่ใน Kotlin อันดับแรกมอบหมายsetMaskingMoneyให้EditText

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

จากนั้นอินเตอร์เฟซที่ควรจะยื่นออกมาจากMyTextWatcher TextWatcherเนื่องจากเราต้องการเพียงafterTextChangedวิธีการเท่านั้นจึงจำเป็นต้องใช้วิธีการอื่น ๆ ในอินเทอร์เฟซ

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

การใช้งานเต็มรูปแบบ:

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

และบางแห่งในเมธอด onCreate:

yourTextView.setMaskingMoney("Rp. ")

0

หลังจากดูที่มากที่สุดของการโพสต์ StackOverflow เกี่ยวกับวิธีการที่แตกต่างกันเพื่อให้บรรลุนี้ใช้TextWatcher, InputFilterหรือห้องสมุดเช่นCurrencyEditTextผมเคยตัดสินในการแก้ปัญหานี้ง่ายใช้OnFocusChangeListenerผมเคยตัดสินในการแก้ปัญหานี้ง่ายใช้

ตรรกะคือการแยกวิเคราะห์EditTextตัวเลขเมื่อถูกโฟกัสและจัดรูปแบบกลับเมื่อสูญเสียโฟกัส

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

ฉันติดตั้งเวอร์ชัน Kotlin + Rx แล้ว

เป็นสกุลเงินของบราซิล (เช่น 1,500.00 - 5,21 - 192,90) แต่คุณสามารถปรับให้เข้ากับรูปแบบอื่น ๆ ได้อย่างง่ายดาย

หวังว่าคนอื่นจะพบว่ามันเป็นประโยชน์

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

ป้อนคำอธิบายภาพที่นี่


0

นี่คือวิธีที่ฉันสามารถแสดงสกุลเงินใน EditText ที่ใช้งานง่ายและทำงานได้ดีสำหรับผู้ใช้โดยไม่มีสัญลักษณ์ที่บ้าคลั่งอยู่ทั่วทุกแห่ง การดำเนินการนี้จะไม่พยายามจัดรูปแบบใด ๆ จนกว่า EditText จะไม่มีโฟกัสอีกต่อไป ผู้ใช้ยังคงสามารถย้อนกลับและทำการแก้ไขได้โดยไม่ต้องเสี่ยงต่อการจัดรูปแบบ ฉันใช้ตัวแปร 'formattedPrice' สำหรับการแสดงผลเท่านั้นและตัวแปร 'itemPrice' เป็นค่าที่ฉันเก็บ / ใช้สำหรับการคำนวณ

ดูเหมือนว่าจะทำงานได้ดีจริง ๆ แต่ฉันอยู่ที่นี่ได้เพียงไม่กี่สัปดาห์ดังนั้นคำวิจารณ์ที่สร้างสรรค์ใด ๆ ก็ยินดีต้อนรับ!

มุมมอง EditText ใน xml มีแอตทริบิวต์ต่อไปนี้:

android:inputType="numberDecimal"

ตัวแปรส่วนกลาง:

private String formattedPrice;
private int itemPrice = 0;

ในเมธอด 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

ในกรณีที่มีคนสนใจวิธีการโดยใช้ RxBinding และ 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

เพียงแสดงความคิดเห็นเพิ่มเติมสำหรับคำตอบที่ได้รับอนุมัติ คุณอาจเกิดข้อขัดข้องเมื่อเลื่อนเคอร์เซอร์บนฟิลด์ข้อความแก้ไขเนื่องจากการแยกวิเคราะห์ ฉันทำคำสั่ง try catch แต่ใช้โค้ดของคุณเอง

@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

คุณสามารถใช้วิธีการเหล่านี้

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

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

จากนั้นใช้บล็อกนี้เป็นข้อความแก้ไขของคุณ

   <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

ฉันเปลี่ยนคลาสนี้ให้ยอมรับจำนวนลบ รหัสดังกล่าวเป็นคำตอบ
Michel Fernandes

0

นี่ก็เหมือนกับคำตอบของ Saeid Mohammadi แต่ฉันเปลี่ยนไปยอมรับจำนวนลบ

  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")))
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.