Bagaimana cara saya menggunakan InputFilter untuk membatasi karakter dalam EditText di Android?

171

Saya ingin membatasi karakter menjadi 0-9, az, AZ, dan bilah spasi saja. Mengatur tipe input I dapat membatasi ke angka tetapi saya tidak dapat menemukan cara-cara Inputfilter melihat melalui dokumen.

Tim Wayne
sumber

Jawaban:

186

Saya menemukan ini di forum lain. Bekerja seperti jagoan.

InputFilter filter = new InputFilter() {
    public CharSequence filter(CharSequence source, int start, int end,
            Spanned dest, int dstart, int dend) {
        for (int i = start; i < end; i++) {
            if (!Character.isLetterOrDigit(source.charAt(i))) {
                return "";
            }
        }
        return null;
    }
};
edit.setFilters(new InputFilter[] { filter });
moonlightcheese
sumber
58
sebenarnya itu tidak berfungsi dengan baik di Android yang lebih baru (seperti 4.0+). Mereka memperkenalkan saran kamus di atas keyboard. Saat Anda mengetikkan kata umum (katakanlah "the") diikuti oleh karakter ilegal untuk filter ini (katakanlah, "-"), seluruh kata dihapus dan setelah Anda mengetikkan karakter lain (bahkan yang diizinkan, seperti "bla") filter mengembalikan "" dan tidak ada karakter yang muncul di lapangan. Ini karena metode ini mendapatkan SpannableStringBuilder dalam parameter sumber dengan "the-blah" di dalamnya dan mulai / akhir parameter yang mencakup seluruh string input ... Lihat jawaban saya untuk solusi yang lebih baik.
Łukasz Sromek
4
Dalam contoh itu, di mana ia mengembalikan "", saya pikir itu harus mengembalikan teks yang harus ditampilkan. yaitu Anda harus menghapus karakter ilegal dan mengembalikan string yang Anda inginkan ditampilkan. developer.android.com/reference/android/widget/… , android.view.KeyEvent)
Andrew Mackenzie
+1 Benar-benar jawaban yang bagus. Character.isLetterOrDigit () semua metode ini sangat berguna.
Atul Bhardwaj
@AndrewMackenzie Jika karakter input, misalnya, koma- ',', yang tidak legal, dan saya ingin menghapusnya, cara memperbaiki kode di atas.
twlkyao
! Character.isLetterOrDigit (source.charAt (i)) &&! Character.isSpaceChar (source.charAt (i)) untuk memungkinkan spasi
Leon
139

InputFilters agak rumit dalam versi Android yang menampilkan saran kamus. Anda terkadang mendapatkan SpannableStringBuilder, terkadang polos Stringdi sourceparameter.

Berikut ini InputFilterharus bekerja. Jangan ragu untuk memperbaiki kode ini!

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

        if (source instanceof SpannableStringBuilder) {
            SpannableStringBuilder sourceAsSpannableBuilder = (SpannableStringBuilder)source;
            for (int i = end - 1; i >= start; i--) { 
                char currentChar = source.charAt(i);
                 if (!Character.isLetterOrDigit(currentChar) && !Character.isSpaceChar(currentChar)) {    
                     sourceAsSpannableBuilder.delete(i, i+1);
                 }     
            }
            return source;
        } else {
            StringBuilder filteredStringBuilder = new StringBuilder();
            for (int i = start; i < end; i++) { 
                char currentChar = source.charAt(i);
                if (Character.isLetterOrDigit(currentChar) || Character.isSpaceChar(currentChar)) {    
                    filteredStringBuilder.append(currentChar);
                }     
            }
            return filteredStringBuilder.toString();
        }
    }
}
Łukasz Sromek
sumber
2
apakah ada alasan mengapa Anda tidak ingin melanjutkan sumbernya? Apakah Anda melihat ada yang salah dengan hanya melakukan ini (untuk hanya membolehkan alfanumerik ditambah beberapa karakter khusus): String replacement = source.subSequence(start, end).toString(); return replacement.replaceAll("[^A-Za-z0-9_\\-@]", "");
Splash
3
Ini tidak mempertimbangkan masalah di mana teks saran kamus berulang muncul. @serwus mengidentifikasi itu dalam jawaban mereka. Pada dasarnya Anda harus mengembalikan nol jika tidak ada modifikasi yang dilakukan dalam kedua kasus.
hooby3dfx
3
Kode ini berfungsi sempurna seperti kata lukasz (dalam jawaban di atas) tetapi saya menghadapi beberapa masalah dengan kata-kata non-kamus. ketika saya mengetik chiru itu menunjukkan 3 kali seperti chiruchiruchiru. bagaimana cara mengatasinya? dan itu mengambil ruang putih juga, tetapi bagaimana membatasi di sebelah ruang putih berikutnya?
chiru
3
Untuk beberapa alasan, ketika source instanceof SpannableStringBuilder, memasuki AB memberi saya AAB seperti ketika mencoba jawaban sebelumnya. Untungnya saya bisa mengatasinya dengan menggunakan solusi @florian di bawah ini.
Guillaume
1
@ hooby3dfx benar sekali. Meskipun tidak berhasil mendapatkan string pada akhirnya, itu mengacaukan ketika Anda menggunakan kamus / penyelesaian kata.
Aravind
107

