现在主流的App中,使用手机验证码的场景越来越多,比如验证码登陆、验证码重置/找回密码等。
一般功能流程如下:
1,防止获取验证码按钮在短时间内被点击多次
2,调用获取验证码接口(提示loading,防止界面被操作)
3,验证码获取成功开始倒计时,此时按钮不可被点击
4,倒计时完毕后,点击可以再次被点击
在Android中实现该功能的方案也有很多,比如Timer、CountDownTimer、Handler等。
接下来主要是讲如何使用RxJava来实现该功能。
点击按钮调用获取验证码接口可能有两种交互
1,调用获取验证码接口,如果是弹出一个loading框,这样的最简单,因为用户不能再次点这个按钮了
(接口返回成功则开始倒计时,失败则loading消失按钮可以再次被点击)
2,调用获取验证码接口,不实用loading框的方式,直接让按钮不可点,按钮的文字变成"正在获取..."之类的。
接口调用成功则开始倒计时,接口失败则把按钮设置为可点
这两种方式用RxJava都可以轻松实现。第一种方式主流一些,以下讲第一种交互实现方式。
代码如下:
RxView.clicks(mBtn)
.throttleFirst(1, TimeUnit.SECONDS)
.flatMap(new Func1<Void, Observable<Boolean>>() {
@Override
public Observable<Boolean> call(Void aVoid) {
//弹出loading框(省略)
//调用获取验证码接口 (省略)
return Observable.just(true);
}
})
.flatMap(new Func1<Boolean, Observable<Boolean>>() {
@Override
public Observable<Boolean> call(Boolean aBoolean) {
//验证码获取成功,把按钮置为不可点
mBtn.setEnabled(false);
//关闭loading框(省略)
return intervalButton();
}
})
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<Boolean>() {
@Override
public void call(Boolean aBoolean) {
Log.e("FetchPhoneCodeFragment", "subscribe call");
}
}, new Action1<Throwable>() {
@Override
public void call(Throwable throwable) {
throwable.printStackTrace();
//关闭loading框(省略)
//出错后把按钮设置为可用
mBtn.setEnabled(true);
Toast.makeText(getActivity(), throwable.getMessage(), Toast.LENGTH_SHORT).show();
}
});
获取验证码成功后,开始倒计时
public Observable<Boolean> intervalButton() {
//如果使用Observable.interval(interval, TimeUnit),会默认延迟interval执行
return Observable.interval(0, 1000, TimeUnit.MILLISECONDS) // ,每秒发射一次事件
//如果aLong小于等于我们设定的秒数,发射事件
.takeWhile(new Func1<Long, Boolean>() {
@Override
public Boolean call(Long aLong) {
return aLong <= INTERVAL_TIME;
}
})
//可以通过filter达到takeWhile相同的效果
// .filter(new Func1<Long, Boolean>() {
// @Override
// public Boolean call(Long aLong) {
// return aLong <= INTERVAL_TIME;
// }
// })
.observeOn(AndroidSchedulers.mainThread())
.flatMap(new Func1<Long, Observable<Boolean>>() {
@Override
public Observable<Boolean> call(Long aLong) {
long diff = INTERVAL_TIME - aLong;
Log.e("FetchPhoneCodeFragment", "thread name:" + Thread.currentThread().getName() + ",aLong:" + aLong + ",diff:" + diff);
mBtn.setText(diff + "s");
//倒计时完毕后,按钮可以再次被点击,重新设置按钮文案
if (diff == 0) {
mBtn.setEnabled(true);
mBtn.setText("获取验证码");
}
return Observable.just(true);
}
});
}
上面的注释也很详细,这里就简单的概述下:
通过RxView的throttleFirst避免在短时间内按钮被点击多次
调用获取验证码接口把按钮置为不可用,并且弹出loading框,如果接口调用成功了则开始倒计时,如果失败的话关闭loading框,把按钮设置为可用。
倒计时功能,到了我们预设的秒数,把按钮置为可用, 否则按钮不可用。
需要注意的是interval定时发射数据都会执行subscribe action;而且不取消的话,interval会一直发射数据,所以使用takeWhile、filter来作为条件判断。
另一方面,在onDestroy的时候需要把Subscription反注销:
@Override
public void onDestroy() {
super.onDestroy();
if (mSubscription != null && !mSubscription.isUnsubscribed()) {
mSubscription.unsubscribe();
}
}