如何在 Android 中使用 InputFilter 来限制 EditText 中的字符?

我想限制字符为0-9,A-Z,A-Z 和空格键。设置输入类型我可以限制为数字,但我不知道 Inputfilter 浏览文档的方式。

189448 次浏览

如果子类化 InputFilter,您可以创建自己的 InputFilter,它将过滤掉任何非字母数字字符。

InputFilter Interface 有一个方法 filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend),它为您提供了所有需要了解的信息,包括将哪些字符输入到分配给它的 EditText 中。

创建了自己的 InputFilter 之后,可以通过调用 setFilters (...)将其分配给 EditText。

Http://developer.android.com/reference/android/text/inputfilter.html#filter (java.lang. charSequence ,int,int,android.text. Spanned,int,int)

我在另一个论坛上找到的,效果很好。

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

除了可接受的答案之外,还可以使用如: android:inputType="textCapCharacters"作为 <EditText>的属性,以便只接受大写字母(和数字)。

使用 setOnKeyListener是可能的。在这个方法中,我们可以定制输入 edittext

在显示字典建议的 Android 版本中,InputFilter有点复杂。有时在 source参数中得到一个 SpannableStringBuilder,有时得到一个简单的 String

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

容易得多:

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

因为某些原因,安卓短信。LoginFilter 类的构造函数是包作用域的,因此不能直接扩展它(即使它与此代码完全相同)。但是您可以扩展 LoginFilter。用户名过滤器通用!然后你只有这个:

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

这并没有真正的文档记录,但它是核心库和 来源很简单的一部分。我已经使用它有一段时间了,到目前为止没有问题,尽管我承认我还没有尝试做任何复杂的涉及可跨越的事情。

当我需要防止用户在 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});


}

所有发布的答案都不适合我,我有自己的解决办法:

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

它是正确的,在 XML 布局中修复它的最好方法是:

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

Florian Fröhlich 正确地指出,它甚至对文本视图也很有效。

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

只是提醒一句,在 android:digits中提到的字符只会显示,所以只是要小心不要错过任何字符集:)

为了正确处理字典建议,忽略其他人已经处理过的 span 内容,我发现下面的代码可以工作。

源代码随着建议的增长而增长,所以在返回任何内容之前,我们必须查看它实际上期望我们替换多少个字符。

如果没有任何无效字符,则返回 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;
}
};

防止编辑文本中的单词。 创建一个你可以随时使用的类。

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

使用它的工作100% 你的需要和非常简单。

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

在 strings.xml 中

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

避免输入类型中的特殊字符

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

这是一个老线程,但目标解决方案都有问题(取决于设备/Android 版本/键盘)。

不同的方法

因此,最终我使用了一种不同的方法,而不是使用 InputFilter有问题的实现,我使用的是 TextWatcherEditTextTextChangedListener

完整代码(示例)

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不关心键盘实现,它允许我们创建一个简单的解决方案,并确保它将在所有设备上工作。

这就是我在 EditText 中为 Name 字段创建筛选器的方法。(第一个字母是大写字母,每个单词后面只能有一个空格。

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

首先加入 strings.xml:

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

XML :

android:digits="@string/vin_code_mask"

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
}

奇怪,但是它在模拟器的软键盘上工作得很奇怪。

警告!下面的代码将过滤除软件键盘数字以外的所有字母和其他符号。只有数字键盘会出现在智能手机上。

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

我也经常设置 maxLengthfiltersinputType

我在 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
}
}
}

并得到一个具有扩展函数的类:

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

为了简单起见,我做了这样一件事:

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编写的。

您可以在正则表达式中指定需要的字符,并在 InputFilter 中使用它:

val regex = Regex("[a-zA-Z\\d ]")
    

editText.filters = arrayOf(InputFilter { source, _, _, _, _, _ ->
source.filter { regex.matches(it.toString()) }
})

注意,我没有使用 \w字符类,因为它包含下划线 _