Android限制EditText只能输入中文或者指定内容的实现

Posted 冷不冷

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android限制EditText只能输入中文或者指定内容的实现相关的知识,希望对你有一定的参考价值。

最近项目中要限制EditText中只能输入中文,之前写过一个限制EditText只能输入中文的实现,不过存在一些问题,而且扩展性不是很好,所以换了一种方法来实现.
先看一下效果图:


具体实现

一般对EditText的操作及处理都是用addTextChangedListener方法来对EditText进行监听,之后在监听方法中去做处理.这里也打算用这个种方法来做,大体的思路是监听EditText中输入的内容,然后将不是中文的部分清除掉,也就是置为空.所以大概应该这样写

 mLimitEt.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) 
            // 1.处理输入的内容s:清除其中不是中文的部分
            ...
            // 2.设置处理完的s
            mLimitEt.setText("处理之后的s");
            

            @Override
            public void afterTextChanged(Editable s) 

            
        );

处理的方法这里先不写,先来看一下这样写会出现的一个问题,运行一下,输入一些内容会发现程序崩溃了,查看崩溃信息,会发现出现了StackOverflowError异常,这是什么原因呢?带着疑问去扒了一下源码(看源码时遇到一个问题,升级完Studio之后,发现无法查看源码了,查了一些资料解决了,也有相同问题的童鞋可以参考下我写的 Mac版Android Studio查看不到源码的解决方法,windows版解决方法也类似)出现异常的位置在 mLimitEt.setText()这句代码上,所以先看一下setText()方法.setText方法在TextView中,看一下实现(这里只关心引起异常的部分,其他部分的内容不讨论)

private void setText(CharSequence text, BufferType type,
                         boolean notifyBefore, int oldlen) 
   ...
   // Text改变前的回调处理
   sendBeforeTextChanged(mText, 0, oldlen, text.length());
   ...
   // Text改变中的回调处理
   sendOnTextChanged(text, 0, oldlen, textLength);
   ...
   // Text改变后的回调处理
   sendAfterTextChanged((Editable) text); 

在setText方法中可以看到这几个方法,然后看一下这些方法做的处理是什么

     /**
     * Not private so it can be called from an inner class without going
     * through a thunk.
     */
       void sendOnTextChanged(CharSequence text, int start, int before, int after) 
        if (mListeners != null) 
            final ArrayList<TextWatcher> list = mListeners;
            final int count = list.size();
            for (int i = 0; i < count; i++) 
                list.get(i).onTextChanged(text, start, before, after);
            
        

        if (mEditor != null) mEditor.sendOnTextChanged(start, after);
    

     /**
     * Not private so it can be called from an inner class without going
     * through a thunk.
     */
    void sendAfterTextChanged(Editable text) 
        if (mListeners != null) 
            final ArrayList<TextWatcher> list = mListeners;
            final int count = list.size();
            for (int i = 0; i < count; i++) 
                list.get(i).afterTextChanged(text);
            
        
        hideErrorIfUnchanged();
    

看一下这些方法,能不能发现点什么,可以看到有一个ArrayList< TextWatcher >对象,先进行判空处理,如果这个对象中存在TextWatcher监听,则逐条进行回调操作.再回头看一下之前写的EditText中回调方法的实现,在回调中,对这个EditText进行了setText操作,因为EditText实现了TextWatcher的回调接口,这样就导致了无限循环 setText->onTextChanged->setText…… 最终导致程序崩溃.那该如何解决这个问题呢.其实很简单,看一下代码

 mLimitEt.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) 
            // 1.处理输入的内容s:清除其中不是中文的部分
            ...
            // 2.删除监听
            mLimitEt.removeTextChangedListener(this);
            // 3.设置处理完的s
            mLimitEt.setText("处理之后的s");
            // 4.重新添加监听
            mLimitEt.addTextChangedListener(this);
            

            @Override
            public void afterTextChanged(Editable s) 

            
        );

在setText之前先删除之前的回调监听,setText时因为没有TextWatcher的监听方法,所以不会出现无限循环的情况,当setText之后再重新添加回调监听,这样就避免了崩溃的产生.之后看一下清除非中文部分的实现,直接看代码

    /**
     * 清除不是中文的内容
     *
     * @param regex
     * @return
     */
    private String clearLimitStr(String regex, String str) 
        return str.replaceAll("[^\\u4E00-\\u9FA5]", "");
    

