20

我正在尝试实现一个EditText将输入限制为仅带有数字的大写字符 [A-Z0-9] 的输入。

我从一些帖子开始使用 InputFilter 方法。但是在这里我在三星 Galaxy Tab 2 上遇到了一个问题,但在模拟器或 Nexus 4 上却没有。

问题是这样的:

  1. 当我输入“A”时,文本显示为“A”,这很好
  2. 现在当我输入“B”时,文本应该是“AB”,但它给了我“AAB”,这看起来很奇怪。

简而言之,它重复字符

这是我正在使用此代码的代码:

public class DemoFilter implements InputFilter {

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

        if (source.equals("")) { // for backspace
            return source;
        }
        if (source.toString().matches("[a-zA-Z0-9 ]*")) // put your constraints
                                                        // here
        {
            return source.toString().toUpperCase();
        }
        return "";
    }
}

XML文件代码:

<EditText
    android:id="@+id/et_licence_plate_1"
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    android:layout_weight="3"
    android:hint="0"
    android:imeOptions="actionNext"
    android:inputType="textNoSuggestions"
    android:maxLength="3"
    android:singleLine="true"
    android:textSize="18px" >
</EditText>

我完全坚持这个,所以这里的任何帮助将不胜感激。

4

10 回答 10

19

字符重复的问题来自 InputFilter 错误的实现。如果替换不应更改,则返回 null:

@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, end, null, sp, 0);
            return sp;
        } else {
            return sb;
        }           
    }
}

private boolean isCharAllowed(char c) {
    return Character.isUpperCase(c) || Character.isDigit(c);
}
于 2013-12-02T10:15:15.010 回答
3

我遇到了同样的问题,在使用此处发布的解决方案修复它之后,具有自动完成功能的键盘仍然存在问题。一种解决方案是将 inputType 设置为“visiblePassword”,但这会减少功能,不是吗?

我能够通过在方法中返回非空结果时filter()使用调用来修复解决方案

TextUtils.copySpansFrom((Spanned) source, start, newString.length(), null, newString, 0);

这会将自动完成范围复制到新结果中,并修复选择自动完成建议时重复的奇怪行为。

于 2016-03-01T14:49:15.217 回答
2

InputFilters可以附加到 Editable S 以限制可以对其进行的更改。请参阅它强调所做的更改而不是它包含的整个文本。

按照下面提到的...

 public class DemoFilter implements InputFilter {

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

            if (source.equals("")) { // for backspace
                return source;
            }
            if (source.toString().matches("[a-zA-Z0-9 ]*")) // put your constraints
                                                            // here
            {
               char[] ch = new char[end - start];

              TextUtils.getChars(source, start, end, ch, 0);

                // make the characters uppercase
                String retChar = new String(ch).toUpperCase();
                return retChar;
            }
            return "";
        }
    }
于 2013-08-30T09:24:45.010 回答
2

以下解决方案还支持自动完成键盘选项

editTextFreeNote.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) {
            String newStr = s.toString();
            newStr = newStr.replaceAll( "[a-zA-Z0-9 ]*", "" );
            if(!s.toString().equals( newStr )) {
                editTextFreeNote.setText( newStr );
                editTextFreeNote.setSelection(editTextFreeNote.getText().length());
            }
        }

        @Override
        public void afterTextChanged(Editable s) {}
    } );
于 2017-11-23T08:43:53.190 回答
2

对我来说也一样,InputFilter重复字符。这是我用过的:

Kotlin版本:

private fun replaceInvalidCharacters(value: String) = value.replace("[a-zA-Z0-9 ]*".toRegex(), "")

textView.addTextChangedListener(object : TextWatcher {
    override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}

    override fun afterTextChanged(s: Editable) {}

    override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
        val newValue = replaceInvalidCharacters(s.toString())
        if (newValue != s.toString()) {
            textView.setText(newValue)
            textView.setSelection(textView.text.length)
        }
    }
})

效果很好。

于 2018-05-07T10:42:41.067 回答
2

我在 Android 的 InputFilter 中发现了许多错误,我不确定这些是错误还是有意为之。但绝对不符合我的要求。所以我选择使用 TextWatcher 而不是 InputFilter

private String newStr = "";

