Как я могу использовать InputFilter для ограничения символов в EditText в Android?


171

Я хочу ограничить символы только 0-9, az, AZ и пробел. Задав тип ввода, я могу ограничиться цифрами, но не могу понять, как Inputfilter просматривает документы.

Ответы:


186

Я нашел это на другом форуме. Работает как чемпион.

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

58
на самом деле это не так хорошо работает в новых Android (например, 4.0+). Они вводят словарные предложения над клавиатурой. Когда вы набираете общее слово (скажем, "the"), за которым следует недопустимый символ для этого фильтра (скажем, "-"), все слово удаляется, и после ввода других символов (даже разрешенных, например, "бла") фильтр возвращает "", и в поле нет символов. Это потому, что метод получает SpannableStringBuilder в исходном параметре с «the-blah» в нем и начальными / конечными параметрами, охватывающими всю входную строку ... Смотрите мой ответ для лучшего решения.
Лукаш Сромек

4
В этом примере, где он возвращает «», я думаю, что он должен вернуть текст, который должен отображаться. т.е. вы должны удалить недопустимые символы и вернуть строку, которую вы хотите отобразить. developer.android.com/reference/android/widget/…android.view.KeyEvent )
Эндрю Маккензи,

+1 Действительно отличный ответ. Character.isLetterOrDigit () все эти методы очень полезны.
Атул Бхардвай

@AndrewMackenzie Если входной символ, например, запятая, ',', который не является законным, и я хочу удалить его, как исправить код выше.
twlkyao

! Character.isLetterOrDigit (source.charAt (i)) &&! Character.isSpaceChar (source.charAt (i)) для пробелов
Леон

139

InputFilterЭто немного сложнее в версиях Android, которые отображают предложения словаря. Вы иногда получаете SpannableStringBuilder, иногда просто Stringв sourceпараметре.

Следующее InputFilterдолжно работать. Не стесняйтесь улучшать этот код!

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

2
Есть ли причина, по которой вы не хотите подпоследовательности источника? Видите ли вы что-то не так с этим (чтобы разрешить только буквенно-цифровые символы и несколько специальных символов): String replacement = source.subSequence(start, end).toString(); return replacement.replaceAll("[^A-Za-z0-9_\\-@]", "");
Splash

3
Это не учитывает проблему, при которой появляется повторяющийся текст предложения словаря. @serwus определил это в своем ответе. По сути, вы должны вернуть null, если в обоих случаях не было никаких изменений.
hooby3dfx

3
Этот код прекрасно работает, как сказал Лукаш (в ответе выше), но я столкнулся с некоторой проблемой со словами, не входящими в словарь. когда я набираю чиру, это показывает 3 раза как чиручиручиру. как это решить? и он также занимает пробелы, но как ограничить следующие пробелы?
Чир

3
По какой-то причине, когда source instanceof SpannableStringBuilderввод AB дает мне AAB, как при попытке предыдущего ответа. К счастью, я смог обойти это, используя @florian решение ниже.
Гийом

1
@ hooby3dfx абсолютно прав. Несмотря на то, что в конце концов ему удается правильно получить строку, она портится, когда вы используете словарь / завершение слов.
Аравинд

107

намного легче:

<EditText
    android:inputType="text"
    android:digits="0,1,2,3,4,5,6,7,8,9,*,qwertzuiopasdfghjklyxcvbnm" />

9
Хотя это выглядит как идеальный ответ, в соответствии с документацией существует проблема: «Что касается всех реализаций KeyListener, этот класс касается только аппаратных клавиатур. Методы ввода программного обеспечения не обязаны запускать методы этого класса». Я думаю, что InputFilter, вероятно, является лучшим, хотя и более сложным способом.
Крейг Б

19
Потрясающее решение просто хочу добавить Вам не нужно давать ","между. Вы можете использовать что-то вроде этого"0123456789qwertzuiopasdfghjklyxcvbnmQWERTZUIOPASDFGHJKLYXCVBNM"
DeltaCap019

26
Не многоязычное решение
AAverin

Если вы планируете использовать переводы в вашем приложении. НЕ ИСПОЛЬЗУЙТЕ ЭТО РЕШЕНИЕ!
grantespo

Кажется, что это может не работать imeOptions="actionNext", и т. Д.
Пит Дойл

68

Ни один из опубликованных ответов не сработал для меня. Я пришел с моим собственным решением:

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

3
Это единственный ответ, который на самом деле имеет правильный подход, чтобы предотвратить повторение текста из словарных предложений! Upvote!
hooby3dfx

4
Единственное, EditTextуже могут быть свои фильтры, например, фильтр длины. Поэтому вместо того, чтобы просто переопределять фильтры, вы, скорее всего, захотите добавить свои фильтры к уже существующим.
Алекс Н.

Это все еще актуально? Для меня Android 6.0.1 работает на экранной клавиатуре
XxGoliathusxX

2
Незначительная проблема с этим ответом (или с тем, как работает механизм ввода Android) заключается в том, что пользователю иногда кажется, что backspace не работает, потому что они возвращаются назад по ранее введенным недопустимым символам, которые все еще содержатся в исходном буфере.
jk7

1
Та же проблема, что и с решением Лукаша Сромека: недопустимый символ после пробела заменяется пробелом.
Инго Шальк-Шупп

25

Используйте эту свою работу на 100% и очень просто.

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

В strings.xml

<string name="myAlphaNumeric">abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789</string>

15

Чтобы избежать специальных символов в типе ввода

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

Вы можете установить фильтр для вашего текста редактирования, как показано ниже

