Android EditText'te Ondalık Yerleri Sınırla


125

Mali durumunuzu yönetmenize yardımcı olacak bir uygulama yazmaya çalışıyorum. EditTextKullanıcının bir miktar para belirleyebileceği bir Alan kullanıyorum .

Ben ayarlamak inputTypeiçin numberDecimalbu insanlar gibi sayıları girmek için izin verir olması dışında, cezası çalıştığı 123.122için para mükemmel almayan.

Ondalık noktadan sonraki karakter sayısını ikiyle sınırlamanın bir yolu var mı?


Normal bir ifade yazabilir ve odağı kaybettiğinde düzenleme metninin içeriğini doğrulayabilirsiniz.
blindstuff

InputFilterArayüzü buldum, geliştirici.android.com/filter reference/android/text/ method/… istediğimi yapıyor gibi görünüyor , ancak uygulamak zorunda olduğum yöntem benim için oldukça kafa karıştırıcı. Biri böyle bir Filtre yazdı mı ve nasıl kullanılacağını biliyor mu?
Konstantin Weitz

Önerilen çözümlerden herhangi biri RTL yerel ayarları için çalışıyor mu? Bildiğim kadarıyla yapmayacaklar ...
Nick

Yanıtlar:


118

Daha zarif bir yol, aşağıdaki gibi bir normal ifade (regex) kullanmak olacaktır:

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

}

Kullanmak için şunları yapın:

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

32
Merhaba, hala iyi ele alınmayan bazı uç durumlar var. Örneğin, 2.45 yazdıktan sonra, "imleci metnin en önüne taşıma" eğilimindeyim. 12.45 metnini üretmek istiyorum, buna izin vermeyecek.
Cheok Yan Cheng

3
kullanıcı ondalık virgülden sonra 2 hane girdikten sonra ondalık sayıdan önce rakamların değiştirilmesine izin vermez.
Gaurav Singla

9
Harika bir çözüm ama tamamen doğru değil. Eşleştirici dest'i kontrol etmemeli, edittext'deki değeri kontrol etmelidir (dest.subSequence (0, dstart) + source.subSequence (start, end) + dest.subSequence (dend, dest.length ()))
Mihaela Romanca

7
Mihaela haklı, edittext'i doldurmaya çalışan dizeyle eşleşmeliyiz. Bu CharSequence match = TextUtils.concat (dest.subSequence (0, dstart), source.subSequence (start, end), dest.subSequence (dend, dest.length ()) gibi başka bir soruyu nasıl birleştireceğimi buldum; Normal ifade daha sonra sorunlara neden oluyordu, bu yüzden bunu "^ \\ d {1," + digitsBeforeZero + "} (\\. \\ d {0," + digitsAfterZero + "}) olarak değiştirdim? $" Ama siz ' Daha sonra da doğrulama yapmanız gerekecek çünkü "1." bu normal ifade ile geçerlidir ancak bu şekilde ihtiyacımız var, böylece nokta yazılabilir.
dt0

6
Bunun virgül (,) için de çalışmasını nasıl sağlarsınız? Dünyanın bazı bölgeleri virgülle ondalık sayılar yazar (ör: 123,45).
Andrew

65

Normal ifade kullanmadan daha basit çözüm:

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

}

Kullanmak:

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

Dizeye 'a' gibi sayısal olmayan karakterler eklemekten beni ne alıkoyabilir?
Konstantin Weitz

4
Bu: <EditText ... android: inputType = "number" />
peceps

1
Bu şöyle olmalıdır: editText.setFilters (yeni InputFilter [] {new DecimalDigitsInputFilter (2)});
frak

6
Bu, "999" yazdığım ve ardından ilk 9'dan sonra bir ondalık nokta eklediğim durumu işlemez.
Jake Stoeffler

1
Bu faydalı teşekkürler. Bunun gibi uzunluk filtresini kullanarak ondalık noktadan önceki basamakları da kısıtlayabiliriz. Kotlin: edtAnyAmount.filters = arrayOf <InputFilter> (InputFilter.LengthFilter (7), DecimalDigitsInputFilter (2))
Faldu Jaldeep

37

Bu uygulama InputFiltersorunu çözer.

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

Null yerine SpannableStringBuilder'ı döndürmemiz için herhangi bir neden olup olmadığını öğrenebilir miyim? Null ile test ediyorum, o da iyi çalışıyor. Ayrıca, DigitsKeyListener'dan miras almamıza gerek var mı? Android kullanırken: inputType = "numberDecimal" tüm "0123456789" işlemini gerçekleştirecektir. karakter yaptırımı.
Cheok Yan Cheng