jauh lebih mudah:

<EditText
    android:inputType="text"
    android:digits="0,1,2,3,4,5,6,7,8,9,*,qwertzuiopasdfghjklyxcvbnm" />
Florian Fröhlich
sumber
9
Meskipun ini sepertinya jawaban yang sempurna, ada masalah menurut dokumen: "Adapun semua implementasi KeyListener, kelas ini hanya berkaitan dengan keyboard perangkat keras. Metode input perangkat lunak tidak memiliki kewajiban untuk memicu metode di kelas ini." Saya pikir InputFilter mungkin cara yang lebih baik, meskipun lebih rumit.
Craig B
19
Solusi Luar Biasa Hanya Ingin Menambahkan Anda tidak perlu menyerah ","di antara keduanya. Anda dapat menggunakan sesuatu seperti ini"0123456789qwertzuiopasdfghjklyxcvbnmQWERTZUIOPASDFGHJKLYXCVBNM"
DeltaCap019
26
Bukan solusi multibahasa
AAverin
Jika Anda berencana menggunakan terjemahan di aplikasi Anda. JANGAN GUNAKAN SOLUSI INI!
grantespo
Sepertinya ini mungkin tidak berhasil imeOptions="actionNext", dll.
Pete Doyle
68

Tidak ada jawaban yang diposting yang berfungsi untuk saya. Saya datang dengan solusi saya sendiri:

InputFilter filter = new InputFilter() {
    @Override
    public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {
        boolean keepOriginal = true;
        StringBuilder sb = new StringBuilder(end - start);
        for (int i = start; i < end; i++) {
            char c = source.charAt(i);
            if (isCharAllowed(c)) // put your condition here
                sb.append(c);
            else
                keepOriginal = false;
        }
        if (keepOriginal)
            return null;
        else {
            if (source instanceof Spanned) {
                SpannableString sp = new SpannableString(sb);
                TextUtils.copySpansFrom((Spanned) source, start, sb.length(), null, sp, 0);
                return sp;
            } else {
                return sb;
            }           
        }
    }

    private boolean isCharAllowed(char c) {
        return Character.isLetterOrDigit(c) || Character.isSpaceChar(c);
    }
}
editText.setFilters(new InputFilter[] { filter });
Kamil Seweryn
sumber
3
Ini adalah satu-satunya jawaban yang benar-benar memiliki pendekatan yang tepat untuk mencegah pengulangan teks dari saran kamus! Suara positif!
hooby3dfx
4
Satu-satunya hal, EditTextsudah dapat memiliki filter sendiri, mis. Filter panjang. Jadi, alih-alih hanya mengganti filter, Anda kemungkinan besar ingin menambahkan filter Anda ke yang sudah ada.
Aleks N.
Apakah ini masih terkini? Bagi saya Android 6.0.1 berfungsi pada keyboard layar
XxGoliathusxX
2
Masalah kecil dengan jawaban ini (atau dengan bagaimana mekanisme input Android bekerja) adalah bahwa backspace kadang-kadang muncul bagi pengguna untuk tidak berfungsi karena mereka melakukan backspacing atas karakter yang dimasukkan sebelumnya yang tidak valid yang masih ditahan di buffer sumber.
jk7
1
Masalah yang sama dengan solusi Łukasz Sromek: Karakter yang tidak valid setelah spasi diganti dengan spasi.
Ingo Schalk-Schupp
25