myEditText.addTextChangedListener(new TextWatcher() {
        @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) {
            String str = s.toString();
            if (str.isEmpty()) {
                myEditText.append(newStr);
                newStr = "";
            } else if (!str.equals(newStr)) {
                // Replace the regex as per requirement
                newStr = str.replaceAll("[^A-Z0-9]", "");
                myEditText.setText("");
            }
        }

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

上面的代码不允许用户在 EditText 中输入任何特殊符号。只允许使用大写字母数字字符。

于 2017-07-31T10:33:01.910 回答
0

尝试这个:

class CustomInputFilter implements InputFilter {
    StringBuilder sb = new StringBuilder();

    @Override
    public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {
        Log.d(TAG, "filter " + source + " " + start + " " + end + " dest " + dest + " " + dstart + " " + dend);
        sb.setLength(0);
        for (int i = start; i < end; i++) {
            char c = source.charAt(i);
            if (Character.isUpperCase(c) || Character.isDigit(c) || c == ' ') {
                sb.append(c);
            } else
            if (Character.isLowerCase(c)) {
                sb.append(Character.toUpperCase(c));
            }
        }
        return sb;
    }
}

这也允许在 filter() 方法一次接受多个字符时进行过滤,例如从剪贴板粘贴的文本

于 2013-08-30T12:29:42.213 回答
0

最近我遇到了同样的问题,问题的原因是......如果输入字符串没有变化,那么不要返回源字符串返回 null,某些设备不能正确处理这个问题,这就是字符重复的原因。

在您返回的代码中

return source.toString().toUpperCase();

不要返回 this ,return null;代替return source.toString().toUpperCase();,但这将是一个补丁修复,它不会处理所有场景,对于所有场景,您都可以使用此代码。

public class SpecialCharacterInputFilter implements InputFilter {

    private static final String PATTERN = "[^A-Za-z0-9]";
    // if you want to allow space use this pattern
    //private static final String PATTERN = "[^A-Za-z\\s]";

    @Override
    public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {
        // Only keep characters that are letters and digits
        String str = source.toString();
        str = str.replaceAll(PATTERN, AppConstants.EMPTY_STRING);
        return str.length() == source.length() ? null : str;
    }
}

这段代码中发生了什么,有一个正则表达式,我们将找到除字母和数字之外的所有字符,现在它将用空字符串替换所有字符,然后剩余的字符串将有字母和数字。

于 2019-03-26T07:53:31.607 回答
0

这里大多数答案的问题是它们都弄乱了光标位置。

  • 如果您只是替换文本,您的光标最终会出现在下一个输入字符的错误位置
  • 如果您认为您通过将光标放回末尾来处理该问题,那么他们无法添加前缀文本或中间文本,它们总是在每个键入的字符上跳回到末尾,这是一种糟糕的体验。

你有一个简单的方法来处理它,还有一个更通用的方法来处理它。

简单的方法

          <EditText
            android:id="@+id/itemNameEditText"
            android:text="@={viewModel.selectedCartItemModel.customName}"
            android:digits="abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
            android:inputType="textVisiblePassword"/>

完毕!

可见密码将解决双重回调的问题和类似的问题。此解决方案的问题在于它会删除您的建议和自动完成功能,以及类似的事情。因此,如果您可以摆脱这个方向,请这样做!!!它将消除试图处理所有可能的困难问题的麻烦,哈哈。

艰难的道路

该问题与自动完成触发的 inputfilter 回调结构有关。它很容易复制。只需设置您的 inputType = text,然后输入 abc@,您会看到它被调用了两次,并且如果您试图忽略 @,您最终可以使用 abcabc 而不是 abc。

  1. 您必须处理的第一件事是删除来执行此操作,您必须返回 null 以接受“”,因为这是由删除触发的。
  2. 您必须处理的第二件事是保持删除,因为它会经常更新,但可能会以一长串字符的形式出现,因此您需要在替换文本之前查看文本长度是否缩小,否则最终可能会复制文本而持有删除。
  3. 您需要处理的第三件事是重复回调,通过跟踪先前的文本更改调用以避免获得两次。不用担心,您仍然可以背靠背输入相同的字母,这不会阻止。

这是我的例子。它并不完美,还有一些问题需要解决,但它是一个很好的起点。

以下示例使用数据绑定,但如果这是您的风格,欢迎您只使用不使用数据绑定的 intentFilter。用于仅显示重要部分的缩写 UI。

在此示例中,我仅限制为字母、数字和空格。我能够在疯狂地敲打安卓键盘的同时让一个分号出现一次。因此,我认为仍然需要进行一些调整。

免责声明

--我还没有测试过自动完成

--我没有用建议进行测试

--我没有测试过复制/粘贴

--这个解决方案是一个 90% 的解决方案来帮助你,而不是一个经过实战考验的解决方案

文件

<layout
    xmlns:bind="http://schemas.android.com/apk/res-auto"
    >

<EditText
     bind:allowAlphaNumericOnly="@{true}

对象文件

@JvmStatic
@BindingAdapter("allowAlphaNumericOnly")
fun restrictTextToAlphaNumericOnly(editText: EditText, value: Boolean) {
    val tagMap = HashMap<String, String>()
    val lastChange = "repeatCheck"
    val lastKnownSize = "handleHoldingDelete"

    if (value) {
        val filter = InputFilter { source, start, end, dest, dstart, dend ->
            val lastKnownChange = tagMap[lastChange]
            val lastKnownLength = tagMap[lastKnownSize]?.toInt()?: 0

            //handle delete
            if (source.isEmpty() || editText.text.length < lastKnownLength) {
                return@InputFilter null
            }

            //duplicate callback handling, Android OS issue
            if (source.toString() == lastKnownChange) {
                return@InputFilter ""
            }

            //handle characters that are not number, letter, or space
            val sb = StringBuilder()
            for (i in start until end) {
                if (Character.isLetter(source[i]) || Character.isSpaceChar(source[i]) || Character.isDigit(source[i])) {
                    sb.append(source[i])
                }
            }

            tagMap[lastChange] = source.toString()
            tagMap[lastKnownSize] = editText.text.length.toString()

            return@InputFilter sb.toString()
        }

        editText.filters = arrayOf(filter)
    }
}
于 2021-12-24T18:32:18.320 回答
0

我以前遇到过这个问题几次。在 xml 中设置某些类型的 inputTypes 是问题的根源。要在没有任何额外逻辑的情况下解决它,InputFilter或者TextWatcher只是在代码中设置输入类型,而不是像这样的 xml:

editText.setInputType(getInputType() | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS);

于 2019-02-21T07:38:31.487 回答