【问题标题】:How do I use InputFilter to limit characters in an EditText in Android?如何在 Android 中使用 InputFilter 限制 EditText 中的字符?
【发布时间】:2011-03-21 22:02:05
【问题描述】:

我只想将字符限制为 0-9、a-z、A-Z 和空格键。设置 inputtype 我可以限制为数字,但我无法弄清楚 Inputfilter 浏览文档的方式。

【问题讨论】:

    标签: android android-edittext


    【解决方案1】:

    我在另一个论坛上找到了这个。像冠军一样工作。

    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(如 4.0+)中效果不佳。他们在键盘上方引入字典建议。当您键入一个常用词(比如“the”)后跟一个非法字符(比如“-”)时,整个词都会被删除,然后在你输入另一个字符(甚至是允许的字符,比如“blah”)之后过滤器返回“”并且字段中不显示任何字符。这是因为该方法在源参数中获取了 SpannableStringBuilder,其中包含“the-blah”,并且开始/结束参数跨越整个输入字符串......请参阅我的答案以获得更好的解决方案。
    • 在那个例子中,它返回“”,我认为它应该返回应该显示的文本。即您应该删除非法字符并返回您想要显示的字符串。 developer.android.com/reference/android/widget/…, android.view.KeyEvent)
    • @AndrewMackenzie 如果输入字符,例如逗号-',',这是不合法的,我想删除它,如何修复上面的代码。
    • !Character.isLetterOrDigit(source.charAt(i)) && !Character.isSpaceChar(source.charAt(i)) 允许空格
    • @ŁukaszSromek 在较新的 android,尤其是三星设备中,您可以在 xml 文件中使用一行来禁用文本自动完成(如果适合您的需要)。您可以尝试 android:inputType="textVisiblePassword" 禁用真正问题的自动完成功能。这不是正确的方法,但它确实有效。干杯!
    【解决方案2】:

    InputFilters 在显示字典建议的 Android 版本中有点复杂。您有时会在 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();
            }
        }
    }
    

    【讨论】:

    • 您是否有理由不想对源进行子序列化?你觉得这样做有什么问题吗(为了只允许字母数字加上一些特殊字符):String replacement = source.subSequence(start, end).toString(); return replacement.replaceAll("[^A-Za-z0-9_\\-@]", "");
    • 这没有考虑出现重复字典建议文本的问题。 @serwus 在他们的回答中指出了这一点。如果在这两种情况下都没有进行任何修改,基本上你应该返回 null。
    • 这段代码正如卢卡斯所说的那样完美(在上面的答案中),但我遇到了一些非字典单词的问题。当我输入 chiru 时,它会显示 3 次,如 chiruchiruchiru。如何解决?它也需要空格但是如何限制下一个空格?
    • 由于某种原因,当source instanceof SpannableStringBuilder 时,输入 AB 给了我 AAB 就像尝试上一个答案时一样。幸运的是,我能够通过使用下面的@florian 解决方案来解决它。
    • @hooby3dfx 是绝对正确的。虽然它最终确实能够正确地获取字符串,但当您使用字典/单词完成时它会搞砸。
    【解决方案3】:

    容易多了:

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

    【讨论】:

    • 虽然这似乎是一个完美的答案,但根据文档有一个问题:“对于 KeyListener 的所有实现,此类仅与硬件键盘有关。软件输入法没有义务触发此类中的方法。”我认为 InputFilter 可能是一种更好的方法,尽管更复杂。
    • 很棒的解决方案只想添加您无需在两者之间提供","。你可以使用类似"0123456789qwertzuiopasdfghjklyxcvbnmQWERTZUIOPASDFGHJKLYXCVBNM"
    • 不是多语言解决方案
    • 如果您打算在您的应用中使用翻译。不要使用这个解决方案!
    • 似乎这可能不适用于imeOptions="actionNext"等。
    【解决方案4】:

    所有发布的答案都不适合我。我有自己的解决方案:

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

    【讨论】:

    • 这是唯一真正具有正确方法来防止字典建议中的重复文本的答案!点赞!
    • 唯一的事情,EditText 已经可以拥有自己的过滤器,例如长度过滤器。因此,您很可能希望将您的过滤器添加到现有的过滤器中,而不是仅仅覆盖过滤器。
    • 这仍然是最新的吗?对我来说,Android 6.0.1 它适用于屏幕键盘
    • 这个答案(或 Android 的输入机制如何工作)的一个小问题是,退格有时对用户来说似乎不起作用,因为它们在退格之前输入的无效字符仍保留在源缓冲区。
    • 与 Łukasz Sromek 的解决方案相同的问题:空格后面的无效字符被空格替换。
    【解决方案5】:

    100% 满足您的需要,而且非常简单。

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

    在strings.xml中

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

    【讨论】:

    • 我相信你错过了这里的空格,但当然你只需要在“myAlphaNumeric”中添加一个空格就可以了。
    【解决方案6】:

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

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

    【讨论】:

    • 不阻止任何这些字符。 ㋡㋛ ☺ ☹ ☻ 〠 シッツ ヅ Ü 〲 〴 ϡ ﭢ ت ⍡ ⍢ ⍣ ⍤ ⍥ ⍨ ⍩ ὃ ὕ ὣ Ѷ
    【解决方案7】:

    为了简单起见,我做了这样的事情:

    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

    【讨论】:

    • 谢谢!此解决方案在键入和粘贴文本时效果很好。
    • 这应该是公认的答案!执行TextWatcher 将发生StackOverFlowError,除非执行以下检查,如下所示:link.medium.com/YfjQwprHUhb
    • 惊人的解决方案。将号码从拨号器粘贴到 EditText 时效果特别好
    【解决方案8】:

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

    【讨论】:

    • android:inputType="textCapCharacters" 不限制使用其他字符,如 '., -" 等。
    • 也只是输入法的提示。它不限制允许输入的字符。
    【解决方案9】:

    没错,在 XML 布局本身中修复它的最佳方法是:

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

    正如 Florian Fröhlich 正确指出的那样,它甚至适用于文本视图。

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

    请注意,android:digits 中提到的字符只会显示,所以请注意不要错过任何一组字符:)

    【讨论】:

    • 你需要定义 inputType="textFilter" 然后它才能正常工作。
    • @ShreyashMahajan,它是否取决于设备/键盘应用程序?在我的情况下,inputType 不会影响过滤。
    【解决方案10】:

    先加入strings.xml:

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

    XML:

    android:digits="@string/vin_code_mask"
    

    Code 在 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

    【讨论】:

      【解决方案11】:

      由于某种原因,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;
          }
      }
      

      这并没有真正记录,但它是核心库和source is straightforward 的一部分。我已经使用它一段时间了,到目前为止没有任何问题,尽管我承认我没有尝试过任何涉及跨度的复杂操作。

      【讨论】:

        【解决方案12】:

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

        【讨论】:

        • 如何调用这个解决方案?
        【解决方案13】:

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

        val regex = Regex("[a-zA-Z\\d ]")
            
        editText.filters = arrayOf(InputFilter { source, _, _, _, _, _ ->
            source.filter { regex.matches(it.toString()) }
        })
        

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

        【讨论】:

          【解决方案14】:

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

          InputFilter 接口有一个方法,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)

          【讨论】:

            【解决方案15】:

            忽略其他人处理过的跨度内容,为了正确处理字典建议,我发现以下代码有效。

            来源随着建议的增加而增加,因此我们必须先查看它实际上希望我们替换多少个字符,然后再返回任何内容。

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

            【讨论】:

              【解决方案16】:

              防止在edittext中出现单词。 创建一个你可以随时使用的类。

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

              【讨论】:

                【解决方案17】:

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

                不同的方法

                所以最终我采用了不同的方法,而不是使用有问题的InputFilter 实现,而是使用TextWatcherTextChangedListenerTextChangedListener

                完整代码(示例)

                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
                【解决方案18】:

                可以使用setOnKeyListener。在这个方法中,我们可以自定义输入edittext

                【讨论】:

                  【解决方案19】:

                  这就是我为编辑文本中的名称字段创建过滤器的方式。(第一个字母是大写字母,每个单词后只允许一个空格。

                  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 未知。
                  【解决方案20】:

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

                  【讨论】:

                    猜你喜欢
                    • 1970-01-01
                    • 2012-01-11
                    • 2011-03-24
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    相关资源
                    最近更新 更多