验证码效果图(点击发送验证码后,开始倒计时,退出该界面,再进来,还在倒计时)
效果.gif在开发过程中,几乎每一款应用都会有发送验证码的功能,因此,心中产生了封装的念头,一次封装到处可用,这也是程序员偷懒的一种方式。
Android开发实现倒计时主要有以下几种方式
1、使用Handlert通过延时发送信息实现倒计时功能
2、使用CountDownTimer(源码中使用的也是Handler延时实现)
3、使用Timer
在这里,我参考了一下CountDownTimer源码,在此基础上进行修改,加入一些自定义属性,例如倒计时的前缀、后缀、Handler标识,这些属性在布局文件中都可以设置。
1、自定义控件
1.1自定义属性(复制到自己的项目中attrs文件下)
<declare-styleable name="ValidateCodeView" tools:ignore="ResourceName">
<!--总秒数-->
<attr name="mTotalSeconds" format="integer" />
<!--前缀文本-->
<attr name="mPreStr" format="string" />
<!--后缀文本-->
<attr name="mSuffixStr" format="string" />
<!--handler发送信息表标识码(解决多个界面共用验证码问题)-->
<attr name="mHandlerSendCode" format="integer" />
</declare-styleable>
1.2自定义控件(复制到自己的项目下)
ValidateCodeView.java
/**
* 类描述:发送验证码控件
* 作者:xues
* 时间:2017年08月01日
*/
public class ValidateCodeView extends TextView {
private static final String TAG = "ValidateCodeView";
/**
* 退出界面后,在onDestroy方法中保存当前剩余时间
*/
private static final HashMap<Integer, Long> mStopTimes = new HashMap<>();
/**
* 前缀文本
*/
private String mPreStr;
/**
* 后缀文本
*/
private String mSuffixStr;
/**
* 原始显示字符串
*/
private String mSrcStr;
/**
* Millis since epoch when alarm should stop.
* 总时间
*/
private long mMillisInFuture = 3000;
/**
* 间隔时间毫秒 固定一秒
* The interval in millis that the user receives callbacks
*/
private final long mCountdownInterval = 1000;
/**
* 最终的完整时间 例如:当前时间mCurTime为2017年08月04日18点22分20秒 30秒后结束 那么mStopTimeInFuture是mCurTime加上30*1000毫秒
*/
private long mStopTimeInFuture;
/**
* boolean representing if the timer was cancelled
*/
private boolean mCancelled = false;
/**
* Hander发送信息的what标识,保存结束时间的key
*/
private int mHandlerSendCode;
// handles counting down
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
synchronized (ValidateCodeView.this) {
if (mCancelled) {
return;
}
//不同界面使用该空间时,发送的标识不同
if (msg.what != mHandlerSendCode) {
return;
}
final long millisLeft = mStopTimeInFuture - SystemClock.elapsedRealtime();
//SystemClock.elapsedRealtime(),开机持续时间,刚开机得到的时间为1970/01/01 00:00:00
//它包括了系统深度睡眠的时间。这个时钟是单调的,它保证一直计时,即使CPU处于省电模式,所以它是推荐使用的时间计时器。
if (millisLeft <= 0) {
onFinish();
} else {
long lastTickStart = SystemClock.elapsedRealtime();
setText(mPreStr + (millisLeft / 1000) + mSuffixStr);//前缀+剩余秒数+后缀
// take into account user's onTick taking time to execute
long delay = lastTickStart + mCountdownInterval - SystemClock.elapsedRealtime();
// special case: user's onTick took more than interval to
// complete, skip to next interval
while (delay < 0) delay += mCountdownInterval;
sendMessageDelayed(obtainMessage(mHandlerSendCode), delay);
}
}
}
};
public ValidateCodeView(Context context) {
super(context);
initView(context, null, 0);
}
public ValidateCodeView(Context context, AttributeSet attrs) {
super(context, attrs);
initView(context, attrs, 0);
}
public ValidateCodeView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initView(context, attrs, defStyleAttr);
}
private void initView(Context context, AttributeSet attrs, int defStyleAttr) {
TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.ValidateCodeView, defStyleAttr, 0);
long mTotalSeconds = a.getInteger(R.styleable.ValidateCodeView_mTotalSeconds, 30);
mMillisInFuture = mTotalSeconds * 1000l + 1000;//总毫秒数
mPreStr = a.getString(R.styleable.ValidateCodeView_mPreStr);//前缀文本
mSuffixStr = a.getString(R.styleable.ValidateCodeView_mSuffixStr);//后缀文本
mHandlerSendCode = a.getInteger(R.styleable.ValidateCodeView_mHandlerSendCode, 1);//布局文件中不写统一认为同一个验证码
a.recycle();
mSrcStr = getText().toString().trim();//获取原始文字,例如:发送验证码
setClickable(true);//设置可点击
}
/**
* Cancel the countdown.
*/
public synchronized final void cancel() {
mCancelled = true;
mHandler.removeMessages(mHandlerSendCode);
}
/**
* Start the countdown.
*/
public synchronized final void start() {
setEnabled(false);//设置控件不可用(不可点击)
mCancelled = false;
//间隔时间
if (mMillisInFuture <= 0) {
onFinish();
return;
}
//结束时间
mStopTimeInFuture = SystemClock.elapsedRealtime() + mMillisInFuture;
//发送信息
mHandler.sendMessage(mHandler.obtainMessage(mHandlerSendCode));
return;
}
/**
* Callback fired when the time is up.
*/
public void onFinish() {
setEnabled(true);
setText(mSrcStr);
}
/**
* 加入生命周期的方法,在Activity的onCreate方法中调用即可
*/
public void onCreate() {
//如果保存了当前页面的剩余时间则自动开始
if (mStopTimes.containsKey(mHandlerSendCode)) {
mStopTimeInFuture = mStopTimes.get(mHandlerSendCode);
mCancelled = false;
final long millisLeft = mStopTimeInFuture - SystemClock.elapsedRealtime();//剩余毫秒数
//剩余毫秒数小于或等于零,结束,否则设置按钮不可用
if (millisLeft <= 0) {
onFinish();
return;
}
setEnabled(false);
mHandler.sendMessage(mHandler.obtainMessage(mHandlerSendCode));
}
}
/**
* 加入生命周期的方法,在Activity的onCreate方法中调用即可
*/
public void onDestroy() {
//保存剩余时间
mStopTimes.put(mHandlerSendCode, mStopTimeInFuture);
}
}
2、使用篇
2.1在布局中引用控件
<com.ewhalelibrary.widget.ValidateCodeView
android:id="@+id/validateCodeView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:background="@color/red"
android:gravity="center"
android:padding="@dimen/px20dp"
android:text="发送验证码"
android:textColor="@color/select_code_text_color"
app:mHandlerSendCode="2"
app:mPreStr="剩余"
app:mSuffixStr="秒后重新发送"
app:mTotalSeconds="30" />
<!--
app:mHandlerSendCode="2" 默认为1,注册和修改密码的验证码,后台要分开的话,该值要设置成不同的数值
app:mPreStr="剩余" 前缀
app:mSuffixStr="秒后重新发送" 后缀
app:mTotalSeconds="30" 总时长30秒
-->
2.2在Activity或Fragment中绑定生命周期
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
validateCodeView.onCreate();
}
@Override
protected void onDestroy() {
validateCodeView.onDestroy();
super.onDestroy();
}
2.3调用发送验证码的接口成功后调用start方法即可
validateCodeView.start();
网友评论