1
İyi çalışıyor. Çok teşekkür ederim.
Andrei Aulaska

34

Burada, ondalık noktadan önce en fazla 4 hane ve ondan sonra en fazla 1 hane olan örnek bir InputFilter verilmiştir.

Edittext'in izin verdiği değerler: 555.2 , 555 , .2

Edittext'nin engellediği değerler: 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 });

izin vermiyor. yazılacak
AmiNadimi

22

@Pinhassi çözümü için bazı düzeltmeler yaptım. Bazı durumları ele alır:

1. imleci herhangi bir yere taşıyabilirsiniz

2. eksi işareti işleme

3. digitbefore = 2 ve digitsafter = 4 ve 12.4545 girersiniz. Ardından "." Öğesini kaldırmak isterseniz, izin vermeyecektir.

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

Bence mükemmel bir çözüm olmalı, günümü yaptım. Teşekkür ederim.
Pratik Butani

1
@Omkar bu yanlış. bu koşul, uzunluk> 0 olsa bile her zaman doğru olacaktır, dest.length () == 0 tüm metni 0'dan fazla düzenleseniz bile her zaman doğrudur ...
user924

@Omkar yorumunuzu kaldırın lütfen
user924

@android_dev neden negatif değerler (eksi) yazamıyorum?
user924

EĞER ayarlarsanız android:inputType="number"veya android:inputType="numberDecimal"eksi yazmanıza izin android:digits="0123456789.-"vermezseniz , bu yardımcı olmaz
kullanıcı924

18

Diğer çözümü sevmiyorum ve kendi çözümümü yarattım. Bu çözümle noktadan önce MAX_BEFORE_POINT basamaktan fazlasını giremezsiniz ve ondalık sayılar MAX_DECIMAL'den fazla olamaz.

Basamağı fazla yazamazsınız, başka efekt yok! Ek olarak "" yazarsanız. "0" yazar.

  1. Düzende Metni Düzenle'yi şu şekilde ayarlayın:

    android: InputType = "numberDecimal"

  2. Dinleyiciyi onCreate'inize ekleyin. Aramayı PerfectDecimal (str, NUMBER_BEFORE_POINT, NUMBER_DECIMALS) olarak düzenlemek için noktadan önceki ve sonraki basamak sayısını değiştirmek istiyorsanız, burada 3 ve 2 olarak ayarlanır

    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. Bu İşlevi dahil et:

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

Ve bitti!


1
Şerefe. Diğeri benim için çalışmadığında çok iyi çalışın
Ayı

1
Bu Kabul Edilen Cevap olmalı .. Mükemmel .. Burada tüm koşullar sağlanıyor.
Nayan

1
Tüm çok oylanan cevapların üzerinde, bu cevap aslında benim için çalıştı.
Rohit Mandiwal

Aferin! Tüm kombinasyonları denedim ve harika çalışıyor gibi görünüyor. Teşekkür ederim.
akelec

Nasıl çalıştığını bilmiyorum ama bir cazibe gibi çalışıyor.
wonsuc

17

Bunu TextWatcherşu şekilde başardım

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

Bu çözüm, kullanıcının ondalık noktadan sonra ikiden fazla rakam girmesine izin vermeyecektir. Ayrıca ondalık virgülden önce istediğiniz sayıda basamak girebilirsiniz. Birden çok EditText için filtre ayarlamak üzere http://v4all123.blogspot.com/2013/05/set-limit-for-fraction-in-decimal.html bu bloga bakın . Umarım bu yardımcı olacak. Teşekkür ederim.


Geç bilgi için özür dilerim. Dont başlatmak için unutmak countile -1. O zaman sadece bu doğru çalışacaktır. int count = -1;
Gunaseelan

Gunaseelan - yukarıdaki kodu denedim ve iyi çalışıyor. Ama yazdığım metni silip tekrar yazmaya başladığımda, sadece bir rakam yazarak, bunun için herhangi bir çözüm .....
Siva K

@SivaK Olmaz arkadaş. Silerseniz ve yazarsanız minimum 100 rakam kabul eder. Buna nasıl erişeceğinizi bilmiyorum listener. Herhangi yolu bir göz bir bloguma atın yazısı . Bir fikir edinebilirsiniz. Eğer yapamazsan lütfen bana haber ver. Bu konuda size yardımcı olacağım.
Gunaseelan

@ SivaK'ın söylediklerini doğruladım. bu her halükarda zekice ama bunda tamamen işlevsel olması için bazı düzenlemeler yapacağım (bence)
MrTristan

