Batasi Tempat Desimal di Android EditText

125

Saya mencoba menulis aplikasi yang membantu Anda mengelola keuangan Anda. Saya menggunakan EditTextBidang di mana pengguna dapat menentukan jumlah uang.

Aku mengatur inputTypeuntuk numberDecimalyang bekerja dengan baik, kecuali bahwa ini memungkinkan orang untuk memasukkan nomor seperti 123.122yang tidak sempurna untuk uang.

Adakah cara untuk membatasi jumlah karakter setelah koma desimal menjadi dua?

Konstantin Weitz
sumber
Anda dapat menulis ekspresi reguler dan memverifikasi konten teks edit ketika kehilangan fokus.
penutup mata
Saya menemukan InputFilterantarmuka, sepertinya melakukan apa yang saya inginkan developer.android.com/reference/android/text/method/… , tetapi metode filteryang harus saya terapkan agak membingungkan saya. Apakah seseorang sudah menulis Filter seperti itu dan tahu cara menggunakannya?
Konstantin Weitz
Apakah ada solusi yang disarankan berfungsi untuk lokal RTL? Sejauh yang saya tahu mereka tidak akan ...
Nick

Jawaban:

118

Cara yang lebih elegan adalah menggunakan ekspresi reguler (regex) sebagai berikut:

public class DecimalDigitsInputFilter implements InputFilter {

Pattern mPattern;

public DecimalDigitsInputFilter(int digitsBeforeZero,int digitsAfterZero) {
    mPattern=Pattern.compile("[0-9]{0," + (digitsBeforeZero-1) + "}+((\\.[0-9]{0," + (digitsAfterZero-1) + "})?)||(\\.)?");
}

@Override
public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {

        Matcher matcher=mPattern.matcher(dest);       
        if(!matcher.matches())
            return "";
        return null;
    }

}

Untuk menggunakannya lakukan:

editText.setFilters(new InputFilter[] {new DecimalDigitsInputFilter(5,2)});
Asaf Pinhassi
sumber
32
Hai, ada beberapa kasus tepi yang masih belum ditangani dengan baik. Misalnya, setelah saya mengetik 2.45, saya cenderung "memindahkan kursor ke sebagian besar teks". Saya ingin menghasilkan teks 12.45, itu tidak mengizinkan.
Cheok Yan Cheng
3
itu tidak memungkinkan untuk mengubah digit sebelum desimal setelah pengguna memasukkan 2 digit setelah koma desimal.
Gaurav Singla
9
Solusi hebat tetapi tidak sepenuhnya benar. Pencocok seharusnya tidak memeriksa tujuan, itu harus memeriksa nilai dalam teks edittext (dest.subSequence (0, dstart) + source.subSequence (start, end) + dest.subSequence (dend, dest.length ()))
Mihaela Romanca
7
Mihaela benar, kita harus mencocokkan dengan string yang mencoba mengisi teks editan. Saya menemukan cara menggabungkan pertanyaan lain seperti ini CharSequence match = TextUtils.concat (dest.subSequence (0, dstart), source.subSequence (start, end), dest.subSequence (dend, dest.length ())); Namun, ekspresi reguler menyebabkan masalah setelahnya, jadi saya mengubahnya menjadi "^ \\ d {1", + digitsBeforeZero + "} (\\. \\ d {0," + digitsAfterZero + "})? $" Tapi Anda ' Anda juga harus melakukan validasi setelahnya karena "1." valid dengan regex itu tetapi kita membutuhkannya dengan cara itu sehingga titik dapat diketik.
dt0
6
Bagaimana Anda akan membuat ini berfungsi untuk koma (,) juga? Beberapa wilayah di dunia mengetikkan angka desimal dengan koma (contoh: 123,45).
Andrew
65

Solusi yang lebih sederhana tanpa menggunakan regex:

import android.text.InputFilter;
import android.text.Spanned;

/**
 * Input filter that limits the number of decimal digits that are allowed to be
 * entered.
 */
public class DecimalDigitsInputFilter implements InputFilter {

  private final int decimalDigits;

  /**
   * Constructor.
   * 
   * @param decimalDigits maximum decimal digits
   */
  public DecimalDigitsInputFilter(int decimalDigits) {
    this.decimalDigits = decimalDigits;
  }

  @Override
  public CharSequence filter(CharSequence source,
      int start,
      int end,
      Spanned dest,
      int dstart,
      int dend) {


    int dotPos = -1;
    int len = dest.length();
    for (int i = 0; i < len; i++) {
      char c = dest.charAt(i);
      if (c == '.' || c == ',') {
        dotPos = i;
        break;
      }
    }
    if (dotPos >= 0) {

      // protects against many dots
      if (source.equals(".") || source.equals(","))
      {
          return "";
      }
      // if the text is entered before the dot
      if (dend <= dotPos) {
        return null;
      }
      if (len - dotPos > decimalDigits) {
        return "";
      }
    }

    return null;
  }

}

Menggunakan:

editText.setFilters(new InputFilter[] {new DecimalDigitsInputFilter(2)});
peceps
sumber
Apa yang akan menghentikan saya untuk memasukkan karakter non numerik ke string seperti 'a'?
Konstantin Weitz
4
Ini: <EditText ... android: inputType = "number" />
peceps
1
Itu seharusnya: editText.setFilters (new InputFilter [] {new DecimalDigitsInputFilter (2)});
frak
6
Ini tidak menangani kasus di mana saya mengetik "999" dan kemudian memasukkan koma desimal setelah 9. pertama.
Jake Stoeffler
1
Terima kasih ini berguna. Kami juga dapat membatasi angka sebelum titik desimal menggunakan filter panjang seperti ini. Kotlin: edtAnyAmount.filters = arrayOf <InputFilter> (InputFilter.LengthFilter (7), DecimalDigitsInputFilter (2))
Faldu Jaldeep
37

Implementasi ini InputFiltermemecahkan masalah.

import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.method.DigitsKeyListener;

public class MoneyValueFilter extends DigitsKeyListener {
    public MoneyValueFilter() {
        super(false, true);
    }

    private int digits = 2;

    public void setDigits(int d) {
        digits = d;
    }