Gunakan ini kerjanya 100% kebutuhan Anda dan sangat sederhana.

<EditText
android:inputType="textFilter"
android:digits="@string/myAlphaNumeric" />

Di strings.xml

<string name="myAlphaNumeric">abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789</string>
Mohamed Ibrahim
sumber
15

Untuk menghindari Karakter Khusus dalam tipe input

public static InputFilter filter = new InputFilter() {
    @Override
    public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {
        String blockCharacterSet = "~#^|$%*!@/()-'\":;,?{}=!$^';,?×÷<>{}€£¥₩%~`¤♡♥_|《》¡¿°•○●□■◇◆♧♣▲▼▶◀↑↓←→☆★▪:-);-):-D:-(:'(:O 1234567890";
        if (source != null && blockCharacterSet.contains(("" + source))) {
            return "";
        }
        return null;
    }
};

Anda dapat mengatur filter ke teks edit Anda seperti di bawah ini

edtText.setFilters(new InputFilter[] { filter });
CommonGuy
sumber
@sathya You wc, Senang membantu Anda :)
1
Tidak memblokir salah satu dari karakter ini. ㋡ ㋛ ☺ ☹ ☻ 〠 シ ッ ツ ツ Ü Ü Ü Ü ﭢ ت ت ت ت ت Ѷ Ѷ Ѷ An An An
Anarchofascist
7

Selain jawaban yang diterima, dimungkinkan juga untuk menggunakan misalnya: android:inputType="textCapCharacters"sebagai atribut <EditText>agar hanya menerima karakter huruf besar (dan angka).

mblenton
sumber
5
android: inputType = "textCapCharacters" tidak membatasi penggunaan karakter lain seperti '., - "dll.
Tvd
Ini juga hanya petunjuk untuk metode input. Itu tidak membatasi karakter apa yang diizinkan untuk dimasukkan.
dcow
5

Untuk beberapa alasan konstruktor kelas android.text.LoginFilter adalah paket-lingkup, sehingga Anda tidak dapat langsung memperpanjangnya (meskipun itu akan identik dengan kode ini). Tetapi Anda dapat memperpanjang LoginFilter.UsernameFilterGeneric! Maka Anda hanya memiliki ini:

class ABCFilter extends LoginFilter.UsernameFilterGeneric {
    public UsernameFilter() {
        super(false); // false prevents not-allowed characters from being appended
    }

    @Override
    public boolean isAllowed(char c) {
        if ('A' <= c && c <= 'C')
            return true;
        if ('a' <= c && c <= 'c')
            return true;

        return false;
    }
}

Ini tidak benar-benar didokumentasikan, tetapi itu adalah bagian dari inti lib, dan sumbernya langsung . Saya telah menggunakannya untuk sementara waktu sekarang, sejauh ini tidak ada masalah, meskipun saya akui saya belum mencoba melakukan sesuatu yang rumit yang melibatkan spannables.

Groxx
sumber
5

Itu Benar, cara terbaik untuk memperbaikinya untuk memperbaikinya dalam Layout XML menggunakan:

<EditText
android:inputType="text"
android:digits="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" />

sebagaimana ditunjukkan oleh Florian Fröhlich, ini berfungsi dengan baik untuk tampilan teks yang rata.

<TextView
android:inputType="text"
android:digits="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" />

Hanya kata hati-hati, karakter yang disebutkan dalam android:digitshanya akan ditampilkan, jadi berhati-hatilah untuk tidak melewatkan serangkaian karakter :)