@Gunaseelan çözümünüz için teşekkür ederim. Ama bazı hataları var. Örneğin, ikinci ondalık sayımı sildiğimde, tekrar yazmak imkansızdır (tekrar yazabilmek için tüm ondalıkları silmeliyim). Ayrıca, tüm girişi sildikten sonra, tekrar yazarken bazı garip sınırlamalar oluşur.
akelec

14

Bulduğum InputFilter, ondalık basamaktan önceki ve sonraki basamak sayısını yapılandırmanıza olanak tanır. Ek olarak, baştaki sıfırlara izin vermez.

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

Güzel çözüm! Benim için çalışıyor, ancak baştaki döneme (nokta) izin vermemek istiyorum. Örneğin, ".123" dizisine izin verilmez. Bunu nasıl başarabilirim?
ibogolyubskiy

13

Gereklilik, ondalık sayıdan sonra 2 basamaktır. Ondalık virgülden önceki basamak için sınır olmamalıdır . Yani çözüm,

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

Ve şu şekilde kullanın:

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

İlham için @ Pinhassi'ye teşekkürler.


İyi ... İyi çalışıyor
jojo

12

Çözümüm basit ve mükemmel çalışıyor!

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

Kullanımı:

myTextEdit.addTextChangedListener(new DecimalInputTextWatcher(myTextEdit, 2));

isValid()Her isValid()aramada desenleri yeniden oluşturmaktan kaçınmak için desenleri oluşturucuya taşıyın .
Hemant Kaushik

6

Bir TextView'a koymadan önce dizenizi biçimlendirmek için NumberFormat.getCurrencyInstance () kullanmayı deneyin .

Gibi bir şey:

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

Düzenle - Dokümanlarda bulabildiğim para birimi için inputType yok. Sanırım Japon Yeni gibi ondalık basamaklar için aynı kuralı takip etmeyen bazı para birimleri var.

LeffelMania'nın da belirttiği gibi, yukarıdaki kodu TextWatchersizin üzerinde ayarlanmış olan bir kod ile kullanarak kullanıcı girişini düzeltebilirsiniz EditText.


6

Biraz iyileştirilmiş @Pinhassi çözümü.

Çok iyi çalışıyor. Birleştirilmiş dizeleri doğrular.

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

 }

6

Yukarıdaki çözümleri değiştirdim ve aşağıdakini yarattım. Ondalık noktadan önceki ve sonraki basamak sayısını ayarlayabilirsiniz.

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

}


Reisub'un 2014'te burada aynı soruya neredeyse cevap verdiği şey buydu.
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;
    }
});

Bunun yaptığı şey, düzenleme aşamasından çıktığınızda alanı doğru biçime biçimlendirmesidir. Şu anda sadece 2 ondalık karaktere sahip. Bunun bunu yapmanın oldukça kolay bir yolu olduğunu düşünüyorum.


4

Buradaki tüm cevaplar oldukça karmaşık, çok daha basit hale getirmeye çalıştım Koduma bakın ve kendiniz karar verin -

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

    }
});

benim için mükemmel çalışıyor. Tüm senaryoları kontrol ettim. Teşekkürler.
Amarnath Baitha

1234.56 girdiğimi varsayalım, şimdi 12378.56 gibi düzenlemek istiyorum, ondalık sayıları silmeden yapamam.
Aman Verma

4

Pinhassi'nin cevabını gerçekten beğendim, ancak kullanıcı ondalık noktadan sonra belirtilen rakamları girdikten sonra artık ondalık ayırıcının sol tarafına metin giremeyeceğinizi fark ettim. Sorun, çözümün girilen mevcut metni değil, yalnızca girilen önceki metni test etmesiydi. İşte doğrulama için orijinal metne yeni karakteri ekleyen çözümüm.

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

Ve editText filtresini ayarlama çağrısı

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

4

Pinhassi'nin normal ifadesini kullanan çözümü geliştirdim, böylece uç durumları da doğru şekilde ele alıyor. Girişin doğru olup olmadığını kontrol etmeden önce, ilk olarak son dize android docs tarafından açıklandığı gibi oluşturulur.

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

Kullanımı:

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

4

Basit Yardımcı sınıfı, kullanıcının ondalık sayıdan sonra 2'den fazla basamak girmesini önlemek için buradadır:

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

4

№6 cevabını değiştirdim (Favas Kv ile) çünkü oraya sadece noktayı ilk konuma koyabilirsiniz.

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

3

Diğerlerinin dediği gibi, bu sınıfı projeme ekledim ve filtreyi EditTextistediğim şekilde ayarladım .