    @Override
    public CharSequence filter(CharSequence source, int start, int end,
            Spanned dest, int dstart, int dend) {
        CharSequence out = super.filter(source, start, end, dest, dstart, dend);

        // if changed, replace the source
        if (out != null) {
            source = out;
            start = 0;
            end = out.length();
        }

        int len = end - start;

        // if deleting, source is empty
        // and deleting can't break anything
        if (len == 0) {
            return source;
        }

        int dlen = dest.length();

        // Find the position of the decimal .
        for (int i = 0; i < dstart; i++) {
            if (dest.charAt(i) == '.') {
                // being here means, that a number has
                // been inserted after the dot
                // check if the amount of digits is right
                return (dlen-(i+1) + len > digits) ? 
                    "" :
                    new SpannableStringBuilder(source, start, end);
            }
        }

        for (int i = start; i < end; ++i) {
            if (source.charAt(i) == '.') {
                // being here means, dot has been inserted
                // check if the amount of digits is right
                if ((dlen-dend) + (end-(i + 1)) > digits)
                    return "";
                else
                    break;  // return new SpannableStringBuilder(source, start, end);
            }
        }

        // if the dot is after the inserted part,
        // nothing can break
        return new SpannableStringBuilder(source, start, end);
    }
}
Konstantin Weitz
sumber
Bolehkah saya tahu apakah ada alasan mengapa kita perlu mengembalikan SpannableStringBuilder daripada null? Saya mengujinya dengan nol, itu bekerja dengan baik juga. Juga, apakah kita perlu mewarisi dari DigitsKeyListener? Karena menggunakan android: inputType = "numberDecimal" akan melakukan semua "0123456789." penegakan karakter.
Cheok Yan Cheng
1
Bekerja dengan baik. Terima kasih banyak.
Andrei Aulaska
34

Berikut adalah contoh InputFilter yang hanya memperbolehkan maksimal 4 digit sebelum koma desimal dan maksimal 1 digit setelah itu.

Nilai yang diizinkan teks edit: 555.2 , 555 , .2

Nilai yang blok edittext: 55555.2 , 055.2 , 555.42

        InputFilter filter = new InputFilter() {
        final int maxDigitsBeforeDecimalPoint=4;
        final int maxDigitsAfterDecimalPoint=1;

        @Override
        public CharSequence filter(CharSequence source, int start, int end,
                Spanned dest, int dstart, int dend) {
                StringBuilder builder = new StringBuilder(dest);
                builder.replace(dstart, dend, source
                        .subSequence(start, end).toString());
                if (!builder.toString().matches(
                        "(([1-9]{1})([0-9]{0,"+(maxDigitsBeforeDecimalPoint-1)+"})?)?(\\.[0-9]{0,"+maxDigitsAfterDecimalPoint+"})?"

                        )) {
                    if(source.length()==0)
                        return dest.subSequence(dstart, dend);
                    return "";
                }

            return null;

        }
    };

    mEdittext.setFilters(new InputFilter[] { filter });
Favas Kv
sumber
tidak membiarkan. untuk diketik
AmiNadimi
22

Saya membuat beberapa perbaikan untuk solusi @Pinhassi. Ini menangani beberapa kasus:

1.Anda dapat memindahkan kursor ke mana saja

2. penanganan tanda minus

3. digit sebelum = 2 dan digit setelah = 4 dan Anda memasukkan 12.4545. Kemudian jika Anda ingin menghapus ".", Itu tidak akan mengizinkan.

public class DecimalDigitsInputFilter implements InputFilter {
    private int mDigitsBeforeZero;
    private int mDigitsAfterZero;
    private Pattern mPattern;

    private static final int DIGITS_BEFORE_ZERO_DEFAULT = 100;
    private static final int DIGITS_AFTER_ZERO_DEFAULT = 100;

    public DecimalDigitsInputFilter(Integer digitsBeforeZero, Integer digitsAfterZero) {
    this.mDigitsBeforeZero = (digitsBeforeZero != null ? digitsBeforeZero : DIGITS_BEFORE_ZERO_DEFAULT);
    this.mDigitsAfterZero = (digitsAfterZero != null ? digitsAfterZero : DIGITS_AFTER_ZERO_DEFAULT);
    mPattern = Pattern.compile("-?[0-9]{0," + (mDigitsBeforeZero) + "}+((\\.[0-9]{0," + (mDigitsAfterZero)
        + "})?)||(\\.)?");
    }

    @Override
    public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {
    String replacement = source.subSequence(start, end).toString();
    String newVal = dest.subSequence(0, dstart).toString() + replacement
        + dest.subSequence(dend, dest.length()).toString();
    Matcher matcher = mPattern.matcher(newVal);
    if (matcher.matches())
        return null;

    if (TextUtils.isEmpty(source))
        return dest.subSequence(dstart, dend);
    else
        return "";
    }
}
android_dev
sumber
Saya pikir itu harus menjadi solusi yang tepat, membuat hari saya. Terima kasih.
Pratik Butani
1
@Omkar ini salah. kondisi ini akan selalu benar meskipun panjang> 0, tujuan.length () == 0 selalu benar meskipun Anda mengedit teks keseluruhan lebih dari 0 ...
pengguna924
@Omkar hapus komentar Anda
tolong
@android_dev mengapa saya tidak bisa mengetik nilai negatif (minus)?
pengguna924
JIKA Anda menyetel android:inputType="number"atau android:inputType="numberDecimal"tidak mengizinkan untuk mengetik minus, ini android:digits="0123456789.-"tidak membantu
user924
18

Saya tidak suka solusi lain dan saya membuatnya sendiri. Dengan solusi ini, Anda tidak boleh memasukkan lebih dari MAX_BEFORE_POINT digit sebelum titik dan desimal tidak boleh lebih dari MAX_DECIMAL.

