EditText过滤特殊符号
序言
在开发过程中总是会遇到产品要求某个输入框只能输入特定的字符。因为这些特殊字符作为url连接参数,sql语句参数等地方会有问题。
文章目录
- EditText过滤特殊符号
- 序言
- 需求如下
- 思路
- 实践
- 1. 使用过滤器
- 监听点击事件
- 直接通过监听EditText的文字变化
- 思路
- 代码如下(kotlin)
- 结语
需求如下
- 只能输入某些特定的字符
- 在用户输入不正确的字符的时候不显示这些错误字符
- 不能有奇怪的bug
思路
那么这边会快速的想到三种解决方案
- 过滤器,使用过滤器InputFilter可以直接过滤掉不想要的字符
- 监听键盘点击事件,只让用户点击需要的按键才有反应
- 监听EditText输入框的变化
实践
我们这边的案例需求为可以输入数字、英文、汉字,不能输入任何中英文标点符号,以及emoji表情。
1. 使用过滤器
那么我们在网络上找到了两种实现方案,一种是直接继承InputFilter另一种是继承InputFilter的子类,使用方法如下(kotlin代码):
1
2editText.filters = arrayOf(EtInputFilters())
下面是InputFilter(Java)实现类:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243import android.text.InputFilter; import android.text.Spanned; import android.text.TextUtils; import java.util.regex.Matcher; import java.util.regex.Pattern; public class EtInputFilters implements InputFilter { /** * 限制输入的最大值 */ public static final int TYPE_MAXNUMBER = 1; /** * 限制输入最大长度 */ public static final int TYPE_MAXLENGTH = 2; /** * 限制输入小数位数 */ public static final int TYPE_DECIMAL = 3; /** * 限制输入最小整数 */ public static final int TYPE_MINNUMBER = 4; /** * 限制输入手机号 */ public static final int TYPE_PHONENUMBER = 5; /** * 限制输入数字,汉字,英文 */ public static final int TYPE_NORMAL = 6; private Pattern mPattern; private double mMaxNum; //最大数值 private int mMaxLength; //最大长度 private int mType = 0; public EtInputFilters(int type) { this.mType = type; } @Override public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) { switch (mType) { case TYPE_MAXNUMBER: return filterMaxNum(source, start, end, dest, dstart, dend); case TYPE_MAXLENGTH: return filterMaxLength(source, start, end, dest, dstart, dend); case TYPE_DECIMAL: return filterDecimal(source, dest, dstart, dend); case TYPE_MINNUMBER: return filterMinnum(source, dest, dstart); case TYPE_PHONENUMBER: return filterPhoneNum(source, dest, dstart); case TYPE_NORMAL: return stringFilter(source); } return source; } /** * 最大值的限制 * * @param min 允许的最小值 * @param maxNum 允许的最大值 * @param numOfDecimals 允许的小数位 */ public EtInputFilters setMaxNum(int min, double maxNum, int numOfDecimals) { this.mMaxNum = maxNum; this.mPattern = Pattern.compile("^" + (min < 0 ? "-?" : "") + "[0-9]*\.?[0-9]" + (numOfDecimals > 0 ? ("{0," + numOfDecimals + "}$") : "*")); return this; } /** * 过滤最大值 */ private CharSequence filterMaxNum(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) { if (source.equals(".")) { if (dstart == 0 || !(dest.charAt(dstart - 1) >= '0' && dest.charAt(dstart - 1) <= '9') || dest.charAt(0) == '0') { return ""; } } if (source.equals("0") && (dest.toString()).contains(".") && dstart == 0) { return ""; } StringBuilder builder = new StringBuilder(dest); builder.delete(dstart, dend); builder.insert(dstart, source); if (!mPattern.matcher(builder.toString()).matches()) { return ""; } if (!TextUtils.isEmpty(builder)) { double num = Double.parseDouble(builder.toString()); if (num > mMaxNum) { return ""; } } return source; } /** * 设置最大长度 * * @param maxLength 最大长度 */ public EtInputFilters setMaxNum(int maxLength) { this.mMaxLength = maxLength; return this; } /** * 过滤最大长度 */ private CharSequence filterMaxLength(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) { int keep = mMaxLength - (dest.length() - (dend - dstart)); if (keep <= 0) { return ""; } else if (keep >= end - start) { return null; // keep original } else { keep += start; if (Character.isHighSurrogate(source.charAt(keep - 1))) { --keep; if (keep == start) { return ""; } } return source.subSequence(start, keep); } } /** * 设置可输入小数位数 * * @param decimal 允许的小数位 */ public EtInputFilters setDecimal(int decimal) { this.mPattern = Pattern.compile("^[0-9]*\.?[0-9]" + (decimal > 0 ? ("{0," + decimal + "}$") : "*")); return this; } /** * 过滤小数 */ private CharSequence filterDecimal(CharSequence source, Spanned dest, int dstart, int dend) { if (source.equals(".")) { if (dstart == 0 || !(dest.charAt(dstart - 1) >= '0' && dest.charAt(dstart - 1) <= '9') || dest.charAt(0) == '0') { return ""; } } if (source.equals("0") && (dest.toString()).contains(".") && dstart == 0) { //防止在369.369的最前面输入0变成0369.369这种不合法的形式 return ""; } StringBuilder builder = new StringBuilder(dest); builder.delete(dstart, dend); builder.insert(dstart, source); if (!mPattern.matcher(builder.toString()).matches()) { return ""; } return source; } /** * 设置只能输入整数,限制最小整数 * * @param minnum 最小整数 */ public EtInputFilters setMinnumber(int minnum) { this.mPattern = Pattern.compile("^" + (minnum < 0 ? "-?" : "") + "[0-9]*$"); return this; } /** * 过滤整数 */ private CharSequence filterMinnum(CharSequence source, Spanned dest, int dstart) { StringBuilder builder = new StringBuilder(dest); builder.insert(dstart, source); if (!mPattern.matcher(builder.toString()).matches()) { return ""; } return source; } /** * 设置只能输入手机号 * * @return */ public EtInputFilters setPhone() { this.mPattern = Pattern.compile("^((13[0-9])|(15[^4])|(18[0-9])|(17[0-8])|(1[57]))\d{8}$"); return this; } /** * 过滤手机号 */ private CharSequence filterPhoneNum(CharSequence source, Spanned dest, int dstart) { StringBuilder builder = new StringBuilder(dest); builder.insert(dstart, source); int length = builder.length(); if (length == 1) { if (builder.charAt(0) == '1') { return source; } else { return ""; } } if (length > 0 && length <= 11) { if (mPattern.matcher(builder.toString()).matches()) { return source; } else { return ""; } } return ""; } public CharSequence stringFilter(CharSequence source) { // 只允许字母、数字和汉字 String regEx = "[^a-zA-Z0-9u4E00-u9FA5]";//正则表达式 Pattern p = Pattern.compile(regEx); Matcher m = p.matcher(source); return m.replaceAll("").trim(); } }
那么这个方案有致命的缺陷,在华为mate10、华为mate20、还有部分vivo手机上。他们自带的原皮百度版输入法,百度输入法vivo版,出现了按删除按钮edittext也会显示联想词汇的问题,以及英文输入法界面“.”和“?”这两个按钮点击会直接删除已有的文字。 经过一番搜索之后发现是百度输入的bug,只要换个输入法皮肤就好了,但毕竟不能逃避问题,那我们用下面这个filter
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35import android.text.LoginFilter; public class MyInputFilter extends LoginFilter.UsernameFilterGMail { public MyInputFilter() { super(); } @Override public boolean isAllowed(char c) { // return true; if ('0' <= c && c <= '9') return true; if ('a' <= c && c <= 'z') return true; if ('A' <= c && c <= 'Z') return true; if ('.' == c || '?' == c) return false; else return isChineseByBlock(c); } //使用UnicodeBlock方法判断 public boolean isChineseByBlock(char c) { Character.UnicodeBlock ub = Character.UnicodeBlock.of(c); return ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS || ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_A || ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_B || ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_C || ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_D || ub == Character.UnicodeBlock.CJK_COMPATIBILITY_IDEOGRAPHS || ub == Character.UnicodeBlock.CJK_COMPATIBILITY_IDEOGRAPHS_SUPPLEMENT; } }
那么问题来了,这个还是有英文输入法界面“.”和“?”这两个按钮点击会直接删除已有的文字。 的问题这是不能忍的。
那么到这里九十九步差一步就实现需求了,我们想到了监听点击事件来屏蔽掉.和?这两个按钮。
监听点击事件
那么好onkeydown还有其他的两个key事件全部监听失败没有抓取到任何的键盘输入信息,至此以上流程走不通。单独的监听键盘点击也是不可取的,因为你要屏蔽的按钮也可能会联想出表情包。
直接通过监听EditText的文字变化
这里就有个问题了就是光标的处理,一开始用上面的办法就是为了避免光标的计算问题才迂回处理的。
思路
- changeListener有start这个值那么你就可以根据这个值去设置光标而不会IndexOutOfBoundException
- 在变化的一瞬间就干掉不符合规则的输入
- 如果本身输入框里面有文字,那么第一次显示的时候就要把不符合的规则的文字全删掉,然后把光标放到字符串最后一格
代码如下(kotlin)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49override fun afterTextChanged(s: Editable?) { } override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) { } override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) { var string = s.toString() val chars = string.toCharArray() for (char in chars) { if (!isAllowed(char)) { string = string.replace(char.toString(), "") } } if (string != s.toString()) { text.setText(string) text.setSelection(start) } } private fun isAllowed(c: Char): Boolean { // return true; if (c in '0'..'9') return true if (c in 'a'..'z') return true if (c in 'A'..'Z') return true return if ('.' == c || '?' == c) false else isChineseByBlock(c) } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_edit) text.addTextChangedListener(this) var content = intent.getStringExtra(Constant.CONTENT) val chars = content.toCharArray() for (char in chars) { if (!isAllowed(char)) { content = content.replace(char.toString(), "") } } text.setText(content) text.setSelection(text.length()) }
以上代码直接设置到对应的EditText上就可以了。在每次设置EditText文字的时候需要自己手动的去删除不符合标准的字符比如上面的onCreate方法里面。试运行不兼容的机型没有任何问题,试运行本来就没啥问题的小米和nexus也没有问题。
结语
谷歌本身给的过滤器还是机型适配有问题的,大家还是不要偷懒自己处理光标和第一次显示的过滤来实现吧。希望我的文章会让你们少躺坑。
最后
以上就是任性芝麻最近收集整理的关于EditText过滤特殊符号EditText过滤特殊符号的全部内容,更多相关EditText过滤特殊符号EditText过滤特殊符号内容请搜索靠谱客的其他文章。
发表评论 取消回复