edtText.setFilters(new InputFilter[] { filter });

@sathya Вы wc, рады помочь вам :)

1
Не блокирует ни одного из этих персонажей. An ㋛ ☺ ☹ ☻ 〠 シ ッ ツ Ü Ü 〲 〴 ϡ ت ت ⍢ ⍣ ⍤ ⍥ ⍨ ⍩ ὃ ὕ ὣ Ѷ
Анархофашист

7

В дополнение к принятому ответу также можно использовать, например, android:inputType="textCapCharacters"в качестве атрибута <EditText>, чтобы принимать только символы верхнего регистра (и цифры).


5
android: inputType = "textCapCharacters" не ограничивается использованием других символов, таких как '., - "и т. д.
Tvd

Это также только подсказка к методу ввода. Это не ограничивает то, какие символы могут быть введены.
dcow

5

По какой-то причине конструктор класса android.text.LoginFilter имеет пакетную область, поэтому вы не можете напрямую расширять его (даже если он будет идентичен этому коду). Но вы можете расширить LoginFilter.UsernameFilterGeneric! Тогда у вас просто есть это:

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

Это на самом деле не задокументировано, но это часть базовой библиотеки, а источник понятен . Я уже давно пользуюсь им, пока проблем нет, хотя признаюсь, что не пробовал делать что-либо сложное, включая spanables.


5

Это верно, лучший способ исправить это в самом XML-макете, используя:

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

как справедливо указал Флориан Фрелих, он хорошо работает даже для просмотра текста.

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

Просто предостережение, символы, упомянутые в, android:digitsбудут отображаться только, так что будьте осторожны, чтобы не пропустить ни одного набора символов :)


вам нужно определить inputType = "textFilter", тогда только он будет работать правильно.
Шреяш Махаджан

@ShreyashMahajan, это зависит от устройства / приложения клавиатуры? В моем случае inputTypeэто не влияет на фильтрацию.
CoolMind

4

Это простое решение работало для меня, когда мне нужно было запретить пользователю вводить пустые строки в EditText. Конечно, вы можете добавить больше символов:

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

}

как назвать это решение?
zeeks

1

Если у вас есть подкласс InputFilter, вы можете создать свой собственный InputFilter, который бы отфильтровывал любые не алфавитно-цифровые символы.

Интерфейс InputFilter имеет один метод filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend)и предоставляет вам всю необходимую информацию о том, какие символы были введены в EditText, которому он назначен.

После того как вы создали свой собственный InputFilter, вы можете назначить его EditText, вызвав setFilters (...).

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


1

Не обращая внимания на то, с чем сталкиваются другие люди, чтобы правильно обрабатывать предложения словаря, я обнаружил, что следующий код работает.

Источник растет по мере увеличения предложения, поэтому мы должны посмотреть, сколько символов на самом деле мы ожидаем заменить, прежде чем что-то вернуть.

Если у нас нет недопустимых символов, верните null, чтобы произошла замена по умолчанию.

В противном случае нам нужно извлечь действительные символы из подстроки, которая фактически будет помещена в 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;
    } 
}; 

1

чтобы предотвратить слова в тексте редактирования. создать класс, который вы могли бы использовать в любое время.

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

1

Сначала добавьте в strings.xml:

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

XML :

android:digits="@string/vin_code_mask"

Код в Котлине:

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
}

Странно, но странно работает на мягкой клавиатуре эмулятора.

Предупреждение! Следующий код отфильтрует все буквы и другие символы, кроме цифр для программных клавиатур. На смартфонах появится только цифровая клавиатура.

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

Я также , как правило , устанавливается maxLength, filters, inputType.


1

Это старая тема, но у всех намеченных решений есть проблемы (в зависимости от устройства / версии Android / клавиатуры).

РАЗНЫЙ ПОДХОД

Так что в конце концов я пошел по другому пути, вместо того, чтобы использовать InputFilterпроблемную реализацию, которую я использую, TextWatcherи TextChangedListenerиз EditText.

ПОЛНЫЙ КОД (ПРИМЕР)

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

Причина InputFilterне является хорошим решением в Android, потому что это зависит от реализации клавиатуры. Ввод с клавиатуры фильтруется перед тем, как ввод передается в EditText. Но, поскольку некоторые клавиатуры имеют разные реализации для InputFilter.filter()вызова, это проблематично.

С другой стороны TextWatcher, не заботится о реализации клавиатуры, это позволяет нам создать простое решение и быть уверенным, что оно будет работать на всех устройствах.


onTextChangedПросто необходимо public voidперед ним.
Энди

Спасибо за комментарий. Исправлена.
Эяль Биран

1

Я сделал что-то вроде этого, чтобы сделать это простым:

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 ]"), "")
    }
})

Таким образом, мы заменим все ненужные символы в новой части исходной строки пустой строкой.

edit_textПеременной является EditTextобъект , который мы имеем в виду.

Код написан на kotlin .


1
Спасибо! Это решение хорошо работает как при наборе текста, так и при вставке текста.
CoolMind

0

Можно использовать setOnKeyListener. В этом методе мы можем настроить ввод edittext!


0

Так я создал фильтр для поля «Имя» в «Редактировать текст». (Первая буква - CAPS, и после каждого слова допускается только один пробел.

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

Constants.Delimiter.BLANK неизвестен.
CoolMind

0

У меня такой же ответ в 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
        }
    }
}

и получить класс с функцией Extension:

fun EditText.filterByDataType(filterType: Int) {
    this.filters = arrayOf<InputFilter>(InputFilterAlphanumeric(filterType))
}
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.