項(xiàng)目需求討論-擺脫EditText內(nèi)容規(guī)則的枯燥判斷

大家好核芽,又到了新的一期的項(xiàng)目需求討論。我想大家在開發(fā)APP簿煌,肯定會有很多需要填入EditText內(nèi)容的界面,比如注冊界面鉴吹,修改密碼界面姨伟。這些界面都會有很多個(gè)相應(yīng)的EditText。同時(shí)每個(gè)EditText需要填寫的內(nèi)容不同豆励,所以就造成我們對于每個(gè)EditText進(jìn)行相應(yīng)的判斷夺荒。

比如下面的界面:

可能我們需要輸入“用戶名”、“地址”良蒸、“郵箱”技扼、“電話”。然后下面可能就有一個(gè)“注冊”的按鈕嫩痰,當(dāng)我們按下“注冊”按鈕的時(shí)候剿吻。我們可能平時(shí)都是這么做的:

  1. 獲取了四個(gè)EditText的對象
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);
  1. 獲取他們的內(nèi)容,從上往下串纺,一個(gè)個(gè)判斷他是不是為空丽旅,如果為空,我們就提示用戶漏填了某個(gè)內(nèi)容:
if(TextUtils.isEmpty(mNameEditText.getText().toString()){
       Toast.makeText(this, "用戶名是必填項(xiàng)纺棺,請輸入內(nèi)容", Toast.LENGTH_SHORT).show();
       return;
}
....
....
....
  1. 當(dāng)每個(gè)都填了內(nèi)容后榄笙,你可能還要相應(yīng)的不同的EditText還有相應(yīng)的規(guī)則,比如我們上面已經(jīng)判斷了用戶名不為空了祷蝌。然后我們的APP有規(guī)定茅撞,用戶名的長度不能小于5同時(shí)不能大于10,然后你又要寫:
int nameLength = mNameEditText.getText().toString().length();
   if(nameLength < 5 || nameLength > 10){
       Toast.makeText(this, "用戶名長度不能小于5且不能大于10", Toast.LENGTH_SHORT).show();
       return;
   }
  1. 然后你還要對相應(yīng)的EditText去一個(gè)個(gè)判斷郵箱規(guī)則巨朦,電話規(guī)則乡翅,如果還要“密碼”的EditText,一般同事還有一個(gè)“確認(rèn)密碼”的EditText,這時(shí)候你不僅要第一個(gè)EditText符合密碼規(guī)則罪郊,然后還要判斷二個(gè)EditText的內(nèi)容是不是相等蠕蚜。反正是麻煩至極,而且代碼基本都是很多地方都是重復(fù)悔橄,讓閱讀也變得很差靶累。

  所以程序員本著對于這些代碼的懶惰原則。我就去尋找相關(guān)的優(yōu)秀的工具癣疟,這不挣柬,本文的主角出場了:

**android-saripaar
**

這里我們分別對于本文主角的使用功能來進(jìn)行介紹:

基本使用方法:

比如我們上面講到的用戶名不能為空,我們看下用saripaar是怎么來使用

  1. 在我們定義的EditText的引用上方睛挚,添加相應(yīng)功能的注解即可
@NotEmpty
private EditText mNameEditText;
  1. 創(chuàng)建Validator對象邪蛔,并且設(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)證失敗
        }
});
  1. 在某種條件下(比如按下注冊按鈕)調(diào)用驗(yàn)證方法:
mValidator.validate();

基本用法就是上面那樣侧到,可能大家會說勃教,老司機(jī)你就這樣?匠抗?這個(gè)功能也太少了吧故源。別急,容許老司機(jī)一步步來介紹相關(guān)詳細(xì)內(nèi)容汞贸。


基本的注解:

比如有一些:
@NotEmpty :非空
@Length: 長度
@Email: 郵箱
@Password:密碼(默認(rèn)是6位)
@ConfirmPassword:確認(rèn)密碼
@Checked:CheckBox是否被選中
@Length:長度判斷
IpAddress:ip地址判斷

等绳军,我就不列舉了,直接看圖吧


@Order:

