Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱
MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina.com

倒计时 总结 Timer Handler RxJava


目录

目录
利用系统API的几种实现方式
使用 CountDownTimer 实现 - 最简洁【推荐】
CountDownTimer 简介
使用案例
使用 RxJava 实现 - 方便强大【推荐】
使用 Timer + Handler 实现 - 麻烦【不推荐】
Timer + 普通 Handler - 麻烦
Timer + 静态 Handler - 更麻烦
Timer + runOnUiThread - 也麻烦
使用纯 Handler 实现 - 特麻烦【强烈不建议】
开源框架 CountdownView 简介
在 RecyclerView 中实现倒计时
更改数据源方式 - 简单但不可靠
让 System 帮我们倒计时 - 推荐
自己维护倒计时 - 既麻烦又低效

利用系统API的几种实现方式

使用 CountDownTimer 实现 - 最简洁【推荐】

CountDownTimer 简介

文档

Schedule安排、清单 a countdown until a time in the future, with regular规律的 notifications on intervals间隔 along the way过程.

在文本字段中显示一个30秒倒计时的示例:

@BindView(R.id.send) Button send;//发送验证码
new CountDownTimer(60000, 1000) {@Overridepublic void onTick(long millisUntilFinished) {send.setText(millisUntilFinished / 1000 + "S");}@Overridepublic void onFinish() {send.setEnabled(true);send.setText("重新发送");}
}.start();

The calls to onTick(long) are synchronized同步 to this object so that one call to onTick(long) won't ever occur before the previous callback is complete.

This is only relevant相应、相关 when the implementation of onTick(long) takes an amount of一定数量的 time to execute执行 that is significant重大 compared to the countdown interval间隔.

API数量非常少,但各个都极其有用
构造方法

CountDownTimer(long millisInFuture, long countDownInterval)

  • millisInFuture: The number of millis in the future from the call to start() until the countdown is done and onFinish() is called.
  • countDownInterval: The interval along the way to receive onTick(long) callbacks.

开启和结束方法

final void cancel()
final CountDownTimer start()

抽象(回调)方法

abstract void onFinish():Callback fired when the time is up.
abstract void onTick(long millisUntilFinished):Callback fired on regular interval. millisUntilFinished: The amount of time until finished.

使用案例