Anda tidak bisa mengetik digit secara berlebihan, tidak ada efek lain! Sebagai tambahan jika Anda menulis "." itu mengetik "0"

  1. Setel EditText dalam tata letak ke:

    android: inputType = "numberDecimal"

  2. Tambahkan Pendengar di onCreate Anda. Jika Anda ingin mengubah jumlah digit sebelum dan sesudah titik, edit panggilan ke PerfectDecimal (str, NUMBER_BEFORE_POINT, NUMBER_DECIMALS), di sini diatur ke 3 dan 2

    EditText targetEditText = (EditText)findViewById(R.id.targetEditTextLayoutId);
    
    targetEditText.addTextChangedListener(new TextWatcher() {
      public void onTextChanged(CharSequence arg0, int arg1, int arg2, int arg3) {}
    
      public void beforeTextChanged(CharSequence arg0, int arg1, int arg2, int arg3) {}
    
      public void afterTextChanged(Editable arg0) {
        String str = targetEditText.getText().toString();
        if (str.isEmpty()) return;
        String str2 = PerfectDecimal(str, 3, 2);
    
        if (!str2.equals(str)) {
            targetEditText.setText(str2);
            int pos = targetEditText.getText().length();
            targetEditText.setSelection(pos);
        }
      }
    });
  3. Sertakan Fungsi ini:

    public String PerfectDecimal(String str, int MAX_BEFORE_POINT, int MAX_DECIMAL){
      if(str.charAt(0) == '.') str = "0"+str;
      int max = str.length();
    
      String rFinal = "";
      boolean after = false;
      int i = 0, up = 0, decimal = 0; char t;
      while(i < max){
        t = str.charAt(i);
        if(t != '.' && after == false){
            up++;
            if(up > MAX_BEFORE_POINT) return rFinal;
        }else if(t == '.'){
            after = true;
        }else{
            decimal++;
            if(decimal > MAX_DECIMAL)
                return rFinal;
        }
        rFinal = rFinal + t;
        i++;
      }return rFinal;
    }

Dan selesai!

Apoleo
sumber
1
Bersulang. Bekerja dengan sangat baik ketika yang lain tidak berhasil untuk saya
Beruang
1
Ini harus menjadi Jawaban yang Diterima .. Sempurna .. Semua syarat terpenuhi di sini.
Nayan
1
Di atas semua jawaban yang paling banyak dipilih, jawaban ini benar-benar berhasil untuk saya.
Rohit Mandiwal
Sudah selesai dilakukan dengan baik! Saya mencoba semua kombinasi dan tampaknya berhasil dengan baik. Terima kasih.
akelec
Saya tidak tahu cara kerjanya, tetapi itu hanya berfungsi seperti pesona.
wonsuc
17

Saya mencapai ini dengan bantuan TextWatcherdengan cara berikut

final EditText et = (EditText) findViewById(R.id.EditText1);
int count = -1;
et.addTextChangedListener(new TextWatcher() {
    public void onTextChanged(CharSequence arg0, int arg1, int arg2,int arg3) {             

    }
    public void beforeTextChanged(CharSequence arg0, int arg1,int arg2, int arg3) {             

    }

    public void afterTextChanged(Editable arg0) {
        if (arg0.length() > 0) {
            String str = et.getText().toString();
            et.setOnKeyListener(new OnKeyListener() {
                public boolean onKey(View v, int keyCode, KeyEvent event) {
                    if (keyCode == KeyEvent.KEYCODE_DEL) {
                        count--;
                        InputFilter[] fArray = new InputFilter[1];
                        fArray[0] = new InputFilter.LengthFilter(100);
                        et.setFilters(fArray);
                        //change the edittext's maximum length to 100. 
                        //If we didn't change this the edittext's maximum length will
                        //be number of digits we previously entered.
                    }
                    return false;
                }
            });
            char t = str.charAt(arg0.length() - 1);
            if (t == '.') {
                count = 0;
            }
            if (count >= 0) {
                if (count == 2) {                        
                    InputFilter[] fArray = new InputFilter[1];
                    fArray[0] = new InputFilter.LengthFilter(arg0.length());
                    et.setFilters(fArray);
                    //prevent the edittext from accessing digits 
                    //by setting maximum length as total number of digits we typed till now.
                }
                count++;
            }
        }
    }
});

Solusi ini tidak akan mengizinkan pengguna memasukkan lebih dari dua digit setelah koma desimal. Anda juga dapat memasukkan sejumlah digit sebelum titik desimal. Lihat blog ini http://v4all123.blogspot.com/2013/05/set-limit-for-fraction-in-decimal.html untuk menyetel filter untuk beberapa EditText. Saya harap ini akan membantu. Terima kasih.

Gunaseelan
sumber
Maaf untuk info terlambat. Jangan lupa untuk menginisialisasi countdengan -1. Maka hanya ini yang akan bekerja dengan benar. int count = -1;
Gunaseelan
Gunaseelan - Saya mencoba kode di atas dan bekerja dengan baik. Tetapi ketika saya menghapus teks yang diketik dan mulai mengetik lagi mengetik hanya satu digit, solusi apa pun untuk ini .....
Siva K
@SivaK Tidak mungkin teman. Jika Anda menghapus dan kemudian mengetik itu akan menerima minimal 100 digit. Saya tidak tahu bagaimana Anda mengakses ini listener. Bagaimanapun, silakan lihat posting blog saya . Anda mungkin mendapat ide. Jika Anda tidak bisa, tolong beri tahu saya. Saya akan membantu Anda mengatasi masalah ini.
Gunaseelan
saya telah memverifikasi apa yang dikatakan @SivaK. ini pintar dalam hal apa pun tetapi saya akan melakukan beberapa pengeditan untuk ini sehingga berfungsi penuh (menurut saya)
MrTristan
@Gunaseelan terima kasih atas solusinya. Tetapi ada beberapa bug. Misalnya, ketika saya menghapus desimal kedua, tidak mungkin mengetiknya lagi (saya harus menghapus semua desimal agar dapat mengetiknya lagi). Juga, setelah penghapusan seluruh entri, beberapa batasan aneh terjadi saat mengetik lagi.
akelec
14

InputFilter yang saya buat memungkinkan Anda untuk mengkonfigurasi jumlah digit sebelum dan sesudah tempat desimal. Selain itu, ini melarang nol di depan.

public class DecimalDigitsInputFilter implements InputFilter
{
    Pattern pattern;

