
大家好,又到了新的一期的項(xiàng)目需求討論。我想大家在開(kāi)發(fā)APP,肯定會(huì)有很多需要填入EditText內(nèi)容的界面,比如注冊(cè)界面,修改密碼界面。這些界面都會(huì)有很多個(gè)相應(yīng)的EditText。同時(shí)每個(gè)EditText需要填寫(xiě)的內(nèi)容不同,所以就造成我們對(duì)于每個(gè)EditText進(jìn)行相應(yīng)的判斷。
比如下面的界面:
可能我們需要輸入“用戶名”、“地址”、“郵箱”、“電話”。然后下面可能就有一個(gè)“注冊(cè)”的按鈕,當(dāng)我們按下“注冊(cè)”按鈕的時(shí)候。我們可能平時(shí)都是這么做的:
- 獲取了四個(gè)EditText的對(duì)象
private EditText mNameEditText;
private EditText mAddressEditText;
private EditText mEmailEditText;
private EditText mPhoneEditText;
mNameEditText = (EditText) findViewById(R.id.nameEditText);
mAddressEditText = (EditText) findViewById(R.id.addressEditText);
mEmailEditText = (EditText) findViewById(R.id.emailEditText);
mPhoneEditText = (EditText) findViewById(R.id.phoneEditText);
- 獲取他們的內(nèi)容,從上往下,一個(gè)個(gè)判斷他是不是為空,如果為空,我們就提示用戶漏填了某個(gè)內(nèi)容:
if(TextUtils.isEmpty(mNameEditText.getText().toString()){
Toast.makeText(this, "用戶名是必填項(xiàng),請(qǐng)輸入內(nèi)容", Toast.LENGTH_SHORT).show();
return;
}
....
....
....
- 當(dāng)每個(gè)都填了內(nèi)容后,你可能還要相應(yīng)的不同的EditText還有相應(yīng)的規(guī)則,比如我們上面已經(jīng)判斷了用戶名不為空了。然后我們的APP有規(guī)定,用戶名的長(zhǎng)度不能小于5同時(shí)不能大于10,然后你又要寫(xiě):
int nameLength = mNameEditText.getText().toString().length();
if(nameLength < 5 || nameLength > 10){
Toast.makeText(this, "用戶名長(zhǎng)度不能小于5且不能大于10", Toast.LENGTH_SHORT).show();
return;
}
- 然后你還要對(duì)相應(yīng)的EditText去一個(gè)個(gè)判斷郵箱規(guī)則,電話規(guī)則,如果還要“密碼”的EditText,一般同事還有一個(gè)“確認(rèn)密碼”的EditText,這時(shí)候你不僅要第一個(gè)EditText符合密碼規(guī)則,然后還要判斷二個(gè)EditText的內(nèi)容是不是相等。反正是麻煩至極,而且代碼基本都是很多地方都是重復(fù),讓閱讀也變得很差。
所以程序員本著對(duì)于這些代碼的懶惰原則。我就去尋找相關(guān)的優(yōu)秀的工具,這不,本文的主角出場(chǎng)了:
**android-saripaar
**
這里我們分別對(duì)于本文主角的使用功能來(lái)進(jìn)行介紹:
基本使用方法:
比如我們上面講到的用戶名不能為空,我們看下用saripaar是怎么來(lái)使用
- 在我們定義的EditText的引用上方,添加相應(yīng)功能的注解即可
@NotEmpty
private EditText mNameEditText;
- 創(chuàng)建Validator對(duì)象,并且設(shè)置EditText中的內(nèi)容規(guī)則判斷后的回調(diào)事件:
Validator mValidator = new Validator(this);
mValidator.setValidationListener(new Validator.ValidationListener() {
@Override
public void onValidationSucceeded() {
//符合我們添加的相關(guān)規(guī)則,驗(yàn)證成功
}
@Override
public void onValidationFailed(List<ValidationError> errors) {
//不符合我們添加的相關(guān)規(guī)則,驗(yàn)證失敗
}
});
- 在某種條件下(比如按下注冊(cè)按鈕)調(diào)用驗(yàn)證方法:
mValidator.validate();
基本用法就是上面那樣,可能大家會(huì)說(shuō),老司機(jī)你就這樣??這個(gè)功能也太少了吧。別急,容許老司機(jī)一步步來(lái)介紹相關(guān)詳細(xì)內(nèi)容。
基本的注解:
比如有一些:
@NotEmpty :非空
@Length: 長(zhǎng)度
@Email: 郵箱
@Password:密碼(默認(rèn)是6位)
@ConfirmPassword:確認(rèn)密碼
@Checked:CheckBox是否被選中
@Length:長(zhǎng)度判斷
IpAddress:ip地址判斷
等,我就不列舉了,直接看圖吧

@Order:
我們一般來(lái)說(shuō)界面上會(huì)有好幾個(gè)EditText,比如name,email,address三個(gè)輸入框,我們會(huì)對(duì)三個(gè)輸入框都設(shè)置相關(guān)的規(guī)則,這時(shí)候每個(gè)app中對(duì)于這些輸入框的判斷的順序有所要求,比如先是判斷name,然后依次往下判斷,但有些可能先判斷address等。
所以@Order就是用來(lái)讓我們驗(yàn)證好幾個(gè)EditText時(shí)來(lái)進(jìn)行排序的。
@NotEmpty
@Order(1)
private EditText mNameEditText;
@NotEmpty
@Order(2)
private EditText mAddressEditText;
@Email
@Order(3)
private EditText mEmailEditText;
這時(shí)候大家可能就會(huì)問(wèn)了。你這里設(shè)置了@Order,那順序體現(xiàn)在哪里呢?就在我們上面設(shè)置過(guò)的Listener中:
@Override
public void onValidationFailed(List<ValidationError> errors) {
}
我們可以看到,這個(gè)失敗的方法里面的參數(shù)是List<ValidationError> errors
我們可以看下ValidationError的源碼里面有這么幾個(gè)方法:
public View getView() {
return view;
}
public List<Rule> getFailedRules() {
return failedRules;
}
public String getCollatedErrorMessage(final Context context) {
StringBuilder stringBuilder = new StringBuilder();
for (Rule failedRule : failedRules) {
String message = failedRule.getMessage(context).trim();
if (message.length() > 0) {
stringBuilder.append(message).append('\n');
}
}
return stringBuilder.toString().trim();
}
我們一眼就會(huì)發(fā)現(xiàn)getView這個(gè)方法就是我們所需要的,沒(méi)錯(cuò),如果我們有好幾個(gè)EditText都不符合規(guī)則,在List```<ValidationError>中就會(huì)按照我們寫(xiě)的@Order``的順序來(lái)進(jìn)行排序。
比如我們想讓EditText不符合規(guī)則的時(shí)候出現(xiàn):
我們只需要:
@Override
public void onValidationFailed(List<ValidationError> errors) {
for (ValidationError error : errors){
((EditText) error.getView()).setError("不符合規(guī)則");
}
}
也許有人會(huì)說(shuō)。我希望name錯(cuò)了的時(shí)候就提示name,后面的就不管了。報(bào)第一個(gè)不符合規(guī)則的錯(cuò)誤,然后有人會(huì)說(shuō)errors.get(0).getView()獲取,當(dāng)然這樣是可以的,但是saripaar已經(jīng)幫我考慮到了:
//全部不符合規(guī)則
mValidator.setValidationMode(Validator.Mode.BURST);
//按照順序第一個(gè)不符合規(guī)則的
mValidator.setValidationMode(Validator.Mode.IMMEDIATE);
我們?cè)O(shè)置了后,同樣的代碼就變成了:
(message = ""):
有小伙伴會(huì)說(shuō),你上面提示的內(nèi)容的都是不符合規(guī)則,我們想要不同的EditText不符合規(guī)則后提示不同的內(nèi)容,還記得我們上面ValidationError有個(gè)方法getCollatedErrorMessage(context),沒(méi)錯(cuò),我們可以給每個(gè)EditText設(shè)置不同的message,然后在驗(yàn)證失敗后,顯示相應(yīng)的message即可:
@NotEmpty(message = "名字不能為空")
private EditText mNameEditText;
@Override
public void onValidationFailed(List<ValidationError> errors) {
for (ValidationError error : errors){
((EditText) error.getView()).setError(error.getCollatedErrorMessage(OrderedValidateActivity.this));
}
}
sequence:
我們有時(shí)候?qū)τ谝粋€(gè)EditText會(huì)有多種要求,比如不僅不能為空,而且同時(shí)要符合郵箱的標(biāo)準(zhǔn),這時(shí)候我們對(duì)于驗(yàn)證也希望有驗(yàn)證順序,比如先判斷是否為空,如果為空,直接就提示錯(cuò)誤了。如果不為空再判斷是不是符合郵箱的規(guī)則。
@NotEmpty(sequence = 1, message = "不能為空")
@Email(sequence = 2, message = "不符合郵箱規(guī)則")
private EditText mEmailEditText;
這時(shí)候你會(huì)發(fā)現(xiàn)這個(gè)EditText就會(huì)按照你所規(guī)定的規(guī)則順序來(lái)判斷。
但這里注意了,上面提過(guò)我們獲取message是用
error.getCollatedErrorMessage(context);
因?yàn)樯厦嫖覀円粋€(gè)EditText只添加了一個(gè)規(guī)則判斷,所以無(wú)所謂,比如我這里添加了二個(gè),我們?cè)倏吹臅r(shí)候會(huì)變成怎么樣?
沒(méi)錯(cuò),雖然判斷規(guī)則的順序的確是按照我們寫(xiě)的那樣,但是,你發(fā)現(xiàn)了,
error.getCollatedErrorMessage(context);方法獲取到的message的內(nèi)容是全部不符合規(guī)則的message的集合。但我們想要的是非空的時(shí)候先提示不能為空,然后在不為空的條件下,不是郵箱格式,再提示郵箱不符合郵箱格式。
還記不記得我們已經(jīng)介紹了上面ValidationError的二個(gè)方法,還有一個(gè)方法getFailedRules()沒(méi)介紹過(guò),沒(méi)錯(cuò),我們可以用這個(gè),從字面意思我們就可以理解,獲取到失敗的規(guī)則的集合,而且這個(gè)集合的順序就是我們?cè)O(shè)置的sequence的順序,我們這么寫(xiě):
for (ValidationError error : errors){
((EditText) error.getView()).
setError(error.getFailedRules().get(0)
.getMessage(OrderedValidateActivity.this));
}
我們獲取第一個(gè)不符合的規(guī)則的Message即可。
看效果:
@Optional:
很多時(shí)候我們一些信息是非必填的,比如還是email,我們可以為空,但是如果你填了,那么一定就要符合郵箱的規(guī)則,這時(shí)候@Optional就起到作用了:
@Optional
@Email
EditText mEmailEditText;
你可以不填,這時(shí)候驗(yàn)證是通過(guò)的,但是如果你填了內(nèi)容,就一定要符合email規(guī)則。使用起來(lái)什么方便。
validateTill 和 validateBefore
我們上面在最后起到驗(yàn)證功能是調(diào)用了
mValidator.validate();
同時(shí)它還提供了:
mValidator.validateTill(view);
mValidator.validateBefore(view);
從字面意思我們就知道,到某個(gè)View為止的規(guī)則檢驗(yàn),及某個(gè)View前的規(guī)則檢驗(yàn)。
validateTill:比如有a,b,c,d四個(gè)View,并且order的順序相對(duì)應(yīng)也是1,2,3,4,
比如當(dāng)前四個(gè)View都不符合規(guī)則,并且你調(diào)用了validateTill(c),那么我們的List<ValidationError> errors中就包含了a,b,c。如果你調(diào)用了validateBefore(c),則List<ValidationError> errors就包含了a,b,也就是比傳入的View的Order小的會(huì)被包含。
同時(shí)你在使用的時(shí)候會(huì)發(fā)現(xiàn)他們?nèi)齻€(gè)方法都有另外的重載方法,分別是:
validate(boolean);
validateTill(view ,boolean);
validateBefore(view,boolean);
當(dāng)那個(gè)Boolean值為true的時(shí)候,就是說(shuō)我們可以把驗(yàn)證的過(guò)程放在后臺(tái)的AsyncTask中去執(zhí)行。
@AssertTrue 和 @AssertFalse
我們有些輸入框可能不是通用的規(guī)則,像郵箱啊,電話號(hào)碼什么的,比如某個(gè)輸入框的判斷規(guī)則是"青蛙要fly好帥",當(dāng)EditText的內(nèi)容是這個(gè)的時(shí)候才能認(rèn)為通過(guò)。這時(shí)候我們就要添加自己的規(guī)則??梢允褂?code>@AssertTrue來(lái)判斷是否符合你定義的規(guī)則。
@AssertTrue
private EditText testEt;
mValidator.registerAdapter(EditText.class, new ViewDataAdapter<EditText, Boolean>() {
@Override
public Boolean getData(EditText view) throws ConversionException {
return "青蛙要fly好帥".equals(view.getText().toString());
}
@Override
public <T extends Annotation> boolean containsOptionalValue(EditText view, T ruleAnnotation) {
return false;
}
});
Custom Annotation
當(dāng)然我們還可以自己制定相關(guān)的注解,比如我現(xiàn)在自己寫(xiě)一個(gè)@CoolBoy,用來(lái)判斷EditText是否符合我寫(xiě)的相關(guān)內(nèi)容:
CoolBoy.java
@ValidateUsing(CoolBoyRule.class)
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface CoolBoy {
@StringRes int messageResId() default -1;
String message() default "不能說(shuō)青蛙要fly不帥氣";
int sequence() default -1;
}
CoolBoyRule.java
public class CoolBoyRule extends AnnotationRule<CoolBoy, String> {
protected CoolBoyRule(final CoolBoy coolBoy) {
super(coolBoy);
}
@Override
public boolean isValid(String data) {
return "青蛙要fly是個(gè)帥哥".equals(data);
}
}
最后一步:
在Validator.java中添加我們剛聲明好的注解:
SARIPAAR_REGISTRY.register(
ConfirmEmail.class, ConfirmPassword.class, CreditCard.class,
Digits.class, Domain.class, Email.class, Future.class,
IpAddress.class, Isbn.class, Length.class, NotEmpty.class,
Password.class, Past.class, Pattern.class, Url.class, CoolBoy.class);
//在最后添加了我們的CoolBoy.class
然后在我們的代碼中使用:
@CoolBoy
private EditText mNameEditText;
補(bǔ)充
我們?cè)趘alidator.java中還可以看到其他的申明等:
static {
// CheckBoxBooleanAdapter
SARIPAAR_REGISTRY.register(CheckBox.class, Boolean.class,
new CheckBoxBooleanAdapter(),
AssertFalse.class, AssertTrue.class, Checked.class);
// RadioGroupBooleanAdapter
SARIPAAR_REGISTRY.register(RadioGroup.class, Boolean.class,
new RadioGroupBooleanAdapter(),
Checked.class);
// RadioButtonBooleanAdapter
SARIPAAR_REGISTRY.register(RadioButton.class, Boolean.class,
new RadioButtonBooleanAdapter(),
AssertFalse.class, AssertTrue.class, Checked.class);
// SpinnerIndexAdapter
SARIPAAR_REGISTRY.register(Spinner.class, Integer.class,
new SpinnerIndexAdapter(),
Select.class);
// TextViewDoubleAdapter
SARIPAAR_REGISTRY.register(DecimalMax.class, DecimalMin.class);
// TextViewIntegerAdapter
SARIPAAR_REGISTRY.register(Max.class, Min.class);
// TextViewStringAdapter
SARIPAAR_REGISTRY.register(
ConfirmEmail.class, ConfirmPassword.class, CreditCard.class,
Digits.class, Domain.class, Email.class, Future.class,
IpAddress.class, Isbn.class, Length.class, NotEmpty.class,
Password.class, Past.class, Pattern.class, Url.class, CoolBoy.class);
}
哈哈,更多的demo大家也可以看:
**android-saripaar
**
里面有相關(guān)的testdemo的使用。歡迎大家吐槽。哈哈,還是老話,哪里錯(cuò)了,希望大家能指出。