我們一般來說界面上會有好幾個(gè)EditText矢腻,比如name,email,address三個(gè)輸入框,我們會對三個(gè)輸入框都設(shè)置相關(guān)的規(guī)則门驾,這時(shí)候每個(gè)app中對于這些輸入框的判斷的順序有所要求,比如先是判斷name多柑,然后依次往下判斷猎唁,但有些可能先判斷address等。
所以@Order就是用來讓我們驗(yàn)證好幾個(gè)EditText時(shí)來進(jìn)行排序的顷蟆。

@NotEmpty
@Order(1)
private EditText mNameEditText;

@NotEmpty
@Order(2)
private EditText mAddressEditText;

@Email
@Order(3)
private EditText mEmailEditText;

這時(shí)候大家可能就會問了。你這里設(shè)置了@Order腐魂,那順序體現(xiàn)在哪里呢帐偎?就在我們上面設(shè)置過的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();
}

我們一眼就會發(fā)現(xiàn)getView這個(gè)方法就是我們所需要的蛔屹,沒錯(cuò)削樊,如果我們有好幾個(gè)EditText都不符合規(guī)則,在List```<ValidationError>中就會按照我們寫的@Order``的順序來進(jìn)行排序兔毒。

比如我們想讓EditText不符合規(guī)則的時(shí)候出現(xiàn):


我們只需要:

@Override
public void onValidationFailed(List<ValidationError> errors) {
    for (ValidationError error : errors){
        ((EditText) error.getView()).setError("不符合規(guī)則");
    }
}

也許有人會說漫贞。我希望name錯(cuò)了的時(shí)候就提示name,后面的就不管了育叁。報(bào)第一個(gè)不符合規(guī)則的錯(cuò)誤迅脐,然后有人會說errors.get(0).getView()獲取,當(dāng)然這樣是可以的,但是saripaar已經(jīng)幫我考慮到了:

//全部不符合規(guī)則
mValidator.setValidationMode(Validator.Mode.BURST);

//按照順序第一個(gè)不符合規(guī)則的
mValidator.setValidationMode(Validator.Mode.IMMEDIATE);

我們設(shè)置了后豪嗽,同樣的代碼就變成了:


(message = ""):

有小伙伴會說谴蔑,你上面提示的內(nèi)容的都是不符合規(guī)則,我們想要不同的EditText不符合規(guī)則后提示不同的內(nèi)容龟梦,還記得我們上面ValidationError有個(gè)方法getCollatedErrorMessage(context),沒錯(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會有多種要求计贰,比如不僅不能為空钦睡,而且同時(shí)要符合郵箱的標(biāo)準(zhǔn),這時(shí)候我們對于驗(yàn)證也希望有驗(yàn)證順序躁倒,比如先判斷是否為空荞怒,如果為空洒琢,直接就提示錯(cuò)誤了。如果不為空再判斷是不是符合郵箱的規(guī)則挣输。

@NotEmpty(sequence = 1, message = "不能為空")
@Email(sequence = 2, message = "不符合郵箱規(guī)則")
private EditText mEmailEditText;

這時(shí)候你會發(fā)現(xiàn)這個(gè)EditText就會按照你所規(guī)定的規(guī)則順序來判斷纬凤。
但這里注意了,上面提過我們獲取message是用

error.getCollatedErrorMessage(context);

因?yàn)樯厦嫖覀円粋€(gè)EditText只添加了一個(gè)規(guī)則判斷撩嚼,所以無所謂停士,比如我這里添加了二個(gè),我們再看的時(shí)候會變成怎么樣完丽?


沒錯(cuò)恋技,雖然判斷規(guī)則的順序的確是按照我們寫的那樣,但是逻族,你發(fā)現(xiàn)了蜻底,error.getCollatedErrorMessage(context);方法獲取到的message的內(nèi)容是全部不符合規(guī)則的message的集合。但我們想要的是非空的時(shí)候先提示不能為空聘鳞,然后在不為空的條件下薄辅,不是郵箱格式,再提示郵箱不符合郵箱格式抠璃。

還記不記得我們已經(jīng)介紹了上面ValidationError的二個(gè)方法站楚,還有一個(gè)方法getFailedRules()沒介紹過,沒錯(cuò)搏嗡,我們可以用這個(gè)窿春,從字面意思我們就可以理解,獲取到失敗的規(guī)則的集合采盒,而且這個(gè)集合的順序就是我們設(shè)置的sequence的順序旧乞,我們這么寫:

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)證是通過的左权,但是如果你填了內(nèi)容皮胡,就一定要符合email規(guī)則。使用起來什么方便赏迟。


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的順序相對應(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小的會被包含究抓。

同時(shí)你在使用的時(shí)候會發(fā)現(xiàn)他們?nèi)齻€(gè)方法都有另外的重載方法,分別是:

validate(boolean);
validateTill(view ,boolean);
validateBefore(view,boolean);

當(dāng)那個(gè)Boolean值為true的時(shí)候袭灯,就是說我們可以把驗(yàn)證的過程放在后臺的AsyncTask中去執(zhí)行刺下。


@AssertTrue 和 @AssertFalse

我們有些輸入框可能不是通用的規(guī)則,像郵箱啊稽荧,電話號碼什么的橘茉,比如某個(gè)輸入框的判斷規(guī)則是"青蛙要fly好帥",當(dāng)EditText的內(nèi)容是這個(gè)的時(shí)候才能認(rèn)為通過姨丈。這時(shí)候我們就要添加自己的規(guī)則畅卓。可以使用@AssertTrue來判斷是否符合你定義的規(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)在自己寫一個(gè)@CoolBoy,用來判斷EditText是否符合我寫的相關(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 "不能說青蛙要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ǔ)充

我們在validator.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ò)了,希望大家能指出呀邢。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末洒沦,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子价淌,更是在濱河造成了極大的恐慌申眼,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,378評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蝉衣,死亡現(xiàn)場離奇詭異括尸,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)病毡,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評論 2 382
  • 文/潘曉璐 我一進(jìn)店門濒翻,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事有送√视鳎” “怎么了?”我有些...
    開封第一講書人閱讀 152,702評論 0 342
  • 文/不壞的土叔 我叫張陵雀摘,是天一觀的道長裸删。 經(jīng)常有香客問我,道長阵赠,這世上最難降的妖魔是什么涯塔? 我笑而不...
    開封第一講書人閱讀 55,259評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮豌注,結(jié)果婚禮上伤塌,老公的妹妹穿的比我還像新娘。我一直安慰自己轧铁,他們只是感情好每聪,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,263評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著齿风,像睡著了一般药薯。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上救斑,一...
    開封第一講書人閱讀 49,036評論 1 285
  • 那天童本,我揣著相機(jī)與錄音,去河邊找鬼脸候。 笑死穷娱,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的运沦。 我是一名探鬼主播泵额,決...
    沈念sama閱讀 38,349評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼携添!你這毒婦竟也來了嫁盲?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,979評論 0 259
  • 序言:老撾萬榮一對情侶失蹤烈掠,失蹤者是張志新(化名)和其女友劉穎羞秤,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體左敌,經(jīng)...
    沈念sama閱讀 43,469評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡瘾蛋,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,938評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了矫限。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片瘦黑。...
    茶點(diǎn)故事閱讀 38,059評論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡京革,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出幸斥,到底是詐尸還是另有隱情匹摇,我是刑警寧澤,帶...
    沈念sama閱讀 33,703評論 4 323
  • 正文 年R本政府宣布甲葬,位于F島的核電站廊勃,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏经窖。R本人自食惡果不足惜坡垫,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,257評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望画侣。 院中可真熱鬧冰悠,春花似錦、人聲如沸配乱。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,262評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽搬泥。三九已至桑寨,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間忿檩,已是汗流浹背尉尾。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留燥透,地道東北人沙咏。 一個(gè)月前我還...
    沈念sama閱讀 45,501評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像班套,于是被迫代替她去往敵國和親肢藐。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,792評論 2 345

推薦閱讀更多精彩內(nèi)容