Kailas
sumber
Anda perlu mendefinisikan inputType = "textFilter" maka hanya itu yang akan berfungsi dengan baik.
Shreyash Mahajan
@ShreyashMahajan, apakah ini tergantung pada aplikasi perangkat / keyboard? Dalam kasus saya inputTypetidak mempengaruhi pemfilteran.
CoolMind
4

Solusi sederhana ini bekerja untuk saya ketika saya perlu mencegah pengguna memasukkan string kosong ke dalam EditText. Tentu saja Anda dapat menambahkan lebih banyak karakter:

InputFilter textFilter = new InputFilter() {

@Override

public CharSequence filter(CharSequence c, int arg1, int arg2,

    Spanned arg3, int arg4, int arg5) {

    StringBuilder sbText = new StringBuilder(c);

    String text = sbText.toString();

    if (text.contains(" ")) {    
        return "";   
    }    
    return c;   
    }   
};

private void setTextFilter(EditText editText) {

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

}
Swifty McSwifterton
sumber
bagaimana cara memanggil solusi ini?
zeeks
1

Jika Anda subkelas InputFilter Anda dapat membuat InputFilter Anda sendiri yang akan memfilter karakter non-alfa-numerik.

InputFilter Interface memiliki satu metode,, filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend)dan menyediakan Anda dengan semua informasi yang perlu Anda ketahui tentang karakter yang dimasukkan ke dalam EditText yang ditugaskan padanya.

Setelah Anda membuat InputFilter Anda sendiri, Anda dapat menetapkannya ke EditText dengan memanggil setFilters (...).

http://developer.android.com/reference/android/text/InputFilter.html#filter(java.lang.CharSequence , int, int, android.text. Direncanakan, int, int)

nicholas.hauschild
sumber
1

Mengabaikan hal-hal rentang yang telah ditangani orang lain, untuk menangani saran kamus dengan benar, saya menemukan kode berikut berfungsi.

Sumber tumbuh seiring saran tumbuh sehingga kita harus melihat berapa banyak karakter yang sebenarnya diharapkan untuk kita ganti sebelum kita mengembalikan apa pun.

Jika kami tidak memiliki karakter yang tidak valid, kembalikan null sehingga terjadi penggantian default.

Kalau tidak, kita perlu mengekstraksi karakter yang valid dari substring yang SEBENARNYA akan ditempatkan ke dalam EditText.

InputFilter filter = new InputFilter() { 
    public CharSequence filter(CharSequence source, int start, int end, 
    Spanned dest, int dstart, int dend) { 

        boolean includesInvalidCharacter = false;
        StringBuilder stringBuilder = new StringBuilder();

        int destLength = dend - dstart + 1;
        int adjustStart = source.length() - destLength;
        for(int i=start ; i<end ; i++) {
            char sourceChar = source.charAt(i);
            if(Character.isLetterOrDigit(sourceChar)) {
                if(i >= adjustStart)
                     stringBuilder.append(sourceChar);
            } else
                includesInvalidCharacter = true;
        }
        return includesInvalidCharacter ? stringBuilder : null;
    } 
}; 
Whitehouse Kristen
sumber
1

untuk mencegah kata-kata dalam edittext. buat kelas yang bisa Anda gunakan kapan saja.

public class Wordfilter implements InputFilter
{
    @Override
    public CharSequence filter(CharSequence source, int start, int end,Spanned dest, int dstart, int dend) {
        // TODO Auto-generated method stub
        boolean append = false;
        String text = source.toString().substring(start, end);
        StringBuilder str = new StringBuilder(dest.toString());
        if(dstart == str.length())
        {
            append = true;
            str.append(text);
        }
        else
            str.replace(dstart, dend, text);
        if(str.toString().contains("aaaaaaaaaaaa/*the word here*/aaaaaaaa"))
        {
            if(append==true)
                return "";
            else
                return dest.subSequence(dstart, dend);
        }
        return null;
    }
}
Sahar Millis
sumber
1

Pertama tambahkan strings.xml:

<string name="vin_code_mask">0123456789abcdefghjklmnprstuvwxyz</string>

XML :