    public DecimalDigitsInputFilter(int digitsBeforeDecimal, int digitsAfterDecimal)
    {
        pattern = Pattern.compile("(([1-9]{1}[0-9]{0," + (digitsBeforeDecimal - 1) + "})?||[0]{1})((\\.[0-9]{0," + digitsAfterDecimal + "})?)||(\\.)?");
    }

    @Override public CharSequence filter(CharSequence source, int sourceStart, int sourceEnd, Spanned destination, int destinationStart, int destinationEnd)
    {
        // Remove the string out of destination that is to be replaced.
        String newString = destination.toString().substring(0, destinationStart) + destination.toString().substring(destinationEnd, destination.toString().length());

        // Add the new string in.
        newString = newString.substring(0, destinationStart) + source.toString() + newString.substring(destinationStart, newString.length());

        // Now check if the new string is valid.
        Matcher matcher = pattern.matcher(newString);

        if(matcher.matches())
        {
            // Returning null indicates that the input is valid.
            return null;
        }

        // Returning the empty string indicates the input is invalid.
        return "";
    }
}

// To use this InputFilter, attach it to your EditText like so:
final EditText editText = (EditText) findViewById(R.id.editText);

EditText.setFilters(new InputFilter[]{new DecimalDigitsInputFilter(4, 4)});
Luke
sumber
Solusi bagus! Ini berfungsi untuk saya, tetapi saya ingin melarang titik utama (titik). Misalnya, ".123" tidak diperbolehkan berurutan. Bagaimana cara mencapai ini?
ibogolyubskiy
13

Persyaratannya adalah 2 digit setelah desimal. Seharusnya tidak ada batasan untuk angka sebelum koma desimal. Jadi, solusinya harus,

public class DecimalDigitsInputFilter implements InputFilter {

    Pattern mPattern;

    public DecimalDigitsInputFilter() {
        mPattern = Pattern.compile("[0-9]*+((\\.[0-9]?)?)||(\\.)?");
    }

    @Override
    public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {
        Matcher matcher = mPattern.matcher(dest);
        if (!matcher.matches())
            return "";
        return null;
    }
}

Dan gunakan sebagai,

mEditText.setFilters(new InputFilter[]{new DecimalDigitsInputFilter()});

Terima kasih kepada @Pinhassi atas inspirasinya.

Mangesh
sumber
Bagus ... Bekerja dengan baik
jojo
12

Solusi saya sederhana dan berfungsi sempurna!

public class DecimalInputTextWatcher implements TextWatcher {

private String mPreviousValue;
private int mCursorPosition;
private boolean mRestoringPreviousValueFlag;
private int mDigitsAfterZero;
private EditText mEditText;

public DecimalInputTextWatcher(EditText editText, int digitsAfterZero) {
    mDigitsAfterZero = digitsAfterZero;
    mEditText = editText;
    mPreviousValue = "";
    mRestoringPreviousValueFlag = false;
}

@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
    if (!mRestoringPreviousValueFlag) {
        mPreviousValue = s.toString();
        mCursorPosition = mEditText.getSelectionStart();
    }
}

@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}

@Override
public void afterTextChanged(Editable s) {
    if (!mRestoringPreviousValueFlag) {

        if (!isValid(s.toString())) {
            mRestoringPreviousValueFlag = true;
            restorePreviousValue();
        }

    } else {
        mRestoringPreviousValueFlag = false;
    }
}

private void restorePreviousValue() {
    mEditText.setText(mPreviousValue);
    mEditText.setSelection(mCursorPosition);
}

private boolean isValid(String s) {
    Pattern patternWithDot = Pattern.compile("[0-9]*((\\.[0-9]{0," + mDigitsAfterZero + "})?)||(\\.)?");
    Pattern patternWithComma = Pattern.compile("[0-9]*((,[0-9]{0," + mDigitsAfterZero + "})?)||(,)?");

    Matcher matcherDot = patternWithDot.matcher(s);
    Matcher matcherComa = patternWithComma.matcher(s);

    return matcherDot.matches() || matcherComa.matches();
}
}

Pemakaian:

myTextEdit.addTextChangedListener(new DecimalInputTextWatcher(myTextEdit, 2));
Dominik Suszczewicz
sumber
Pindahkan pola dari satu isValid()konstruktor untuk menghindari pembuatan ulang pola pada setiap isValid()panggilan.
Hemant Kaushik
6

Coba gunakan NumberFormat.getCurrencyInstance () untuk memformat string Anda sebelum Anda memasukkannya ke dalam TextView.

Sesuatu seperti:

NumberFormat currency = NumberFormat.getCurrencyInstance();
myTextView.setText(currency.format(dollars));

Sunting - Tidak ada inputType untuk mata uang yang dapat saya temukan di dokumen. Saya membayangkan ini karena ada beberapa mata uang yang tidak mengikuti aturan yang sama untuk tempat desimal, seperti Yen Jepang.

Seperti yang disebutkan LeffelMania, Anda dapat mengoreksi input pengguna dengan menggunakan kode di atas dengan TextWatcheryang disetel di EditText.

Matthew Willis
sumber
6

Solusi @Pinhassi sedikit ditingkatkan.

Bekerja dengan sangat baik. Ini memvalidasi string yang digabungkan.

public class DecimalDigitsInputFilter implements InputFilter {

Pattern mPattern;

public DecimalDigitsInputFilter() {
    mPattern = Pattern.compile("([1-9]{1}[0-9]{0,2}([0-9]{3})*(\\.[0-9]{0,2})?|[1-9]{1}[0-9]{0,}(\\.[0-9]{0,2})?|0(\\.[0-9]{0,2})?|(\\.[0-9]{1,2})?)");

}

@Override
public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {

    String formatedSource = source.subSequence(start, end).toString();

    String destPrefix = dest.subSequence(0, dstart).toString();

    String destSuffix = dest.subSequence(dend, dest.length()).toString();

    String result = destPrefix + formatedSource + destSuffix;

    result = result.replace(",", ".");

    Matcher matcher = mPattern.matcher(result);

    if (matcher.matches()) {
        return null;
    }

    return "";
}

 }
piksel
sumber
6

Saya telah memodifikasi solusi di atas dan membuat yang berikut. Anda dapat mengatur jumlah digit sebelum dan sesudah koma desimal.