Filtre, @ Pixel'in cevabından kopyalanır. Ben sadece hepsini bir araya getiriyorum.

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

Şimdi filtreyi şu şekilde ayarlayın EditText.

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

Burada önemli bir şey, ondalık noktadan sonra ikiden fazla basamağın gösterilmesine izin vermeme sorunumu çözmesidir, EditTextancak sorun şu getText()ki EditText, bundan sonra yazdığım tüm girdiyi döndürür.

Örneğin, filtreyi üzerine uyguladıktan sonra EditText, 1.5699856987 girişini ayarlamaya çalıştım. Yani ekranda mükemmel olan 1.56'yı gösteriyor.

Sonra bu girdiyi başka hesaplamalar için kullanmak istedim, bu yüzden metni o girdi alanından ( EditText) almak istedim . Ben çağrıldığında mEditText.getText().toString()o benim durumumda kabul edilebilir değildi 1,5699856987 döndürür.

Bu yüzden, .tml dosyasından aldıktan sonra değeri tekrar ayrıştırmak zorunda kaldım EditText.

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

setScaleburada tam metni aldıktan sonra hile yapıyor EditText.


Merhaba, kullanıcının ondalık (.) Girmediğinden 2 basamaktan fazlasını giremeyeceğinden nasıl emin olabilirim?
ManishNegi

2

Ben de bu problemle karşılaştım. Kodu birçok EditTexts'te yeniden kullanabilmek istedim. Bu benim çözümüm:

Kullanım:

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

Sınıf:

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

2

@Meh for you ..

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

2

Çok geç bir yanıt: Bunu basitçe şu şekilde yapabiliriz:

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

2

İşte ondalık virgülden sonra TextWatchersadece n sayıda basamağa izin veren .

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

Nasıl kullanılır

yourEditText.addTextChangedListener(getTextWatcherAllowAfterDeci(1));

Tıkır tıkır çalışıyor!!. Teşekkürler Hiren :)
nisha.113a5

2

Bunu başarmanın en basit yolu şudur:

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

Basit ve Kusursuz Cevap
Logo

1

İşte benim çözümüm:

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

Kullanıcı, ondalık noktadan sonra ikiden fazla sayı içeren bir sayı girerse, bu otomatik olarak düzeltilir.

Umarım yardım etmişimdir!


Bu kodu test ettin mi? Gerçekten işe yaramaz çünkü setText () 'i her çağırdığınızda TextWatcher tekrar ateşler => sonsuz döngü.
muetzenflo

Ağustos 6-7: 01: 35,006: E / AndroidRuntime (30230): java.lang.StackOverflowError değil çalışma
Anjula

1

Bu benim için iyi çalışıyor. Odak değiştirildikten ve geri alındıktan sonra bile değer girilmesine izin verir. Örneğin: 123.00, 12.12, 0.01, vb ..

1. Dosyadan erişilen Integer.parseInt(getString(R.string.valuelength)) girdinin uzunluğunu belirtir . Değerleri değiştirmek oldukça kolaydır. 2. , bu ondalık basamaklar için maksimum sınırdır.digits.Valuesstring.xmlInteger.parseInt(getString(R.string.valuedecimal))

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

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

EditTextEylemi gerçekleştirmeye izin veren değerler dizisi .

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

Birden çok edittext Next DecimalDigitsInputFilterNew.classdosyası içeren değerler dizisi kullandım .

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

1

Bu, pinhassi'nin cevabına dayanmaktır - karşılaştığım sorun, ondalık sınıra ulaşıldığında ondalık sayıdan önce değer ekleyememenizdi. Sorunu çözmek için, kalıp eşleştirmesini yapmadan önce son dizeyi oluşturmamız gerekiyor.

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

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

Cevap sürenizden yaklaşık 2 yıl sonra. Kodunuzu kullanmaya çalıştım, sonra çözümünüzle ilgili sorunu sourcesonradan eklediğinizi buldum et.getText(). İnsanların kutunun başı yerine kutunun sonuna yazdıklarını her zaman anlar. StringBuilder stringBuilder = new StringBuilder(text.getText().toString()); stringBuilder.replace(dstart, dend, source.toString()); String temp = stringBuilder.toString();çalışmalı. Yine de teşekkürler.
Truong Hieu

1

Android kotlin'de DecimalDigitsInputFilter adıyla yeni bir sınıf oluşturun

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
}

Bu sınıfı aşağıdaki satırla arayın

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

aynı cevap için çok fazla cevap var, ancak ondalıktan önce 8 hane ve ondalıktan sonra 2 hane girmenize izin verecek

diğer cevaplar sadece 8 rakam kabul ediyor

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.