android:digits="@string/vin_code_mask"

Kode di Kotlin:

edit_text.filters += InputFilter { source, start, end, _, _, _ ->
    val mask = getString(R.string.vin_code_mask)
    for (i in start until end) {
        if (!mask.contains(source[i])) {
            return@InputFilter ""
        }
    }
    null
}

Aneh, tetapi bekerja aneh pada keyboard lunak emulator.

Peringatan! Kode berikut akan memfilter semua huruf dan simbol lainnya kecuali angka untuk keyboard perangkat lunak. Hanya keyboard digital yang akan muncul di smartphone.

edit_text.keyListener = DigitsKeyListener.getInstance(context.getString(R.string.vin_code_mask))

Saya juga biasanya ditetapkan maxLength, filters, inputType.

CoolMind
sumber
1

Ini adalah utas lama, tetapi solusi yang dimaksudkan semuanya memiliki masalah (tergantung pada perangkat / versi Android / Keyboard).

PENDEKATAN YANG BERBEDA

Jadi akhirnya aku pergi dengan pendekatan yang berbeda, daripada menggunakan InputFilterimplementasi bermasalah, saya menggunakan TextWatcherdan TextChangedListenerdari EditText.

KODE LENGKAP (CONTOH)

editText.addTextChangedListener(new TextWatcher() {

    @Override
    public void afterTextChanged(Editable editable) {
        super.afterTextChanged(editable);

        String originalText = editable.toString();
        int originalTextLength = originalText.length();
        int currentSelection = editText.getSelectionStart();

        // Create the filtered text
        StringBuilder sb = new StringBuilder();
        boolean hasChanged = false;
        for (int i = 0; i < originalTextLength; i++) {
            char currentChar = originalText.charAt(i);
            if (isAllowed(currentChar)) {
                sb.append(currentChar);
            } else {
                hasChanged = true;
                if (currentSelection >= i) {
                    currentSelection--;
                }
            }
        }

        // If we filtered something, update the text and the cursor location
        if (hasChanged) {
            String newText = sb.toString();
            editText.setText(newText);
            editText.setSelection(currentSelection);
        }
    }

    private boolean isAllowed(char c) {
        // TODO: Add the filter logic here
        return Character.isLetter(c) || Character.isSpaceChar(c);
    }
    @Override
    public void beforeTextChanged(CharSequence s, int start, int count, int after) {
        // Do Nothing
    }

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

Alasannya InputFilterbukan solusi yang baik di Android karena itu tergantung pada implementasi keyboard. Input Keyboard sedang difilter sebelum input dilewatkan ke EditText. Tetapi, karena beberapa keyboard memiliki implementasi yang berbeda untuk InputFilter.filter()permohonan, ini bermasalah.

Di sisi lain TextWatchertidak peduli dengan implementasi keyboard, itu memungkinkan kami untuk membuat solusi sederhana dan memastikan itu akan bekerja pada semua perangkat.

Eyal Biran
sumber
The onTextChangedhanya membutuhkan public voiddepan di itu.
Andy
Terima kasih atas komentarnya. Tetap.
Eyal Biran
1

Saya telah melakukan sesuatu seperti ini untuk membuatnya tetap sederhana:

edit_text.filters = arrayOf(object : InputFilter {
    override fun filter(
        source: CharSequence?,
        start: Int,
        end: Int,
        dest: Spanned?,
        dstart: Int,
        dend: Int
    ): CharSequence? {
        return source?.subSequence(start, end)
            ?.replace(Regex("[^A-Za-z0-9 ]"), "")
    }
})

Dengan cara ini kami mengganti semua karakter yang tidak diinginkan di bagian baru dari string sumber dengan string kosong.

The edit_textvariabel adalah EditTextobjek kita mengacu kepada.

Kode ditulis dalam bahasa kotlin.

Lazar
sumber
1
Terima kasih! Solusi ini berfungsi baik saat mengetik maupun menempelkan teks.
CoolMind
0

Dimungkinkan untuk digunakan setOnKeyListener. Dalam metode ini, kita dapat menyesuaikan input edittext!

Võ Hoài Lên
sumber
0

Inilah cara saya membuat filter untuk bidang Nama di Edit Teks. (Huruf pertama adalah CAPS, dan hanya mengizinkan satu spasi setelah setiap kata.

public void setNameFilter() {
    InputFilter filter = new InputFilter() {
        @RequiresApi(api = Build.VERSION_CODES.KITKAT)
        public CharSequence filter(CharSequence source, int start, int end,
                                   Spanned dest, int dstart, int dend) {
            for (int i = start; i < end; i++) {
                if (dend == 0) {
                    if (Character.isSpaceChar(source.charAt(i)) ||
                            !Character.isAlphabetic(source.charAt(i))) {
                        return Constants.Delimiter.BLANK;
                    } else {
                        return String.valueOf(source.charAt(i)).toUpperCase();
                    }
                } else if (Character.isSpaceChar(source.charAt(i)) &&
                        String.valueOf(dest).endsWith(Constants.Delimiter.ONE_SPACE)) {
                    return Constants.Delimiter.BLANK;
                } else if ((!Character.isSpaceChar(source.charAt(i)) &&
                        !Character.isAlphabetic(source.charAt(i)))) {
                    return Constants.Delimiter.BLANK;
                }
            }
            return null;
        }
    };
    editText.setFilters(new InputFilter[]{filter, new InputFilter.LengthFilter(Constants.Length.NAME_LENGTH)});
}
u_pendra
sumber
Constants.Delimiter.BLANK tidak dikenal.
CoolMind
0

Saya memiliki jawaban yang sama di Kotlin:

/**
 * Returns the filter of the editText'es CharSequence value when [filterType] is:
 * 1 -> letters; 2 -> letters and digits; 3 -> digits;
 * 4 -> digits and dots
 */
class InputFilterAlphanumeric(private val filterType: Int): InputFilter {
    override fun filter(source: CharSequence?, start: Int, end: Int, dest: Spanned?, dstart: Int, dend: Int): CharSequence {
        (source as? SpannableStringBuilder)?.let {sourceAsSpannableBuilder  ->
            for (i in (end - 1) downTo start) {
                val currentChar = source[i]
                when(filterType) {
                    1 -> {
                        if (!currentChar.isLetter() && !currentChar.isWhitespace()) {
                            sourceAsSpannableBuilder.delete(i, i + 1)
                        }
                    }
                    2 -> {
                        if (!currentChar.isLetterOrDigit() && !currentChar.isWhitespace()) {
                            sourceAsSpannableBuilder.delete(i, i + 1)
                        }
                    }
                    3 -> {
                        if (!currentChar.isDigit()) {
                            sourceAsSpannableBuilder.delete(i, i + 1)
                        }
                    }
                    4 -> {
                        if (!currentChar.isDigit() || !currentChar.toString().contains(".")) {
                            sourceAsSpannableBuilder.delete(i, i + 1)
                        }
                    }
                }
            }
            return source
        } ?: run {
            val filteredStringBuilder = StringBuilder()
            for (i in start until end) {
                val currentChar = source?.get(i)
                when(filterType) {
                    1 -> {
                        if (currentChar?.isLetter()!! || currentChar.isWhitespace()) {
                            filteredStringBuilder.append(currentChar)
                        }
                    }
                    2 -> {
                        if (currentChar?.isLetterOrDigit()!! || currentChar.isWhitespace()) {
                            filteredStringBuilder.append(currentChar)
                        }
                    }
                    3 -> {
                        if (currentChar?.isDigit()!!) {
                            filteredStringBuilder.append(currentChar)
                        }
                    }
                    4 -> {
                        if (currentChar?.isDigit()!! || currentChar.toString().contains(".")) {
                            filteredStringBuilder.append(currentChar)
                        }
                    }
                }
            }
            return filteredStringBuilder
        }
    }
}

dan dapatkan kelas dengan fungsi Extension:

fun EditText.filterByDataType(filterType: Int) {
    this.filters = arrayOf<InputFilter>(InputFilterAlphanumeric(filterType))
}
Irving Kennedy
sumber