public class DecimalDigitsInputFilter implements InputFilter {

private final Pattern mPattern;

public DecimalDigitsInputFilter(int digitsBeforeZero, int digitsAfterZero) {
    mPattern = Pattern.compile(String.format("[0-9]{0,%d}(\\.[0-9]{0,%d})?", digitsBeforeZero, digitsAfterZero));
}

@Override
public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {
    Matcher matcher = mPattern.matcher(createResultString(source, start, end, dest, dstart, dend));
    if (!matcher.matches())
        return "";
    return null;
}

private String createResultString(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {
    String sourceString = source.toString();
    String destString = dest.toString();
    return destString.substring(0, dstart) + sourceString.substring(start, end) + destString.substring(dend);
}

}

tmorcinek.dll
sumber
Ini hampir apa yang reisub telah menjawab untuk pertanyaan yang sama di sini pada tahun 2014.
Mehul Joisar
5
DecimalFormat form = new DecimalFormat("#.##", new DecimalFormatSymbols(Locale.US));
    EditText et; 
    et.setOnEditorActionListener(new TextView.OnEditorActionListener() {
        @Override
        public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {

        if (actionId == EditorInfo.IME_ACTION_DONE) {
            double a = Double.parseDouble(et.getText().toString());
            et.setText(form.format(a));
        }
        return false;
    }
});

Apa yang dilakukan ini adalah ketika Anda keluar dari fase pengeditan, ini memformat bidang ke format yang benar. Pada saat itu hanya memiliki 2 karakter desimal. Saya rasa ini cara yang cukup mudah untuk melakukan ini.

Sipka
sumber
4

Semua jawaban di sini cukup rumit. Saya mencoba membuatnya lebih sederhana. Lihat kode saya dan putuskan sendiri -

int temp  = 0;
int check = 0;

editText.addTextChangedListener(new TextWatcher() {

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

        if(editText.getText().toString().length()<temp)
        {
            if(!editText.getText().toString().contains("."))
                editText.setFilters(new InputFilter[] { new InputFilter.LengthFilter(editText.getText().toString().length()-1) });
            else
                editText.setFilters(new InputFilter[] { new InputFilter.LengthFilter(editText.getText().toString().length()+1) });

        }

        if(!editText.getText().toString().contains("."))
        {
            editText.setFilters(new InputFilter[] { new InputFilter.LengthFilter(editText.getText().toString().length()+1) });
            check=0;
        }


        else if(check==0)
        {
            check=1;
            editText.setFilters(new InputFilter[] { new InputFilter.LengthFilter(editText.getText().toString().length()+2) });
        }
    }

    @Override
    public void beforeTextChanged(CharSequence s, int start, int count,
            int after) {
        temp = editText.getText().toString().length();


    }

    @Override
    public void afterTextChanged(Editable s) {
        // TODO Auto-generated method stub

    }
});
mjosh
sumber
itu bekerja sempurna untuk saya. Saya memeriksa semua skenario. Terima kasih.
Amarnath Baitha
Misalkan saya telah memasukkan 1234.56, sekarang saya ingin mengeditnya seperti ini 12378.56, saya tidak dapat melakukannya tanpa menghapus desimal.
Aman Verma
4

Saya sangat menyukai jawaban Pinhassi, tetapi perhatikan bahwa setelah pengguna memasukkan digit angka yang ditentukan setelah koma, Anda tidak dapat lagi memasukkan teks ke sisi kiri koma desimal. Masalahnya adalah solusinya hanya menguji teks sebelumnya yang telah dimasukkan, bukan teks saat ini yang dimasukkan. Jadi inilah solusi saya yang memasukkan karakter baru ke dalam teks asli untuk validasi.

package com.test.test;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import android.text.InputFilter;
import android.text.Spanned;
import android.util.Log;

public class InputFilterCurrency implements InputFilter {
    Pattern moPattern;

    public InputFilterCurrency(int aiMinorUnits) {
        // http://www.regexplanet.com/advanced/java/index.html
        moPattern=Pattern.compile("[0-9]*+((\\.[0-9]{0,"+ aiMinorUnits + "})?)||(\\.)?");

    } // InputFilterCurrency

    @Override
    public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {
        String lsStart  = "";
        String lsInsert = "";
        String lsEnd    = "";
        String lsText   = "";

        Log.d("debug", moPattern.toString());
        Log.d("debug", "source: " + source + ", start: " + start + ", end:" + end + ", dest: " + dest + ", dstart: " + dstart + ", dend: " + dend );

        lsText = dest.toString();

        // If the length is greater then 0, then insert the new character
        // into the original text for validation
        if (lsText.length() > 0) {

            lsStart = lsText.substring(0, dstart);
            Log.d("debug", "lsStart : " + lsStart);
            // Check to see if they have deleted a character
            if (source != "") {
                lsInsert = source.toString();
                Log.d("debug", "lsInsert: " + lsInsert);
            } // if
            lsEnd = lsText.substring(dend);
            Log.d("debug", "lsEnd   : " + lsEnd);
            lsText = lsStart + lsInsert + lsEnd;
            Log.d("debug", "lsText  : " + lsText);

        } // if

        Matcher loMatcher = moPattern.matcher(lsText);
        Log.d("debug", "loMatcher.matches(): " + loMatcher.matches() + ", lsText: " + lsText);
        if(!loMatcher.matches()) {
            return "";
        }
        return null;

    } // CharSequence

} // InputFilterCurrency

Dan panggilan untuk menyetel filter editText

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

Ouput with two decimal places
05-22 15:25:33.434: D/debug(30524): [0-9]*+((\.[0-9]{0,2})?)||(\.)?
05-22 15:25:33.434: D/debug(30524): source: 5, start: 0, end:1, dest: 123.4, dstart: 5, dend: 5
05-22 15:25:33.434: D/debug(30524): lsStart : 123.4
05-22 15:25:33.434: D/debug(30524): lsInsert: 5
05-22 15:25:33.434: D/debug(30524): lsEnd   : 
05-22 15:25:33.434: D/debug(30524): lsText  : 123.45
05-22 15:25:33.434: D/debug(30524): loMatcher.matches(): true, lsText: 123.45