用了String的replaceAll方法来处理输入的内容(用了正则表达式,使用起来很简单).在onTextChanged和afterTextChanged方法中,得到的输入内容其实是整体的输入内容,所以用replaceAll方法,可以去打印一下这几个方法中的参数,这里就不做了.看一下整体代码

LimitInputTextWatcher:

package com.example.junweiliu.limitinputdemo;

import android.text.Editable;
import android.text.TextWatcher;
import android.widget.EditText;

/**
 * Created by junweiliu on 17/1/6.
 */
public class LimitInputTextWatcher implements TextWatcher 
    /**
     * et
     */
    private EditText et = null;
    /**
     * 筛选条件
     */
    private String regex;
    /**
     * 默认的筛选条件(正则:只能输入中文)
     */
    private String DEFAULT_REGEX = "[^\\u4E00-\\u9FA5]";

    /**
     * 构造方法
     *
     * @param et
     */
    public LimitInputTextWatcher(EditText et) 
        this.et = et;
        this.regex = DEFAULT_REGEX;
    

    /**
     * 构造方法
     *
     * @param et    et
     * @param regex 筛选条件
     */
    public LimitInputTextWatcher(EditText et, String regex) 
        this.et = et;
        this.regex = regex;
    

    @Override
    public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) 

    

    @Override
    public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) 

    

    @Override
    public void afterTextChanged(Editable editable) 
        String str = editable.toString();
        String inputStr = clearLimitStr(regex, str);
        et.removeTextChangedListener(this);
        // et.setText方法可能会引起键盘变化,所以用editable.replace来显示内容
        editable.replace(0, editable.length(), inputStr.trim());
        et.addTextChangedListener(this);

    

    /**
     * 清除不符合条件的内容
     *
     * @param regex
     * @return
     */
    private String clearLimitStr(String regex, String str) 
        return str.replaceAll(regex, "");
    

为了扩展性,提出来了一个类,提供了两个构造方法,如果需要限制其他的特殊内容,可以设置正则的规则.当然如果很简单的话,用EidtText自带的digits属性就可以了.还有一个问题,需要注意,代码中没有用et.setText方法,是因为setText方法可能引起键盘变化异常,所以这里用 editable.replace(0, editable.length(), inputStr.trim());这个方法和setText方法的实现效果是一样的.不过也需要对监听进行处理,原因也是因为会引起无限循环,感兴趣的童鞋可以去看一下.

完整代码

MainActivity:

package com.example.junweiliu.limitinputdemo;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.EditText;

public class MainActivity extends AppCompatActivity 
    /**
     * et
     */
    private EditText mLimitEt;


    @Override
    protected void onCreate(Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mLimitEt = (EditText) findViewById(R.id.et_limit);
        mLimitEt.addTextChangedListener(new LimitInputTextWatcher(mLimitEt));
        // 去除除了a-z  A-Z与0-9和中文的其他符号
//        mLimitEt.addTextChangedListener(new LimitInputTextWatcher(mLimitEt, "[^a-zA-Z0-9\\u4E00-\\u9FA5]"));
    

activity_main:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:paddingBottom="@dimen/activity_vertical_margin"
        android:paddingLeft="@dimen/activity_horizontal_margin"
        android:paddingRight="@dimen/activity_horizontal_margin"
        android:paddingTop="@dimen/activity_vertical_margin"
        tools:context="com.example.junweiliu.limitinputdemo.MainActivity">
    <!--输入框-->
    <!--android:digits="1234567890"-->
    <EditText
            android:id="@+id/et_limit"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerHorizontal="true"
            android:hint="我是一个受限制的输入框"/>
    <!--输入框-->
    <EditText
            android:layout_below="@+id/et_limit"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerHorizontal="true"
            android:text="复制我fuzhiwo845"
            android:hint=""/>
</RelativeLayout>

以上是关于Android限制EditText只能输入中文或者指定内容的实现的主要内容,如果未能解决你的问题,请参考以下文章

android EditText控件中, 如何判断并且限制只能输入数字?

(转)Android EditText限制输入字符的5种实现方式

EditText中输入信息的限制的方法

Android 系统搜索框 如何限制输入字数长度?

EditText限制输入的几种方式及只显示中文汉字的做法

Android EditText输入字数限制总结(包含中文输入内存溢出的解决方法)