@BindView(R.id.send) Button send;//发送验证码
private CountDownTimer timer;//使用CountDownTimer
​
@OnClick({R.id.send, R.id.next})
public void onClickIv(View v) {switch (v.getId()) {case R.id.send:setTimer();//实际是要在点击之后判断如果符合倒计时条件才调用 setTimer 方法,要在失败后调用 destoryTimerbreak;}
}
​
@Override
protected void onDestroy() {super.onDestroy();destoryTimer();
}
​
private void setTimer() {send.setEnabled(false);timer = new CountDownTimer(60 * 1000, 1000) {@Overridepublic void onTick(long millisUntilFinished) {int time = (int) (millisUntilFinished / 1000);send.setText(time + "s");}@Overridepublic void onFinish() {destoryTimer();}};timer.start();
}
​
private void destoryTimer() {send.setEnabled(true);send.setText("获取验证码");if (timer != null) {timer.cancel();timer = null;}
}

使用 RxJava 实现 - 方便强大【推荐】

可以使用 intervalRange 很方便的实现这个功能,也可以使用 repeat、repeatUntil、repeatWhen 间接实现类似功能。

@BindView(R.id.send) Button send;//发送验证码
private Disposable disposable;@OnClick({R.id.send, R.id.next})
public void onClickIv(View v) {switch (v.getId()) {case R.id.send:startCountDown();break;}
}
​
@Override
protected void onDestroy() {super.onDestroy();if (disposable != null && !disposable.isDisposed()) {disposable.dispose();}
}
​
private void startCountDown() {send.setEnabled(false);disposable = Observable.intervalRange(0, 10, 0, 1, TimeUnit.SECONDS) //起始值,发送总数量,初始延迟,固定延迟.observeOn(Schedulers.io()).subscribeOn(AndroidSchedulers.mainThread()).subscribe(time -> send.setText((30 - time) + "s"),Throwable::printStackTrace,() -> runOnUiThread(() -> {send.setEnabled(true);send.setText("获取验证码");}));
}

使用 Timer + Handler 实现 - 麻烦【不推荐】

Timer + 普通 Handler - 麻烦

@BindView(R.id.send) Button send;//发送验证码
private int time = 60;//倒计时时间
private Timer timer;
​
@OnClick({R.id.send, R.id.next})
public void onClickIv(View v) {switch (v.getId()) {case R.id.send:setTimer();//实际是要在点击之后判断如果符合倒计时条件才调用 setTimer 方法,要在失败后调用 destoryTimerbreak;}
}
​
@Override
protected void onDestroy() {super.onDestroy();destoryTimer();
}
​
@SuppressLint("HandlerLeak")
private Handler handler = new Handler() {@Overridepublic void handleMessage(android.os.Message msg) {switch (msg.what) {case 1:send.setText(time + "s");break;case 2:destoryTimer();break;}}
};
​
//定时器
private void setTimer() {send.setEnabled(false);//不可点击timer = new Timer();TimerTask task = new TimerTask() {@Overridepublic void run() {time--;if (time > 0) {handler.sendEmptyMessage(1);} else {handler.sendEmptyMessage(2);}}};timer.schedule(task, 0, 1000);//每隔一秒钟执行一次
}
​
private void destoryTimer() {time = 60;//重新倒计时send.setEnabled(true);//重新可点击send.setText("重新发送");//重设文字if (timer != null) {timer.cancel();timer = null;}if (handler!=null) {handler.removeCallbacksAndMessages(null);}
}

Timer + 静态 Handler - 更麻烦

相比示例一,是将Handler定义为了静态内部类,以防止内存泄漏

@BindView(R.id.send) Button send;//发送验证码
private int time = 60;//倒计时时间
private Timer timer;
​
@OnClick({R.id.send, R.id.next})
public void onClickIv(View v) {switch (v.getId()) {case R.id.send:setTimer();//实际是要在点击之后判断如果符合倒计时条件才调用 setTimer 方法,要在失败后调用 destoryTimerbreak;}
}
​
@Override
protected void onDestroy() {super.onDestroy();destoryTimer();
}
​
private Handler handler = new MyHandler(this);
private static class MyHandler extends Handler {private SoftReference<ForgetPasswordActivity> mSoftReference;MyHandler(ForgetPasswordActivity activity) {mSoftReference = new SoftReference<>(activity);}@Overridepublic void handleMessage(Message msg) {super.handleMessage(msg);ForgetPasswordActivity activity = mSoftReference.get();if (activity != null) {switch (msg.what) {case 1:activity.send.setText(activity.time + "s");break;case 2:activity.destoryTimer();break;}}}
}
​
//定时器
private void setTimer() {send.setEnabled(false);//不可点击timer = new Timer();TimerTask task = new TimerTask() {@Overridepublic void run() {time--;if (time > 0) {handler.sendEmptyMessage(1);} else {handler.sendEmptyMessage(2);}}};timer.schedule(task, 0, 1000);//每隔一秒钟执行一次
}
​
private void destoryTimer() {time = 60;//重新倒计时send.setEnabled(true);//重新可点击send.setText("重新发送");//重设文字if (timer != null) {timer.cancel();timer = null;}if (handler!=null) {handler.removeCallbacksAndMessages(null);}
}

Timer + runOnUiThread - 也麻烦

可以不用Handler而用其他更精简的API:

@BindView(R.id.send) Button send;//发送验证码
private int time = 60;//倒计时时间
private Timer timer;
​
@OnClick({R.id.send, R.id.next})
public void onClickIv(View v) {switch (v.getId()) {case R.id.send:setTimer();//实际是要在点击之后判断如果符合倒计时条件才调用 setTimer 方法,要在失败后调用 destoryTimerbreak;}
}
​
@Override
protected void onDestroy() {super.onDestroy();destoryTimer();
}
​
private void setTimer() {send.setEnabled(false);TimerTask task = new TimerTask() {@Overridepublic void run() {runOnUiThread(() -> {time--;if (time > 0) {send.setText(time + "s");} else {destoryTimer();}});}};timer = new Timer();timer.schedule(task, 0, 1000);//每隔一秒钟执行一次
}
​
private void destoryTimer() {time = 60;send.setEnabled(true);send.setText("获取验证码");if (timer != null) {timer.cancel();timer = null;}
}

使用纯 Handler 实现 - 特麻烦【强烈不建议】

倒计时通过用 Handler 发送 Delayed 消息来实现。核心代码为:

handler.sendMessageDelayed(handler.obtainMessage(1), 1000);
​
final Handler handler = new Handler() {public void handleMessage(Message msg) {switch (msg.what) {case 1:time--;if (time > 0) {send.setText(time + "S");handler.sendMessageDelayed(handler.obtainMessage(1), 1000);//循环发送} else {send.setEnabled(true);send.setText("重新发送");}}}
};

倒计时通过用 Handler 发送 Delayed 的 Runnable 来实现,和上面原理是完全一样的。核心代码为:

Handler handler = new Handler();
handler.postDelayed(runnable, 1000);
​
Runnable runnable = new Runnable() {@Overridepublic void run() {time--;if (time > 0) {send.setText(time + "S");handler.postDelayed(this, 1000);} else {send.setEnabled(true);send.setText("重新发送");}}
};

开源框架 CountdownView 简介

GitHub上星星最多的倒计时控件:CountdownView

CountdownView:Android倒计时控件,使用Canvas绘制,支持多种样式

compile 'com.github.iwgang:countdownview:2.1.3'

引用类名

cn.iwgang.countdownview.CountdownView

基本使用

CountdownView mCountdownView = (CountdownView)findViewById(R.id.countdownView);
mCountdownView.start(995550000); // 毫秒// 或者自己编写倒计时逻辑,然后调用 updateShow 来更新UI
for (int time=0; time<1000; time++) {mCountdownView.updateShow(time);
}

其他用法

  • 动态设置自定义属性:.dynamicShow(DynamicConfig)
  • 倒计时结束后的回调:.setOnCountdownEndListener(OnCountdownEndListener);
  • 指定间隔时间的回调:.setOnCountdownIntervalListener(long, OnCountdownIntervalListener);

在 RecyclerView 中实现倒计时

更改数据源方式 - 简单但不可靠

这种方案在数据量特别小(即List的size()特别小),且刷新item及计算倒计时耗费的时间特别短时适用,否则,将会产生巨大的时间延迟。

//定时器,用于刷新 GridView 的数据源
private void setQryTimer() {cancelQryTimer(); //取消之前的定时器qryTimer = new Timer(); //重新设置定时器qryTimer.schedule(new TimerTask() {@Overridepublic void run() {runOnUiThread(() -> {if (fixRpList != null && fixRpList.size() > 0) {for (FixRpBean item : fixRpList) {if (item.diff_time >= 0) item.diff_time = item.diff_time - 1000L; //更改数据源}if (fixRpDialog != null) fixRpDialog.upDate(fixRpList); //刷新页面}});}}, 0, 1000); //以固定的周期刷新
}public void upDate(List<FixRpBean> redPacketList) {list.clear(); //情况旧的数据list.addAll(redPacketList); //设置新的数据(如果列表的数据和源数据是同一个集合,也可以直接更新)mRecyclerView.getAdapter().notifyDataSetChanged();//建议使用RecyclerView的局部刷新功能
}

让 System 帮我们倒计时 - 推荐

核心思想为:利用System.currentTimeMillis()帮我们计算倒计时,并且在onViewAttachedToWindow时重新开始倒计时,在onViewDetachedFromWindow时关闭倒计时。

public class RecyclerViewActivity extends Activity {private List<ItemInfo> mDataList;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_recyclerview);initData();RecyclerView recyclerView = (RecyclerView) findViewById(R.id.rv);recyclerView.setAdapter(new MyAdapter(this, mDataList));recyclerView.setLayoutManager(new LinearLayoutManager(this));recyclerView.addItemDecoration(new DividerItemDecoration(this, DividerItemDecoration.VERTICAL));recyclerView.setItemAnimator(new DefaultItemAnimator());}private void initData() {mDataList = new ArrayList<>();for (int i = 1; i < 20; i++) {mDataList.add(new ItemInfo(i * 20 * 1000));}// 校对倒计时long curTime = System.currentTimeMillis();for (ItemInfo itemInfo : mDataList) {itemInfo.endTime = curTime + itemInfo.countdown;}}static class MyAdapter extends RecyclerView.Adapter<MyViewHolder> {private Context mContext;private List<ItemInfo> mDatas;public MyAdapter(Context context, List<ItemInfo> datas) {this.mContext = context;this.mDatas = datas;}@Overridepublic MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {return new MyViewHolder(LayoutInflater.from(mContext).inflate(R.layout.list_item, parent, false));}@Overridepublic void onBindViewHolder(MyViewHolder holder, int position) {holder.bindData(mDatas.get(holder.getAdapterPosition()));}@Overridepublic int getItemCount() {return mDatas.size();}//******************************************** 关键代码 ↓↓ **********************************@Overridepublic void onViewAttachedToWindow(MyViewHolder holder) {super.onViewAttachedToWindow(holder);//父类中为空代码holder.refreshTime(mDatas.get(holder.getAdapterPosition()).endTime - System.currentTimeMillis());}@Overridepublic void onViewDetachedFromWindow(MyViewHolder holder) {super.onViewDetachedFromWindow(holder);holder.countdownView.stop();}//******************************************** 关键代码 ↑↑ **********************************}static class MyViewHolder extends RecyclerView.ViewHolder {public CountdownView countdownView;public MyViewHolder(View itemView) {super(itemView);countdownView = (CountdownView) itemView.findViewById(R.id.countdownView);}public void bindData(ItemInfo itemInfo) {refreshTime(itemInfo.endTime - System.currentTimeMillis());}public void refreshTime(long leftTime) {if (leftTime > 0) {countdownView.start(leftTime);} else {countdownView.stop();//停止计时器,mCustomCountDownTimer.stop();countdownView.allShowZero();//所有计时清零,即mCountdown.setTimes(0, 0, 0, 0, 0);}}}static class ItemInfo {public long countdown;/*根据服务器返回的countdown换算成手机对应的开奖时间 (毫秒)[正常情况最好由服务器返回countdown字段,然后客户端再校对成该手机对应的时间,不然误差很大]*/public long endTime;public ItemInfo(long countdown) {this.countdown = countdown;}}
}

自己维护倒计时 - 既麻烦又低效

自己维护倒计时,再调用 countdownView.updateShow 来刷新显示
并且根据需要在 onResume 时开启倒计时,在 onPause 及 onDestroy 时关闭倒计时。

//自己维护倒计时,再调用 countdownView.updateShow 来刷新显示
public class RecyclerViewActivity2 extends AppCompatActivity {private MyAdapter mMyAdapter;private List<ItemInfo> mDataList;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_recyclerview);initData();RecyclerView recyclerView = (RecyclerView) findViewById(R.id.rv);mMyAdapter = new RecyclerViewActivity2.MyAdapter(this, mDataList);recyclerView.setAdapter(mMyAdapter);recyclerView.setLayoutManager(new LinearLayoutManager(this));recyclerView.addItemDecoration(new DividerItemDecoration(this, DividerItemDecoration.VERTICAL));recyclerView.setItemAnimator(new DefaultItemAnimator());}private void initData() {mDataList = new ArrayList<>();for (int i = 1; i < 20; i++) {mDataList.add(new ItemInfo(1000 + i, "RecyclerView_测试标题_" + i, i * 20 * 1000));}// 校对倒计时long curTime = System.currentTimeMillis();for (ItemInfo itemInfo : mDataList) {itemInfo.setEndTime(curTime + itemInfo.getCountdown());}}@Overrideprotected void onResume() {super.onResume();if (null != mMyAdapter) }@Overrideprotected void onPause() {super.onPause();if (null != mMyAdapter) }@Overridepublic void onDestroy() {super.onDestroy();if (null != mMyAdapter) }static class MyAdapter extends RecyclerView.Adapter<MyViewHolder> {private Context mContext;private List<ItemInfo> mDatas;private final SparseArray<MyViewHolder> mCountdownVHList;private Handler mHandler = new Handler();private Timer mTimer;private boolean isCancel = true;public MyAdapter(Context context, List<ItemInfo> datas) {this.mContext = context;this.mDatas = datas;mCountdownVHList = new SparseArray<>();startRefreshTime(); //开启倒计时}public void startRefreshTime() {if (!isCancel) return;if (null != mTimer) mTimer.cancel();isCancel = false;mTimer = new Timer();mTimer.schedule(new TimerTask() {@Overridepublic void run() {mHandler.post(mRefreshTimeRunnable);}}, 0, 10);}public void cancelRefreshTime() {isCancel = true;if (null != mTimer) {mTimer.cancel();}mHandler.removeCallbacks(mRefreshTimeRunnable);}@Overridepublic MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {return new MyViewHolder(LayoutInflater.from(mContext).inflate(R.layout.list_item, parent, false));}@Overridepublic void onBindViewHolder(MyViewHolder holder, int position) {ItemInfo curItemInfo = mDatas.get(position);holder.bindData(curItemInfo);// 处理倒计时if (curItemInfo.getCountdown() > 0) {synchronized (mCountdownVHList) {mCountdownVHList.put(curItemInfo.getId(), holder); //开启倒计时}}}@Overridepublic int getItemCount() {return mDatas.size();}@Overridepublic void onViewRecycled(MyViewHolder holder) {super.onViewRecycled(holder);ItemInfo curAnnounceGoodsInfo = holder.getBean();if (null != curAnnounceGoodsInfo && curAnnounceGoodsInfo.getCountdown() > 0) {mCountdownVHList.remove(curAnnounceGoodsInfo.getId()); //移除}}private Runnable mRefreshTimeRunnable = new Runnable() {@Overridepublic void run() {if (mCountdownVHList.size() == 0) return;synchronized (mCountdownVHList) {long currentTime = System.currentTimeMillis();int key;for (int i = 0; i < mCountdownVHList.size(); i++) {key = mCountdownVHList.keyAt(i);MyViewHolder curMyViewHolder = mCountdownVHList.get(key);if (currentTime >= curMyViewHolder.getBean().getEndTime()) {curMyViewHolder.getBean().setCountdown(0);// 倒计时结束mCountdownVHList.remove(key);notifyDataSetChanged();} else {curMyViewHolder.refreshTime(currentTime); //刷新时间}}}}};}static class MyViewHolder extends RecyclerView.ViewHolder {private TextView mTvTitle;private CountdownView mCvCountdownView;private ItemInfo mItemInfo;public MyViewHolder(View itemView) {super(itemView);mTvTitle = (TextView) itemView.findViewById(R.id.tv_title);mCvCountdownView = (CountdownView) itemView.findViewById(R.id.cv_countdownView);}public void bindData(ItemInfo itemInfo) {mItemInfo = itemInfo;if (itemInfo.getCountdown() > 0) {refreshTime(System.currentTimeMillis());} else {mCvCountdownView.allShowZero();}mTvTitle.setText(itemInfo.getTitle());}public void refreshTime(long curTimeMillis) {if (null == mItemInfo || mItemInfo.getCountdown() <= 0) return;mCvCountdownView.updateShow(mItemInfo.getEndTime() - curTimeMillis);}public ItemInfo getBean() {return mItemInfo;}}static class ItemInfo {private int id;private String title;private long countdown;/*根据服务器返回的countdown换算成手机对应的开奖时间 (毫秒)[正常情况最好由服务器返回countdown字段,然后客户端再校对成该手机对应的时间,不然误差很大]*/private long endTime;public ItemInfo(int id, String title, long countdown) {this.id = id;this.title = title;this.countdown = countdown;}//get、set方法...}}

2017-6-12

倒计时 总结 Timer Handler CountDownTimer RxJava MD相关推荐

  1. Android 倒计时——Timer和CountDownTimer的使用,实现启动,暂停,继续,重复,重设时长以及启动service后台倒计时

    实现效果 单个倒计时功能                                                                                 列表倒计时功能 ...

  2. 倒计时实现方案总结 Timer Handler

    利用Timer实现倒计时 @BindView(R.id.send) Button send;//发送验证码 private int time = 60;//倒计时 private Timer time ...

  3. android 倒计时handle,android -handler 实现倒计时

    实现倒计时想到了三个方案 1.countDownTimer sdk较高版本有bug 计时不精准 2.timer 和timer task的方式 但是在timertask不可以直接更新页面,还是需要用ha ...

  4. Android项目开发实战—倒计时[Handler,Timer,TimerTask,Message]

    Android实现倒计时 先上一个本人实际操作中的界面: 源代码: (activity_main.xml和MainActivity.java) activity_main.xml: <Linea ...

  5. CountDownTimer 实现验证码倒计时

    先看下完整的代码,如是使用入职过去即可 public class MainActivity extends AppCompatActivity {private TextView code;@Over ...

  6. 五分钟掌握计时器CountDownTimer,快速实现倒计时功能

    今天分享一个基础知识,主要有: 1.解决CountDownTimer计时器少一秒的问题: 2.实现一个简单的自定义view 关于计时器,Android已经分装好了CountDownTimer,给我们的 ...

  7. Android实现倒计时之使用CountDownTimer

    在开发中会经常用到倒计时这个功能,包括给手机发送验证码等等,之前我的做法都是使用Handler + Timer + TimerTask来实现,现在发现了这个类,果断抛弃之前的做法,相信还是有很多人和我 ...

  8. 倒计时的CountDownTimer

    直接看这里吧,我只是搬运工. 定时执行在一段时候后停止的倒计时,在倒计时执行过程中会在固定间隔时间得到通知(译者:触发onTick方法),下面的例子显示在一个文本框中显示一个30s倒计时: Java代 ...

  9. android中倒计时控件CountDownTimer分析

    android中倒计时控件CountDownTimer分析1 示例代码 new CountDownTimer(10000, 1000) {public void onTick(long millisU ...

最新文章

  1. poj2112(floyd+二分+二分图多重匹配)
  2. Yii2 获取URL的一些方法
  3. NavigationView的使用
  4. zabbix-agent端自定义监控项(free -m)服务器内存使用率
  5. 20190403vim编辑器week1_day3
  6. 【HDU 4394】Digital Square(bfs,数位搜索,思维,数学)
  7. go中defer的一个隐藏功能
  8. 倒计时小工具_想要工作效率更高?这几款计时工具你一定不能错过!
  9. bread是可数还是不可数_为什么英语里的面包bread是不可数名词?听老师给你讲语法,一听就明白了...
  10. python getsize_Python getsizeof()和getsize()区分详解
  11. Node-介绍与模块化
  12. Scanner初学需要注意的几个问题
  13. 面试题--------1、HashMap和HashTable的区别
  14. 2017年最具价值的十大开源项目:tensorflow 第一
  15. 简单的JSON解析工具
  16. Microsoft Office 2010组件介绍
  17. div左对齐与里面的内容偏左但是距离左边有点儿距离
  18. pycharm复制代码出现空格
  19. 手机app访问服务器数据库数据库文件夹,手机app怎么访问服务器数据库
  20. STL——SET操作与并交差

热门文章

  1. [渝粤教育] 西南科技大学 土木工程施工 在线考试复习资料
  2. 毕业设计之 - 题目:基于机器视觉的试卷批改系统 - opencv python 视觉识别
  3. 买来仅使用了半年多的神舟笔记本翻船,怎么办?自己动手维修看看!
  4. nginx如何解决惊群效应
  5. C语言中字符串变量的函数值传递与指针传递
  6. java if函数的使用方法_IF函数的所有公式(入门+进阶+高级)
  7. HTML文本格式的应用实验原理,实验报告格式
  8. C++程序中执行abort等操作导致没有生成dump文件的问题案例分析
  9. ps软件怎么测试性能,photoshop如何设置性能
  10. 豆瓣评分9.3,吴军博士重磅新作,修炼你的计算思维!(文末赠书)