Ouput inserting a 5 in the middle
05-22 15:26:17.624: D/debug(30524): [0-9]*+((\.[0-9]{0,2})?)||(\.)?
05-22 15:26:17.624: D/debug(30524): source: 5, start: 0, end:1, dest: 123.45, dstart: 2, dend: 2
05-22 15:26:17.624: D/debug(30524): lsStart : 12
05-22 15:26:17.624: D/debug(30524): lsInsert: 5
05-22 15:26:17.624: D/debug(30524): lsEnd   : 3.45
05-22 15:26:17.624: D/debug(30524): lsText  : 1253.45
05-22 15:26:17.624: D/debug(30524): loMatcher.matches(): true, lsText: 1253.45
David Murry
sumber
4

Saya meningkatkan solusi yang menggunakan regex oleh Pinhassi sehingga juga menangani kasus tepi dengan benar. Sebelum memeriksa apakah masukan sudah benar, pertama-tama string terakhir dibuat seperti yang dijelaskan oleh dokumen android.

public class DecimalDigitsInputFilter implements InputFilter {

    private Pattern mPattern;

    private static final Pattern mFormatPattern = Pattern.compile("\\d+\\.\\d+");

    public DecimalDigitsInputFilter(int digitsBeforeDecimal, int digitsAfterDecimal) {
        mPattern = Pattern.compile(
            "^\\d{0," + digitsBeforeDecimal + "}([\\.,](\\d{0," + digitsAfterDecimal +
                "})?)?$");
    }

    @Override
    public CharSequence filter(CharSequence source, int start, int end, Spanned dest, 
                               int dstart, int dend) {

        String newString =
            dest.toString().substring(0, dstart) + source.toString().substring(start, end) 
            + dest.toString().substring(dend, dest.toString().length());

        Matcher matcher = mPattern.matcher(newString);
        if (!matcher.matches()) {
            return "";
        }
        return null;
    }
}

Pemakaian:

editText.setFilters(new InputFilter[] {new DecimalDigitsInputFilter(5,2)});
reisub
sumber
4

Kelas Simple Helper ada di sini untuk mencegah pengguna memasukkan lebih dari 2 digit setelah desimal:

public class CostFormatter  implements TextWatcher {

private final EditText costEditText;

public CostFormatter(EditText costEditText) {
    this.costEditText = costEditText;
}

@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 synchronized void afterTextChanged(final Editable text) {
    String cost = text.toString().trim();

    if(!cost.endsWith(".") && cost.contains(".")){
        String numberBeforeDecimal = cost.split("\\.")[0];
        String numberAfterDecimal = cost.split("\\.")[1];

        if(numberAfterDecimal.length() > 2){
            numberAfterDecimal = numberAfterDecimal.substring(0, 2);
        }
        cost = numberBeforeDecimal + "." + numberAfterDecimal;
    }
    costEditText.removeTextChangedListener(this);
    costEditText.setText(cost);
    costEditText.setSelection(costEditText.getText().toString().trim().length());
    costEditText.addTextChangedListener(this);
}
}
Santhosh
sumber
4

Saya telah mengubah jawaban №6 (oleh Favas Kv) karena di sana Anda dapat menempatkan poin di posisi pertama.

final InputFilter [] filter = { new InputFilter() {

    @Override
    public CharSequence filter(CharSequence source, int start, int end,
                               Spanned dest, int dstart, int dend) {
        StringBuilder builder = new StringBuilder(dest);
        builder.replace(dstart, dend, source
                .subSequence(start, end).toString());
        if (!builder.toString().matches(
                "(([1-9]{1})([0-9]{0,4})?(\\.)?)?([0-9]{0,2})?"

        )) {
            if(source.length()==0)
                return dest.subSequence(dstart, dend);
            return "";
        }
        return null;
    }
}};
koa73
sumber
3

Seperti yang dikatakan orang lain, saya menambahkan kelas ini dalam proyek saya dan menyetel filter ke EditText saya inginkan.

Filter disalin dari jawaban @ Pixel. Saya hanya menyatukan semuanya.

public class DecimalDigitsInputFilter implements InputFilter {

    Pattern mPattern;

    public DecimalDigitsInputFilter() {
        mPattern = Pattern.compile("([1-9]{1}[0-9]{0,2}([0-9]{3})*(\\.[0-9]{0,2})?|[1-9]{1}[0-9]{0,}(\\.[0-9]{0,2})?|0(\\.[0-9]{0,2})?|(\\.[0-9]{1,2})?)");

    }

    @Override
    public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {

        String formatedSource = source.subSequence(start, end).toString();

        String destPrefix = dest.subSequence(0, dstart).toString();

        String destSuffix = dest.subSequence(dend, dest.length()).toString();

        String result = destPrefix + formatedSource + destSuffix;

        result = result.replace(",", ".");

        Matcher matcher = mPattern.matcher(result);

        if (matcher.matches()) {
            return null;
        }

        return "";
    }
}

Sekarang atur filter EditTextseperti ini.

mEditText.setFilters(new InputFilter[]{new DecimalDigitsInputFilter()});

Di sini satu hal penting adalah itu memecahkan masalah saya karena tidak mengizinkan menampilkan lebih dari dua digit setelah titik desimal di dalamnya EditTexttetapi masalahnya adalah ketika saya getText()dari itu EditText, ia mengembalikan seluruh input yang saya ketikkan.

Misalnya, setelah menerapkan filter di atas EditText, saya mencoba menyetel input 1.5699856987. Jadi di layar itu menunjukkan 1,56 yang sempurna.

Kemudian saya ingin menggunakan input ini untuk beberapa kalkulasi lain, jadi saya ingin mendapatkan teks dari kolom input itu (EditText ). Ketika saya memanggilnya mEditText.getText().toString()mengembalikan 1,5699856987 yang tidak dapat diterima dalam kasus saya.

Jadi saya harus mengurai nilainya lagi setelah mendapatkannya dari EditText.

BigDecimal amount = new BigDecimal(Double.parseDouble(mEditText.getText().toString().trim()))
    .setScale(2, RoundingMode.HALF_UP);

