1. CountDownTimer 倒计时(谷歌官方推荐)
CountDownTimer是android中自带的倒计时类
需要两个参数,一个是总计时的时间,一般验证码是60秒,就是60*1000。第二个参数是设置计时的速度,1000就是每秒一次。如果总长度是10秒,速度是1,那么就是10-9-8-7-6-5-4-3-2-1-0;
如果总长度是10秒,速度是2,那么就是10-8-6-4-2-0;
总时间是不变的,如果需要更精确的计时展现,就修改第二个参数。在创建对象的时候,需要重写onTick和onFinish的方法。
onTick就是我们剩余的时间,需要将之转化成秒展示出来。
在倒计时的过程中把按钮禁用。然后在onFinish倒计时结束的时候,按钮启用。
最后通过.start启动倒计时。
/**
* 倒计时显示
*/
CountDownTimer timer;
private void countDown() {
timer = new CountDownTimer(3000, 1000) {
@Override
public void onTick(long millisUntilFinished) {
//每秒执行一次
tvValue.setText( millisUntilFinished / 1000 + "s");
}
@Override
public void onFinish() {
// 最后执行完操作
if (timer!=null){
timer.cancel();
timer = null;
}
}
}.start();
}
注意:
在onTick或者onFinish的计时操作里定时跳转activity时,因为没有用timer.cancel()终止计时,会导致内存溢出等情况,在使用CountDownTimer时,在Activity或fragment生命周期结束时,调用timer.cancle()方法
if (timer != null) {
timer.cancel();
timer = null;
Intent intent=new Intent(MainActivity.this, Main2Activity.class);
startActivity(intent);
}
2. 使用线程
首先需要创建一个主线程,用来接收数据,改变UI
private int time=10;
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 0:
setResult(RESULT_OK);
finish();
break;
case 4:
button4.setEnabled(false);
button4.setText("已发送(" + String.valueOf(time) + ")");
break;
case 5:
button4.setEnabled(true);
button4.setText("重新获取验证码");
time = 10;
break;
default:
}
}
};
运行一个子线程,由于子线程中无法更新UI,所以需要根据time,将当前状态发送到主线程中,在主线程中操作。
当子线程执行的时候,按钮设置不可点击,并且将线程执行的结果一直发送给主线程,在主线程中动态显示倒计时。
当time倒计时为0的时候,将time重置,按钮重新设置可点击。
/**
* 倒计时显示
*/
private void countDown2() {
Runnable runnable = new Runnable() {
@Override
public void run() {
time--;
if (time <= 0) {
mHandler.sendEmptyMessage(5);
} else {
mHandler.sendEmptyMessage(4);
mHandler.postDelayed(this, 1000);
}
}
};
new Thread(runnable).start();
}
3. 使用Timer和TimerTask
Timer类官方的解释是多个线程共享一个计时器,也就是当使用了方法二之后,再使用三、线程会比开辟出来一部分给当前的线程。
假设总时间是10秒,点击按钮2,开始倒计时,同时点按钮3,时间会被瓜分掉。
但是只调用一个线程操作是没问题的。
/**
* 倒计时显示3
*/
int time = 10;
private Timer timer = new Timer();
private void countDown3() {
TimerTask task = new TimerTask() {
@Override
public void run() {
runOnUiThread(new Runnable() {
@Override
public void run() {
time--;
button4.setText("已发送(" + String.valueOf(time) + ")");
button4.setEnabled(false);
if (time <= 0) {
time = 10;
button4.setEnabled(true);
button4.setText("重新获取验证码");
}
}
});
}
};
//调用方法
timer.schedule(task, time, 1000);
}
注意:第三种方法,我忘记重新定义 time = 10;
报出“java.lang.IllegalArgumentException: Negative delay.”这个错误,就是因为TimerTask中的time已经为 0 了,你再调用 time-- ,就会报出这个错误。
4. 使用Handler倒计时(这个是不错的)
//未开课中
private Handler mHandler = new Handler();
private MyRunnable mRunnable;
private int downTime = 10;
if (mRunnable == null) {
mRunnable = new MyRunnable3();
mHandler.postDelayed(mRunnable3, 0);
}
/**
* 未上课计时实现
*/
private class MyRunnable implements Runnable {
@Override
public void run() {
--downTime;
if (downTime == 0) {
UIUtils.MakeTextAndShow(mContext, "即将开始上课");
showLessonState();
} else {
String value = UIUtils.formatTimeS(downTime);
tvTime.setText("还差\u2000" + value + "\u2000上课");
}
if (mHandler != null) {
mHandler.postDelayed(this, 1000);
}
}
}
5. ScheduledExecutorService
此方式中 handler 功能与 timer 方式一致
/**
* ScheduledExecutorService 方式实现
*/
private ScheduledExecutorService scheduled;
private void scheduledExecutorService() {
//初始化一个线程池大小为 1 的 ScheduledExecutorService
scheduled = new ScheduledThreadPoolExecutor(1);
mStart.setEnabled(false);//在发送数据的时候设置为不能点击
mStart.setBackgroundColor(Color.GRAY);//背景色设为灰色
scheduled.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
Message msg = mHandler.obtainMessage(0);
mHandler.sendMessage(msg);
}
}, 0, ONECE_TIME, TimeUnit.MILLISECONDS);
}
6. RxJava
此方法通过 RxJava 的 interval 操作符来实现延时发送消息。接受消息的观察者将计时信息更新到 textview 中。此方法主要涉及到一些 RxJava 操作符以及线程切换,代码中也进行了注释。
/**
* RxJava 方式实现
*/
private void rxJava() {
final long count = TOTAL_TIME / 1000;
Observable.interval(0, 1, TimeUnit.SECONDS)//设置0延迟,每隔一秒发送一条数据
.take((int) (count + 1)) //设置总共发送的次数
.map(new Func1<Long, Long>() {//long 值是从小到大,倒计时需要将值倒置
@Override
public Long call(Long aLong) {
return count - aLong;
}
})
.subscribeOn(Schedulers.computation())
// doOnSubscribe 执行线程由下游逻辑最近的 subscribeOn() 控制,下游没有 subscribeOn() 则跟Subscriber 在同一线程执行
//执行计时任务前先将 button 设置为不可点击
.doOnSubscribe(new Action0() {
@Override
public void call() {
mStart.setEnabled(false);//在发送数据的时候设置为不能点击
mStart.setBackgroundColor(Color.GRAY);//背景色设为灰色
}
})
.observeOn(AndroidSchedulers.mainThread())//操作UI主要在UI线程
.subscribe(new Subscriber<Long>() {
@Override
public void onCompleted() {
mTvValue.setText(getResources().getString(R.string.done));
mStart.setEnabled(true);
mStart.setBackgroundColor(Color.parseColor("#f97e7e"));
}
@Override
public void onError(Throwable e) {
e.printStackTrace();
}
@Override
public void onNext(Long aLong) { //接收到一条就是会操作一次UI
String value = String.valueOf(aLong);
mTvValue.setText(value);
}
});
}