最近项目要求EditText输入文字时过滤掉中文字符、空格等特殊字符,上网查了好多资料,最后决定使用IputFilter来实现这一功能,写一篇博客来记录一下自己的心得
先上效果图
主要代码
public class NoMenuEditText extends EditText {
private final Context context;
private int maxLength;//自定义属性判断是输入字符的最大长度
private boolean allow_blank;//自定义属性判断是否允许输入空格,默认false
private boolean allow_chinese;//自定义属性判断是否允许输入中文
public NoMenuEditText(Context context, AttributeSet attrs) {
super(context, attrs);
this.context = context;
init(attrs);
}
public NoMenuEditText(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
this.context = context;
init(attrs);
}
private void init(AttributeSet attrs) {
final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.NoMenuEditText);//加载自定义styleable文件
maxLength = a.getInteger(R.styleable.NoMenuEditText_maxLength,0);//读取自定义属性输入字符的最大长度
allow_blank = a.getBoolean(R.styleable.NoMenuEditText_allow_blank, false);//读取自定义属性是否允许输入空格
allow_chinese = a.getBoolean(R.styleable.NoMenuEditText_allow_chinese,false);//读取自定义属性是否允许输入中文
if (maxLength > 0){
setFilters(new InputFilter[]{new LengthFilter(maxLength),new InputFilter() {
@Override
public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {
if (!allow_blank){
if (source.equals(" ")) {
return "";
}
}
if (!allow_chinese){
if (checkNameChese(source.toString())){
return "";
}
}
return null;
}
}});
}else {
setFilters(new InputFilter[]{new InputFilter() {
@Override
public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {
if (!allow_blank){
if (source.equals(" ")) {
return "";
}
}
if (!allow_chinese){
if (checkNameChese(source.toString())){
return "";
}
}
return null;
}
}});
}
this.setCustomSelectionActionModeCallback(new ActionModeCallbackInterceptor());
this.setLongClickable(false);
}
private class ActionModeCallbackInterceptor implements ActionMode.Callback {
private final String TAG = NoMenuEditText.class.getSimpleName();
@Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
return false;
}
@Override
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
return false;
}
@Override
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
return false;
}
@Override
public void onDestroyActionMode(ActionMode mode) {
}
}
public boolean stringFilter(String str){
// 只允许字母、数字和汉字
String regEx = "[^\u4E00-\u9FA5]";
Pattern p = Pattern.compile(regEx);
Matcher m = p.matcher(str);
return m.matches();
}
/**
* 判定输入汉字
* @param c
* @return
*/
public boolean isChinese(char c) {
Character.UnicodeBlock ub = Character.UnicodeBlock.of(c);
if (ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS
|| ub == Character.UnicodeBlock.CJK_COMPATIBILITY_IDEOGRAPHS
|| ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_A
|| ub == Character.UnicodeBlock.GENERAL_PUNCTUATION
|| ub == Character.UnicodeBlock.CJK_SYMBOLS_AND_PUNCTUATION
|| ub == Character.UnicodeBlock.HALFWIDTH_AND_FULLWIDTH_FORMS) {
return true;
}
return false;
}
/**
* 检测String是否全是中文
* @param name
* @return
*/
public boolean checkNameChese(String name)
{
boolean res=false;
char [] cTemp = name.toCharArray();
for(int i=0;i<name.length();i++)
{
if(isChinese(cTemp[i]))
{
res=true;
break;
}
}
return res;
}
}
自定义属性文件在attrs.xml文件中
<declare-styleable name="NoMenuEditText">
<attr name="allow_blank" format="boolean" />
<attr name="maxLength" format="integer" />
<attr name="allow_chinese" format="boolean" />
</declare-styleable>
XML布局中的使用方式
<com.xfzd.client.user.view.NoMenuEditText
xmlns:aa="http://schemas.android.com/apk/res-auto/com.xfzd.client"
aa:allow_blank="true"
aa:maxLength="20"
代码分析
上述代码8-11,14-18分别是自定义EditText构造函数当中都有一个初始化init(attrs)方法,我们在此方法中实现自定义属性的解析,和自定义InputFilter过滤器的实现
20行代码,可能到家会对自定义属性maxLength感到疑惑,因为EditText有一个属性android:maxLength=”19”为什么这里还要设置一个自定义属性,岂不是多此一举,我子啊这里要解释一下android:maxLength=”19”这个属性是在EditText的父类也就是TextView中使用到的,我们进入到TextView的代码中我们看一看
这行代码实在TextView的构造函数中,其中maxLength就是解析TextView属性android:maxLength的到的值,我们在这里可以看到此处会根据maxLength的长度来用不同的策略来设置InputFilters这个数组,最后在通过setFilters()这个方法去设置TextView的过滤器,我们看到maxLength长度大于0时,会给InputFilter[]数组设置一个成员new InputFilter.LengthFilter(maxlength) TextView和EidtText正是由于设置了这样一个InputFilter才实现文字长度的限制
进入到LengthFilter的源码中我们发现LengthFilter 实现了 InputFilter这个接口并实现了filter()这个方法,因此才能实现对TextView、EditText的字符长度的限制。我门今天实现的就是通过自定InputFilter实现EditText过滤中文、空格等特殊字符,而这其中的关键也是在于实现InputFilter接口通过实现filter()这个方法,来达到实现字符的过滤
我们还是从最关键的地方Init函数分析
private void init(AttributeSet attrs) {
final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.NoMenuEditText);//加载自定义styleable文件
maxLength = a.getInteger(R.styleable.NoMenuEditText_maxLength,0);//读取自定义属性输入字符的最大长度
allow_blank = a.getBoolean(R.styleable.NoMenuEditText_allow_blank, false);//读取自定义属性是否允许输入空格
allow_chinese = a.getBoolean(R.styleable.NoMenuEditText_allow_chinese,false);//读取自定义属性是否允许输入中文
if (maxLength > 0){
setFilters(new InputFilter[]{new LengthFilter(maxLength),new InputFilter() {
@Override
public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {
if (!allow_blank){
if (source.equals(" ")) {
return "";
}
}
if (!allow_chinese){
if (checkNameChese(source.toString())){
return "";
}
}
return null;
}
}});
}else {
setFilters(new InputFilter[]{new InputFilter() {
@Override
public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {
if (!allow_blank){
if (source.equals(" ")) {
return "";
}
}
if (!allow_chinese){
if (checkNameChese(source.toString())){
return "";
}
}
return null;
}
}});
}
this.setCustomSelectionActionModeCallback(new ActionModeCallbackInterceptor());
this.setLongClickable(false);
}
6行判断maxLength 属性是否大于0(默认为0,大于0说明在布局xml中view的属性中设置过),接下来第7行setFilters(new InputFilter[])我们看到该方法的参数是一个InputFilter[]数组,实际上就是说可以通过多组合InputFilter来实现各种过滤的效果,我们实现的就是过滤中文字符和空字符串,所以我在数组的第二个变量中重写InputFilter的filter方法来实现这样的效果。filter过滤是通过返回值来决定的
- return “”
表示过滤输入的内容 - return null
表示不过滤输入的内容
我们看10行先判断是不允许输入空字符串,进入11行判断输入内容是为空字符串过滤,15行判断不允许输入中文进入16行判断输入内容为中文字符过滤,否则进入20行不进行过滤