setScalemelakukan trik di sini setelah mendapatkan teks lengkap dari EditText.

Reaz Murshed
sumber
Halo, bagaimana saya bisa memastikan jika pengguna tidak memasukkan desimal (.) Dia tidak boleh memasukkan lebih dari 2 digit?
ManishNegi
2

Saya juga menemukan masalah ini. Saya ingin dapat menggunakan kembali kode tersebut di banyak EditTexts. Ini solusi saya:

Penggunaan:

CurrencyFormat watcher = new CurrencyFormat();
priceEditText.addTextChangedListener(watcher);

Kelas:

public static class CurrencyFormat implements TextWatcher {

    public void onTextChanged(CharSequence arg0, int start, int arg2,int arg3) {}

    public void beforeTextChanged(CharSequence arg0, int start,int arg2, int arg3) {}

    public void afterTextChanged(Editable arg0) {
        int length = arg0.length();
        if(length>0){
            if(nrOfDecimal(arg0.toString())>2)
                    arg0.delete(length-1, length);
        }

    }


    private int nrOfDecimal(String nr){
        int len = nr.length();
        int pos = len;
        for(int i=0 ; i<len; i++){
            if(nr.charAt(i)=='.'){
                pos=i+1;
                    break;
            }
        }
        return len-pos;
    }
}
Tom
sumber
2

@Meh untukmu ..

txtlist.setFilters(new InputFilter[] { new DigitsKeyListener( Boolean.FALSE,Boolean.TRUE) {

        int beforeDecimal = 7;
        int afterDecimal = 2;

        @Override
        public CharSequence filter(CharSequence source, int start, int end,Spanned dest, int dstart, int dend) {

            String etText = txtlist.getText().toString();
            String temp = txtlist.getText() + source.toString();
            if (temp.equals(".")) {
                return "0.";
            } else if (temp.toString().indexOf(".") == -1) {
                // no decimal point placed yet
                 if (temp.length() > beforeDecimal) {
                    return "";
                }
            } else {
                int dotPosition ;
                int cursorPositon = txtlistprice.getSelectionStart();
                if (etText.indexOf(".") == -1) {
                    dotPosition = temp.indexOf(".");
                }else{
                    dotPosition = etText.indexOf(".");
                }
                if(cursorPositon <= dotPosition){
                    String beforeDot = etText.substring(0, dotPosition);
                    if(beforeDot.length()<beforeDecimal){
                        return source;
                    }else{
                        if(source.toString().equalsIgnoreCase(".")){
                            return source;
                        }else{
                            return "";
                        }
                    }
                }else{
                    temp = temp.substring(temp.indexOf(".") + 1);
                    if (temp.length() > afterDecimal) {
                        return "";
                    }
                }
            }
            return super.filter(source, start, end, dest, dstart, dend);
        }
    } });
RaRa
sumber
2

Tanggapan yang sangat terlambat: Kita bisa melakukannya seperti ini:

etv.addTextChangedListener(new TextWatcher() {
        @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().length() > 3 && s.toString().contains(".")) {
                if (s.toString().length() - s.toString().indexOf(".") > 3) {
                    etv.setText(s.toString().substring(0, s.length() - 1));
                    etv.setSelection(edtSendMoney.getText().length());
                }
            }
        }

        @Override
        public void afterTextChanged(Editable arg0) {
        }
}
NanPd
sumber
2

Berikut adalah TextWatcheryang hanya mengizinkan n jumlah digit setelah koma desimal.

TextWatcher

private static boolean flag;
public static TextWatcher getTextWatcherAllowAfterDeci(final int allowAfterDecimal){

    TextWatcher watcher = new TextWatcher() {

        @Override
        public void onTextChanged(CharSequence s, int start, int before, int count) {
            // TODO Auto-generated method stub

        }

        @Override
        public void beforeTextChanged(CharSequence s, int start, int count,
                int after) {
            // TODO Auto-generated method stub

        }

        @Override
        public void afterTextChanged(Editable s) {
            // TODO Auto-generated method stub
            String str = s.toString();
            int index = str.indexOf ( "." );
            if(index>=0){
                if((index+1)<str.length()){
                    String numberD = str.substring(index+1);
                    if (numberD.length()!=allowAfterDecimal) {
                        flag=true;
                    }else{
                        flag=false;
                    }   
                }else{
                    flag = false;
                }                   
            }else{
                flag=false;
            }
            if(flag)
                s.delete(s.length() - 1,
                        s.length());
        }
    };
    return watcher;
}

Cara Penggunaan

yourEditText.addTextChangedListener(getTextWatcherAllowAfterDeci(1));
Hiren Dabhi
sumber
Bekerja seperti pesona !!. Terima kasih Hiren :)
nisha.113a5
2

Cara termudah untuk mencapainya adalah:

et.addTextChangedListener(new TextWatcher() {
    public void onTextChanged(CharSequence arg0, int arg1, int arg2, int arg3) {
        String text = arg0.toString();
        if (text.contains(".") && text.substring(text.indexOf(".") + 1).length() > 2) {
            et.setText(text.substring(0, text.length() - 1));
            et.setSelection(et.getText().length());
        }
    }

    public void beforeTextChanged(CharSequence arg0, int arg1, int arg2, int arg3) {

    }

    public void afterTextChanged(Editable arg0) {
    }
});
Sasa Ilic
sumber
Jawaban Sederhana dan Perferct
Logo
1

