#Android封装EditText(正则表达式)
##EditText方法
##BaseFilter
##过滤器
##封装EditTextView
##过滤器的长度优化
###1.EditText方法
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:digits="0123"
android:inputType="number"/>
digits是显示了输入的字母,inputType是设置了输了类型,虽然设置了输入了number,但是由于digits设置了"0123",所以只有输入0,1,2,3才会显示在EditText上.
查看一下系统方法的介绍setInputType
/**
* Set the type of the content with a constant as defined for {@link EditorInfo#inputType}. This
* will take care of changing the key listener, by calling {@link #setKeyListener(KeyListener)},
* to match the given content type. If the given content type is {@link EditorInfo#TYPE_NULL}
* then a soft keyboard will not be displayed for this text view.
*
* Note that the maximum number of displayed lines (see {@link #setMaxLines(int)}) will be
* modified if you change the {@link EditorInfo#TYPE_TEXT_FLAG_MULTI_LINE} flag of the input
* type.
*
* @see #getInputType()
* @see #setRawInputType(int)
* @see android.text.InputType
* @attr ref android.R.styleable#TextView_inputType
*/
public void setInputType(int type) {
final boolean wasPassword = isPasswordInputType(getInputType());
final boolean wasVisiblePassword = isVisiblePasswordInputType(getInputType());
setInputType(type, false);
final boolean isPassword = isPasswordInputType(type);
1.内容的类型是由一个常量定义(EditorInfo的InputType)
2.这个InputType有可能会被setKeyListener方法更改.
3.如果这个类型设置为InputType.TYPE_NULL,软键盘上输入任何内容都不会显示在EditText上.
4.如果设置类型为InputType.TYPE_TEXT_FLAG_MULTI_LINE,maxLine属相将会改变,相当于调用了setMaxLines(int)方法.
<EditText
android:id="@+id/test_edit"
android:layout_width="100dp"
android:layout_height="wrap_content"/>
在布局文件上添加了一个EditText.
EditText test_edit = (EditText) findViewById(R.id.test_edit);
test_edit.setInputType(InputType.TYPE_CLASS_NUMBER);
此时是默认的弹出了数字键盘,在键盘上输入数字才可以显示到EditText上.
EditText test_edit = (EditText) findViewById(R.id.test_edit);
test_edit.setInputType(InputType.TYPE_CLASS_NUMBER);
test_edit.setKeyListener(new DigitsKeyListener() {
@Override
public int getInputType() {
return InputType.TYPE_CLASS_TEXT;
}
@Override
protected char[] getAcceptedChars() {
return "abc".toCharArray();
}
});
if (test_edit.getInputType() == InputType.TYPE_CLASS_NUMBER) {
test_edit.setText("0");
} else if (test_edit.getInputType() == InputType.TYPE_CLASS_TEXT) {
test_edit.setText("a");
}
此时EditeText上显示的是a,那么此时的InputType是TYPE_CLASS_TEXT,说明setKeyListener方法改变了输入类型.
getAcceptedChars进行了过滤,那么现在只能输入a,b,c,才可以显示到EditText上面
同时也可以看出,如果getInputType和getAcceptedChars方法都重写了,只是起到了弹出默认键盘的作用,至于什么字符才可以显示到EditText上面,是getAcceptedChars方法决定的.
还有也可以使用下面方法只是设置了可以输入的字符
String strIds = "abc";
test_edit.setKeyListener(DigitsKeyListener.getInstance(strIds));
也就是如果setInputType方法和setKeyListener方法中的getInputType方法(重写),哪一个在后面,那个起到作用
/**
* Sets the list of input filters that will be used if the buffer is
* Editable. Has no effect otherwise.
*
* @attr ref android.R.styleable#TextView_maxLength
*/
public void setFilters(InputFilter[] filters) {
if (filters == null) {
throw new IllegalArgumentException();
}
mFilters = filters;
if (mText instanceof Editable) {
setFilters((Editable) mText, filters);
}
}
setFilters方法设过滤器,只会影响输入框是否可以编辑,不会影响其他的.
###2.BaseFilter
public class BaseFilter implements InputFilter {
/**
* @param editText 输入框
* @param inputType 输入类型,这个控制的默认弹出的键盘,也会起到输入内容的限制
* @param acceptedChars 控制可输入的字符
*/
public BaseFilter(EditText editText, final int inputType, final String acceptedChars) {
if (!TextUtils.isEmpty(acceptedChars)) {
editText.setKeyListener(new DigitsKeyListener() {
@Override
public int getInputType() {
return inputType;
}
@Override
protected char[] getAcceptedChars() {
return acceptedChars.toCharArray();
}
});
} else {
editText.setInputType(inputType);
}
}
public BaseFilter(EditText editText, int inputType) {
editText.setInputType(inputType);
}
public BaseFilter(EditText editText, final String acceptedChars) {
if (!TextUtils.isEmpty(acceptedChars)) {
editText.setKeyListener(DigitsKeyListener.getInstance(acceptedChars));//只是控制输入的内容,排序/格式 控制不了
}
}
/**
* 在子类中重写这个方法,
* 1.可以控制输入的数量,
* 2.完善输入的内容
* 3.通过正则控制输入的格式
*
* @param source 新输入的字符串
* @param start 新输入的字符串起始下标,一般为0
* @param end 新输入的字符串终点下标,一般为source长度-1
* @param dest 输入之前文本框内容
* @param dstart 原内容起始坐标,一般为0
* @param dend 原内容终点坐标,一般为dest长度-1
* @return 输入内容
*/
@Override
public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {
return null;
}
}
setKeyListener和setInput方法上面已经介绍了,现在重点说一下 public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) 方法.
此方法监听的是输入的内容,只要键盘上输入了内容,那么就会监听到,返回的是新输入的内容,如果返回"".
InputType控制键盘类型,如果不设置其他,那么也会起到筛选输入内容的作用,而filter方法也可以通知输入的字符,如果输入的不符合字符,那么就返回"",则也不会显示到EditText上面.当然也可以控制师傅的格式,例如:InputType.TYPE_CLASS_PHONE EditText只显示数字, filter中可以判断输入电话号码的格式(1开头,最多输入11位).
###3.过滤器
####3.1电话号码过滤器
/**
* 电话过滤器
*/
public class EditPhoneFilter extends BaseFilter {
public EditPhoneFilter(EditText editText) {
super(editText, InputType.TYPE_CLASS_NUMBER, "0123456789");
}
@Override
public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {
String sourceText = source.toString();
String destText = dest.toString();
//验证删除等按键
if (TextUtils.isEmpty(sourceText)) {
return "";
}
if (!FilterUtils.isInputPhoneFormat(destText + sourceText)) {
return "";
}
return sourceText;
}
}
这里EditPhoneFilter继承了BaseFilter
(1)在构造的方法中设置了输入类型, InputType.TYPE_CLASS_NUMBER,默认的弹出数字键盘
(2)设置了输入字符的限制,“0123456789” 也就是只有输入这些字符才会显示到EditText上面.这个也可以不用设置这个,一般是限制的字符比较少,和特殊的字符在这里过滤一些.
(3)重写了filter方法,其中只要的判断手机号的格式是: FilterUtils.isInputPhoneFormat这个方法.如果输入的不符合手机号的格式,那么就返回"",表示这次没有输入任何字符.
public static boolean isInputPhoneFormat(String phoneNumber) {
String regular = "^1[0-9]{0,10}$";
return match(regular, phoneNumber);
}
这里做了输入手机号的时候,是否符合手机号格式,^1[0-9]{0,10}$
^:正则表达式开始.
1:表示必须是1开始
[0-9]{0,10}:表示0-9字符可以至少重复0次,最多重复10次,也就是手机号的第二位到第11位数字符必须是0-9之间的数字.
$:正则表达式结束.
如果判断一串数字是否符合手机号格式,那么正则应该按照下面这样写.
private static boolean match(String regex, String str) {
if (TextUtils.isEmpty(str))
return false;
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(str);
return matcher.matches();
}
/**
* 电话号码正则表达式
*
* @param phoneNumber
* @return
*/
public static boolean regularPhone(String phoneNumber) {
String regular = "^1[0-9]{10}$";
return match(regular, phoneNumber);
}
####3.2名字过滤器
限制了最多输入count位数.
/**
*名字过滤器,只能由汉子组成.
*/
public class EditNameFilter extends BaseFilter {
private int MAX_LENGTH = 5;//默认不长度不大于5
public EditNameFilter(EditText editText, int length) {
super(editText, InputType.TYPE_CLASS_TEXT);
MAX_LENGTH = length;
}
@Override
public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {
String sourceText = source.toString();
String destText = dest.toString();
//验证删除等按键
if (TextUtils.isEmpty(sourceText)) {
return "";
}
if (!FilterUtils.isInputNameFormat(destText + sourceText, MAX_LENGTH)) {
return "";
}
return sourceText;
}
}
假设最多只能输入5个汉子,那么对应的正则应该是:"1{0,5}$"
/**
* 输入时,是否是纯中文
*
* @param name
* @return
*/
public static boolean isInputNameFormat(String name, int count) {
StringBuffer stringBuffer = new StringBuffer();
stringBuffer.append("^").append("[\u4e00-\u9fa5]{0,").append(count).append("}").append("$");
String regular = stringBuffer.toString();
return match(regular, name);
}
3.3金额过滤器
public class EditMoneyFilter extends BaseFilter {
//输入的最大金额
private double MAX_VALUE = Double.MAX_VALUE;
//小数点后的位数
private int POINTER_LENGTH = 2;
private static final String POINTER = ".";
/**
* 默认的保留两位小数,默认最大值为Double.MAX_VALUE
*
* @param editText
*/
public EditMoneyFilter(EditText editText) {
this(editText, 2, Double.MAX_VALUE);
}
/**
* @param editText
* @param pointCount 小数点后保留的小数个数
*/
public EditMoneyFilter(EditText editText, int pointCount) {
this(editText, pointCount, Double.MAX_VALUE);
}
/**
* @param editText
* @param pointCount 小数点后保留的个数
* @param max 最大值
*/
public EditMoneyFilter(EditText editText, int pointCount, double max) {
super(editText, InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_FLAG_DECIMAL);
POINTER_LENGTH = pointCount;
MAX_VALUE = max;
}
@Override
public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {
String sourceText = source.toString();
String destText = dest.toString();
//验证删除等按键
if (TextUtils.isEmpty(sourceText)) {
return "";
}
if (!FilterUtils.isInputMoneyFormat(destText + sourceText, POINTER_LENGTH)) {
return "";
}
double sumText;
if (sourceText.equals(POINTER)) {
sumText = Double.parseDouble(destText);
} else {
//验证输入金额的大小
sumText = Double.parseDouble(destText + sourceText);
}
if (sumText > MAX_VALUE) {
return "";
}
return sourceText;
}
}
第一:输入的内容是数字和点(.)
第二:小数点后面的位数
第三:金额的最大值
方法:isInputMoneyFormat限制了第一和第二条件
最大值判断,要把输入的内容转化为数值,然后和最大值比较
例如:小数点后面保留两位小数.正则为:
"^(0(\\.\\d{0,2})?)|([1-9]\\d*(\\.\\d{0,2})?)$"
可以分为0开始和非零开始
0开始的:
0 ->(0)
0. ->(0\\.)
0.1 ->(0\\.\\d{0,2})
那么合并到一起就是:
(0)|((0\\.)|(0\\.\\d{0,2})
进行优化:
(0(\\.\\d{0,2})?)
|:表示只要匹配到任何一个就是true
?:表示前面离得最近的哪一组可有可无
非零开始的:
1 ->([1-9])
1. ->([1-9]\\.)
12 ->([1-9]\\d*)
12. ->([1-9]\\d*\\.)
12.1 ->([1-9]\\d*\\.\\d{0,2})
合并起来并且优化后:
([1-9]\\d*(\\.\\d{0,2})?)
?:表示前面离得最近的哪一组可有可无
判断输入过程中,输入的数字是否符合金额的格式,监督按照金额格式输入
/**
* 输入时,监听金额格式
*
* @param money
* @param count 小数点后省略几位
* @return
*/
public static boolean isInputMoneyFormat(String money, int count) {
StringBuffer sb = new StringBuffer();
sb.append("^");
//第一种,0开头
sb.append("(0(\\.\\d{0,").append(count).append("})?)").append("|");
//第二种非零开头
sb.append("([1-9]\\d*(\\.\\d{0,").append(count).append("})?)");
sb.append("$");
String regular = sb.toString();
boolean result = match(regular, money);
return result;
}
最后判断输入内容,是否符合金额的正确的格式的判断:其实就{0,count}–>{1,count}
"^(0(\\.\\d{1,2})?)|([1-9]\\d*(\\.\\d{1,2})?)$"
对应的方法
/**
* 金额正则表达式
*
* @param money
* @param count
* @return
*/
public static boolean regularMoney(String money, int count) {
StringBuffer sb = new StringBuffer();
sb.append("^");
//第一种,0开头
sb.append("(0(\\.\\d{1,").append(count).append("})?)").append("|");
//第二种非零开头
sb.append("([1-9]\\d*(\\.\\d{1,").append(count).append("})?)");
sb.append("$");
String regular = sb.toString();
boolean result = match(regular, money);
return result;
}
####3.4字母和数字组合过滤器
/**
* 字母或者数字 组合
*/
public class EditLetterOrNumberFilter extends BaseFilter {
private int MAX_LENGTH = 10;
public EditLetterOrNumberFilter(EditText editText, int maxLength) {
super(editText, InputType.TYPE_TEXT_VARIATION_PASSWORD);
//最有一个参数也可以直接写:0123456789qwertyuioplkjhgfdsazxcvbnmQWERTYUIOPLKJHGFDSAZXCVBNM
//其实这没必要,因为在过滤器中也做了字母和数字的判断,只有一些比较特殊的字符或者比较少的字母 可以写在这里
MAX_LENGTH = maxLength;
}
public EditLetterOrNumberFilter(EditText editText) {
this(editText, 10);
}
@Override
public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {
String sourceText = source.toString();
String destText = dest.toString();
//验证删除等按键
if (TextUtils.isEmpty(sourceText)) {
return "";
}
if (!FilterUtils.isInputOnlyLetterOrNumberFormat(destText + sourceText, MAX_LENGTH)) {
return "";
}
return sourceText;
}
}
InputType.TYPE_TEXT_VARIATION_PASSWORD 默认的弹出英文键盘
只能输入数字和字母,并且长度不能小于20.
那么输入的时候监听的格式: 2{0,20}$
/**
* 输入时,是否仅字母或数字组合
*
* @param name
* @return
*/
public static boolean isInputOnlyLetterOrNumberFormat(String name, int count) {
StringBuffer stringBuffer = new StringBuffer();
stringBuffer.append("^").append("[a-zA-Z0-9]{0,").append(count).append("}").append("$");
String regular = stringBuffer.toString();
return match(regular, name);
}
判断最终是否符合这个格式的正则:3{20}$
/**
*
* @param name
* @return
*/
public static boolean regularLetterOrNumber(String name, int count) {
StringBuffer stringBuffer = new StringBuffer();
stringBuffer.append("^").append("[a-zA-Z0-9]{").append(count).append("}").append("$");
String regular = stringBuffer.toString();
return match(regular, name);
}
####3.5身份证号码过滤器
分为15(都是数字)和18位数(最后一位是数字或者X,x,其余的都是数字)
过滤器的实现方法同上,现在给出关键的正则表达式方法:
监听判断输入过程的方法:
/**
* 输入时,身份证判断, 身份证分为15位和18位(最后一位是数字或者字母X,x)
*
* @param idNumber
* @return
*/
public static boolean isInputIdNumberFormat(String idNumber) {
String regular = "^(\\d{0,15})$|^(\\d{0,17})$|^(\\d{17}[\\d|X|x])$";
return match(regular, idNumber);
}
判断一个字符串是否符合正则身份证号码
/**
* 身份证正则表达式
*
* @param idNumber
* @return
*/
public static boolean regularIdNumber(String idNumber) {
String regular = "^(\\d{15})$|^(\\d{17}[\\d|X|x])$";
return match(regular, idNumber);
}
####3.6邮箱过滤器
my@qq.com类型的格式:
监听输入的过程是否符合邮箱的格式:
/**
* 输入时,邮箱格式监听
*
* @param str
* @return
*/
public static boolean isInputEmailFormat(String str) {
//my my@ my@qq my@qq. my@qq.com
String regular = "^([a-zA-Z0-9_]+)|([a-zA-Z0-9_]+@)|([a-zA-Z0-9_]+@[a-zA-Z0-9_]+)|([a-zA-Z0-9_]+@[a-zA-Z0-9_]+\\.)|([a-zA-Z0-9_]+@[a-zA-Z0-9_]+\\.[a-zA-Z0-9_]+)$";
return match(regular, str);
}
判断一个字符串是否适合邮箱的格式:
/**
* 邮箱的正则表达式
*
* @param str
* @return
*/
public static boolean regularEmail(String str) {
// my@qq.com
String regular = "^([a-zA-Z0-9_]+@[a-zA-Z0-9_]+\\.[a-zA-Z0-9_]+)$";
return match(regular, str);
}
这里注意的是:不要用
String regular = "^\\w+@\\w+.\\w+$";
因为\\w不代表[A-Za-Z0-9_],如果只是多有字符,这样导致也可以输入汉字,
###4.封装EditTextView并使用
####第一步
在res/values中创建一个attrs.xml文件
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="EditTextView">
<attr name="Type">
<enum name="phone" value="1"></enum>
<enum name="money1" value="2"></enum>
<enum name="money2" value="3"></enum>
<enum name="name" value="4"></enum>
<enum name="letterOrnumber" value="5"></enum>
<enum name="password" value="6"></enum>
<enum name="email" value="7"></enum>
<enum name="idnumber" value="8"></enum>
</attr>
</declare-styleable>
</resources>
####第二步
设置过滤器的方法
/**
* 给EditFilter设置过滤器
*
* @param editText
* @param filter
*/
public static void setFilter(EditText editText, BaseFilter filter) {
InputFilter[] filters = {filter};
editText.setFilters(filters);
}
自定义EditTextView.
public class EditTextView extends LinearLayout {
public EditTextView(Context context) {
this(context, null);
}
private EditText view_edittext;
public EditTextView(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.EditTextView);
int type = typedArray.getInteger(R.styleable.EditTextView_Type, -1);
View view = View.inflate(context, R.layout.view_edittext, this);
view_edittext = (EditText) view.findViewById(R.id.view_edittext);
if (type != -1) {
switch (type) {
case 1://电话号码
FilterUtils.setFilter(view_edittext, new EditPhoneFilter(view_edittext));
break;
case 2://金额1,不限制最大值,保留两位小数
FilterUtils.setFilter(view_edittext, new EditMoneyFilter(view_edittext, 2));
break;
case 3://金额2,限制最大值 10000,保留三位小数
FilterUtils.setFilter(view_edittext, new EditMoneyFilter(view_edittext, 3, 10000));
break;
case 4://名字
FilterUtils.setFilter(view_edittext, new EditNameFilter(view_edittext, 5));
break;
case 5://字母和数字组合
FilterUtils.setFilter(view_edittext, new EditLetterOrNumberFilter(view_edittext, 20));
break;
case 6://密码
FilterUtils.setFilter(view_edittext, new EditPasswordFilter(view_edittext));
break;
case 7://邮箱
FilterUtils.setFilter(view_edittext, new EditEmailFilter(view_edittext));
break;
case 8://身份证号码
FilterUtils.setFilter(view_edittext, new EditIdNumberFilter(view_edittext));
break;
}
}
typedArray.recycle();
}
public String getText() {
return view_edittext.getText().toString();
}
}
通过TypedArray读取attrs中的定义的属性
####第三步
在activity_main.xml文件中使用.
在跟布局中声明:
xmlns:edit="http://schemas.android.com/apk/res-auto"
在布局中填充此自定义控件
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="@+id/money1_desc"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:text="金额(保留2位小数):"
android:textSize="15sp"/>
<project.git.com.edittextinput.view.EditTextView
android:id="@+id/money_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
edit:Type="money1"/>
</LinearLayout>
这样在输入的时候,就只能输入金额对应格式的文本,起到作用的是:edit:Type=“money1”
####第四步
MianActivity
moneyView = (EditTextView) findViewById(R.id.money_view);
String money = moneyView.getText();
if (FilterUtils.regularMoney(money, 2)) {
Toast.makeText(MainActivity.this, "是金额格式", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(MainActivity.this, "不是金额格式", Toast.LENGTH_SHORT).show();
}
其实还可以继续对EditTextView进行优化
(1)EditText输入的描述(金额(保留2位小数))抽取到attrs.xml中,作为属性
(2)在attrs.xml中增加一个EditText的hint属性.
(3)把每个过滤器中输入的长度限制也抽取成attrs.xml的属性.
(4)把金额的小数点后几位,最大值也可以抽取到attrs.xml的属性中.
这些就根据具体的需求进行优化,就是照葫芦画瓢了.
###5.过滤器的长度优化
比如对于名字过滤器进行优化,现在如果直接输入6个汉子,那么此时不会限制到EditTextView上,现在有一个这样的需求,如果超过指定的长度,那么就从前面截取指定的最大长度的字符.
/**
* 名字过滤器,只能由汉子组成.
*/
public class EditNameV1Filter extends BaseFilter {
private int MAX_LENGTH = 5;//默认不长度不大于5
public EditNameV1Filter(EditText editText, int length) {
super(editText, InputType.TYPE_CLASS_TEXT);
MAX_LENGTH = length;
}
@Override
public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {
String sourceText = source.toString();
String destText = dest.toString();
//验证删除等按键
if (TextUtils.isEmpty(sourceText)) {
return "";
}
//判断是否纯中文
if (FilterUtils.isOnlyChineseFormat(sourceText)) {
StringBuffer stringBuffer = new StringBuffer();
stringBuffer.append(destText).append(sourceText);
if (stringBuffer.toString().length() > MAX_LENGTH) {
String result = stringBuffer.toString().substring(dend, MAX_LENGTH);
return result;
} else {
return sourceText;
}
} else {
return "";
}
}
}
其中增加了一个判断是否纯中文的方法:
/**
* 是否是纯中文
* @param name
* @return
*/
public static boolean isOnlyChineseFormat(String name) {
String regular = "^[\u4e00-\u9fa5]*$";
return match(regular, name);
}
当已经输入的字符+新输入的字符超过了指定的最大长度,那么就截取已输入的+截取的=MaxLength
源码下载 GitHub库下载地址