Inilah solusi saya:

     yourEditText.addTextChangedListener(new TextWatcher() {
        @Override
        public void onTextChanged(CharSequence s, int start, int before, int count) {
            NumberFormat formatter = new DecimalFormat("#.##");
            double doubleVal = Double.parseDouble(s.toString());
            yourEditText.setText(formatter.format(doubleVal));
        }

        @Override
        public void beforeTextChanged(CharSequence s, int start, int count,int after) {}

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

Jika pengguna memasukkan angka dengan lebih dari dua angka setelah koma, itu akan dikoreksi secara otomatis.

Saya harap saya telah membantu!

lopez.mikhael
sumber
Apakah Anda menguji kode ini? Ini tidak bisa benar-benar berfungsi, karena setiap kali Anda memanggil setText () TextWatcher mengaktifkan lagi => loop tak terbatas.
muetzenflo
06-07 08: 01: 35.006: E / AndroidRuntime (30230): java.lang.StackOverflowError Tidak berfungsi
Anjula
1

Ini bekerja dengan baik untuk saya. Ini memungkinkan nilai untuk dimasukkan bahkan setelah fokus diubah dan diambil kembali. Sebagai contoh: 123.00, 12.12, 0.01, dll ..

1. Integer.parseInt(getString(R.string.valuelength)) Menentukan panjang input yang digits.Valuesdiakses dari string.xmlfile. Sangat mudah untuk mengubah nilai. 2. Integer.parseInt(getString(R.string.valuedecimal)), ini untuk batas maksimal tempat desimal.

private InputFilter[] valDecimalPlaces;
private ArrayList<EditText> edittextArray;

valDecimalPlaces = new InputFilter[] { new DecimalDigitsInputFilterNew(
    Integer.parseInt(getString(R.string.valuelength)),
    Integer.parseInt(getString(R.string.valuedecimal))) 
};

Array EditTextnilai yang memungkinkan untuk melakukan tindakan.

for (EditText etDecimalPlace : edittextArray) {
            etDecimalPlace.setFilters(valDecimalPlaces);

Saya baru saja menggunakan array nilai yang berisi beberapa DecimalDigitsInputFilterNew.classfile edittext Next .

import android.text.InputFilter;
import android.text.Spanned;

public class DecimalDigitsInputFilterNew implements InputFilter {

    private final int decimalDigits;
    private final int before;

    public DecimalDigitsInputFilterNew(int before ,int decimalDigits) {
        this.decimalDigits = decimalDigits;
        this.before = before;
    }

    @Override
    public CharSequence filter(CharSequence source, int start, int end,
        Spanned dest, int dstart, int dend) {
        StringBuilder builder = new StringBuilder(dest);
        builder.replace(dstart, dend, source
              .subSequence(start, end).toString());
        if (!builder.toString().matches("(([0-9]{1})([0-9]{0,"+(before-1)+"})?)?(\\.[0-9]{0,"+decimalDigits+"})?")) {
             if(source.length()==0)
                  return dest.subSequence(dstart, dend);
             return "";
        }
        return null;
    }
}
Karthi Keyan
sumber
1

Ini untuk membangun jawaban pinhassi - masalah yang saya temui adalah Anda tidak dapat menambahkan nilai sebelum desimal setelah batas desimal tercapai. Untuk memperbaiki masalah ini, kita perlu membuat string terakhir sebelum melakukan pencocokan pola.

import java.util.regex.Matcher;
import java.util.regex.Pattern;

import android.text.InputFilter;
import android.text.Spanned;

public class DecimalLimiter implements InputFilter
{
    Pattern mPattern;

    public DecimalLimiter(int digitsBeforeZero,int digitsAfterZero) 
    {
        mPattern=Pattern.compile("[0-9]{0," + (digitsBeforeZero) + "}+((\\.[0-9]{0," + (digitsAfterZero) + "})?)||(\\.)?");
    }

    @Override
    public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) 
    {
        StringBuilder sb = new StringBuilder(dest);
        sb.insert(dstart, source, start, end);

        Matcher matcher = mPattern.matcher(sb.toString());
        if(!matcher.matches())
            return "";
        return null;
    }
}
Robert Conley
sumber
1
et = (EditText) vw.findViewById(R.id.tx_edittext);

et.setFilters(new InputFilter[] {
        new DigitsKeyListener(Boolean.FALSE, Boolean.TRUE) {
            int beforeDecimal = 5, afterDecimal = 2;

            @Override
            public CharSequence filter(CharSequence source, int start, int end,
                    Spanned dest, int dstart, int dend) {
                String temp = et.getText() + source.toString();

                if (temp.equals(".")) {
                    return "0.";
                }
                else if (temp.toString().indexOf(".") == -1) {
                    // no decimal point placed yet
                    if (temp.length() > beforeDecimal) {
                        return "";
                    }
                } else {
                    temp = temp.substring(temp.indexOf(".") + 1);
                    if (temp.length() > afterDecimal) {
                        return "";
                    }
                }

                return super.filter(source, start, end, dest, dstart, dend);
            }
        }
});
Pawan Shukla
sumber
Sekitar 2 tahun dari waktu jawaban Anda. Saya mencoba menggunakan kode Anda kemudian saya menemukan masalah tentang solusi Anda adalah Anda tambahkan sourcesetelahnya et.getText(). Itu selalu dipahami bahwa orang mengetik di akhir kotak, bukan di awal kotak. StringBuilder stringBuilder = new StringBuilder(text.getText().toString()); stringBuilder.replace(dstart, dend, source.toString()); String temp = stringBuilder.toString();harus bekerja. Terima kasih.
Truong Hieu
1

Buat kelas baru di Android kotlin dengan nama DecimalDigitsInputFilter

class DecimalDigitsInputFilter(digitsBeforeZero: Int, digitsAfterZero: Int) : InputFilter {
lateinit var mPattern: Pattern
init {
    mPattern =
        Pattern.compile("[0-9]{0," + (digitsBeforeZero) + "}+((\\.[0-9]{0," + (digitsAfterZero) + "})?)||(\\.)?")
}
override fun filter(
    source: CharSequence?,
    start: Int,
    end: Int,
    dest: Spanned?,
    dstart: Int,
    dend: Int
): CharSequence? {
    val matcher: Matcher = mPattern.matcher(dest?.subSequence(0, dstart).toString() + source?.subSequence(start, end).toString() + dest?.subSequence(dend, dest?.length!!).toString())
    if (!matcher.matches())
        return ""
    else
        return null
}

Panggil kelas ini dengan baris berikut

 et_buy_amount.filters = (arrayOf<InputFilter>(DecimalDigitsInputFilter(8,2)))

ada terlalu banyak jawaban untuk pertanyaan yang sama tetapi Anda dapat memasukkan 8 digit sebelum desimal dan 2 digit setelah desimal

jawaban lain hanya menerima 8 digit

Sagar Bandamwar
sumber