倒计时实现方案总结 Timer Handler

利用Timer实现倒计时

  1. @BindView(R.id.send) Button send;//发送验证码
  2. private int time = 60;//倒计时
  3. private Timer timer;
  4. private Handler handler = new Handler() {
  5. public void handleMessage(android.os.Message msg) {
  6. switch (msg.what) {
  7. case 1:
  8. send.setText(time + "S");
  9. break;
  10. case 2:
  11. send.setEnabled(true);
  12. send.setText("重新发送");
  13. break;
  14. }
  15. }
  16. };
  17. @OnClick({R.id.send, R.id.next})
  18. public void onClickIv(View v) {
  19. switch (v.getId()) {
  20. case R.id.send:
  21. send.setEnabled(false);
  22. getVerificationCode();
  23. break;
  24. }
  25. }
  26. //定时器
  27. private void setTimer() {
  28. timer = new Timer();
  29. TimerTask task = new TimerTask() {
  30. @Override
  31. public void run() {
  32. time--;
  33. if (time > 0) handler.sendEmptyMessage(1);
  34. else {
  35. handler.sendEmptyMessage(2);
  36. destoryTimer();
  37. }
  38. }
  39. };
  40. timer.schedule(task, 0, 1000);
  41. }
  42. private void destoryTimer() {
  43. if (timer != null) {
  44. timer.cancel();
  45. timer = null;
  46. }
  47. }
  48. @Override
  49. protected void onDestroy() {
  50. super.onDestroy();
  51. destoryTimer();
  52. }
当然也可以不用Handler而用其他更精简的API,比如:
  1. TimerTask task = new TimerTask() {
  2. @Override
  3. public void run() {
  4. runOnUiThread(new Runnable() {
  5. @Override
  6. public void run() {
  7. time--;
  8. if (time > 0) send.setText(time + "S");
  9. else {
  10. send.setEnabled(true);
  11. send.setText("重新发送");
  12. destoryTimer();
  13. }
  14. }
  15. });
  16. }
  17. };

利用Handler实现倒计时

  1. handler.sendMessageDelayed(handler.obtainMessage(1), 1000);
  2. final Handler handler = new Handler() {
  3. public void handleMessage(Message msg) {
  4. switch (msg.what) {
  5. case 1:
  6. time--;
  7. if (time > 0) {
  8. send.setText(time + "S");
  9. handler.sendMessageDelayed(handler.obtainMessage(1), 1000);
  10. } else {
  11. send.setEnabled(true);
  12. send.setText("重新发送");
  13. }
  14. }
  15. }
  16. };
类似的实现方式
  1. Handler handler = new Handler();
  2. handler.postDelayed(runnable, 1000);
  3. Runnable runnable = new Runnable() {
  4. @Override
  5. public void run() {
  6. time--;
  7. if (time > 0) {
  8. send.setText(time + "S");
  9. handler.postDelayed(this, 1000);
  10. } else {
  11. send.setEnabled(true);
  12. send.setText("重新发送");
  13. }
  14. }
  15. };

利用CountDownTimer实现

  1. CountDownTimer timer = new CountDownTimer(60000, 1000) {
  2. @Override
  3. public void onTick(long millisUntilFinished) {
  4. send.setText(time + "S");
  5. }
  6. @Override
  7. public void onFinish() {
  8. send.setEnabled(true);
  9. send.setText("重新发送");
  10. }
  11. };

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


CountdownView:Android倒计时控件,使用Canvas绘制,支持多种样式
  1. compile 'com.github.iwgang:countdownview:2.1.3'
XML
  1. <cn.iwgang.countdownview.CountdownView
  2. android:layout_width="wrap_content"
  3. android:layout_height="wrap_content" />
基本使用
  1. CountdownView mCountdownView = (CountdownView)findViewById(R.id.countdownView);
  2. mCountdownView.start(995550000); // 毫秒
  3. // 或者自己编写倒计时逻辑,然后调用updateShow来更新UI
  4. for (int time=0; time<1000; time++) {
  5. mCountdownView.updateShow(time);
  6. }
动态显示设置, 支持所有xml中的配置项来使用java代码设置
  1. mCountdownView.dynamicShow(DynamicConfig dynamicConfig)
倒计时结束后回调
  1. mCountdownView.setOnCountdownEndListener(OnCountdownEndListener);
指定间隔时间回调
  1. mCountdownView.setOnCountdownIntervalListener(long, OnCountdownIntervalListener);

RecyclerView中倒计时方案1【最简单】

这种方案在数据量特别小(即List的size()特别小),且刷新item及计算倒计时耗费的时间特别短时适用,否则,将会产生巨大的时间延迟。
1、更改数据源(即重新设置倒计时剩余时间)
  1. //定时器,用于刷新GridView的数据源
  2. private void setQryTimer() {
  3. cancelQryTimer();
  4. qryTimer = new Timer();
  5. qryTimer.schedule(new TimerTask() {
  6. @Override
  7. public void run() {
  8. runOnUiThread(new Runnable() {
  9. public void run() {
  10. if (fixRpList != null && fixRpList.size() > 0) {
  11. for (FixRpBean item : fixRpList) {
  12. if (item.diff_time >= 0) item.diff_time = item.diff_time - 1000L;
  13. }
  14. if (fixRpDialog != null) fixRpDialog.upDate(fixRpList);
  15. }
  16. }
  17. });
  18. }
  19. }, 0, 1000);
  20. }
2、刷新item(建议使用RecyclerView的局部刷新功能)
  1. public void upDate(List<FixRpBean> redPacketList) {
  2. list.clear();
  3. list.addAll(redPacketList);
  4. mRecyclerView.getAdapter().notifyDataSetChanged();//建议使用RecyclerView的局部刷新功能
  5. }

RecyclerView中倒计时方案2【推荐】

核心思想为:利用System.currentTimeMillis()帮我们计算倒计时。
并且:在onViewAttachedToWindow时重新开始倒计时,在onViewDetachedFromWindow时关闭倒计时。
  1. /**
  2. * 复用 本地的计时器 —— System.currentTimeMillis(), 不必自行计时
  3. */
  4. public class RecyclerViewActivity extends Activity {
  5. private List<ItemInfo> mDataList;
  6. @Override
  7. protected void onCreate(Bundle savedInstanceState) {
  8. super.onCreate(savedInstanceState);
  9. setContentView(R.layout.activity_recyclerview);
  10. initData();
  11. RecyclerView recyclerView = (RecyclerView) findViewById(R.id.rv);
  12. recyclerView.setAdapter(new MyAdapter(this, mDataList));
  13. recyclerView.setLayoutManager(new LinearLayoutManager(this));
  14. recyclerView.addItemDecoration(new DividerItemDecoration(this, DividerItemDecoration.VERTICAL));
  15. recyclerView.setItemAnimator(new DefaultItemAnimator());
  16. }
  17. private void initData() {
  18. mDataList = new ArrayList<>();
  19. for (int i = 1; i < 20; i++) {
  20. mDataList.add(new ItemInfo(i * 20 * 1000));
  21. }
  22. // 校对倒计时
  23. long curTime = System.currentTimeMillis();
  24. for (ItemInfo itemInfo : mDataList) {
  25. itemInfo.endTime = curTime + itemInfo.countdown;
  26. }
  27. }
  28. static class MyAdapter extends RecyclerView.Adapter<MyViewHolder> {
  29. private Context mContext;
  30. private List<ItemInfo> mDatas;
  31. public MyAdapter(Context context, List<ItemInfo> datas) {
  32. this.mContext = context;
  33. this.mDatas = datas;
  34. }
  35. @Override
  36. public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
  37. return new MyViewHolder(LayoutInflater.from(mContext).inflate(R.layout.list_item, parent, false));
  38. }
  39. @Override
  40. public void onBindViewHolder(MyViewHolder holder, int position) {
  41. holder.bindData(mDatas.get(holder.getAdapterPosition()));
  42. }
  43. @Override
  44. public int getItemCount() {
  45. return mDatas.size();
  46. }
  47. //**************************************************关键代码****************************************
  48. @Override
  49. public void onViewAttachedToWindow(MyViewHolder holder) {
  50. super.onViewAttachedToWindow(holder);//父类中为空代码
  51. holder.refreshTime(mDatas.get(holder.getAdapterPosition()).endTime - System.currentTimeMillis());
  52. }
  53. @Override
  54. public void onViewDetachedFromWindow(MyViewHolder holder) {
  55. super.onViewDetachedFromWindow(holder);
  56. holder.countdownView.stop();
  57. }
  58. //**************************************************关键代码****************************************
  59. }
  60. static class MyViewHolder extends RecyclerView.ViewHolder {
  61. public CountdownView countdownView;
  62. public MyViewHolder(View itemView) {
  63. super(itemView);
  64. countdownView = (CountdownView) itemView.findViewById(R.id.countdownView);
  65. }
  66. public void bindData(ItemInfo itemInfo) {
  67. refreshTime(itemInfo.endTime - System.currentTimeMillis());
  68. }
  69. public void refreshTime(long leftTime) {
  70. if (leftTime > 0) {
  71. countdownView.start(leftTime);
  72. } else {
  73. countdownView.stop();//停止计时器,mCustomCountDownTimer.stop();
  74. countdownView.allShowZero();//所有计时清零,即mCountdown.setTimes(0, 0, 0, 0, 0);
  75. }
  76. }
  77. }
  78. static class ItemInfo {
  79. public long countdown;
  80. /*
  81. 根据服务器返回的countdown换算成手机对应的开奖时间 (毫秒)
  82. [正常情况最好由服务器返回countdown字段,然后客户端再校对成该手机对应的时间,不然误差很大]
  83. */
  84. public long endTime;
  85. public ItemInfo(long countdown) {
  86. this.countdown = countdown;
  87. }
  88. }
  89. }

RecyclerView中倒计时方案3【最麻烦】

这种方案为:自己维护倒计时,再调用countdownView.updateShow来刷新显示
并且:在onResume时开启倒计时,在onPause及onDestroy时关闭倒计时。
个人极其不推荐这种方案,既麻烦又低效。
  1. /*
  2. 自己维护倒计时任何,再调用countdownView.updateShow来刷新显示
  3. */
  4. public class RecyclerViewActivity2 extends AppCompatActivity {
  5. private MyAdapter mMyAdapter;
  6. private List<ItemInfo> mDataList;
  7. @Override
  8. protected void onCreate(Bundle savedInstanceState) {
  9. super.onCreate(savedInstanceState);
  10. setContentView(R.layout.activity_recyclerview);
  11. initData();
  12. RecyclerView recyclerView = (RecyclerView) findViewById(R.id.rv);
  13. mMyAdapter = new RecyclerViewActivity2.MyAdapter(this, mDataList);
  14. recyclerView.setAdapter(mMyAdapter);
  15. recyclerView.setLayoutManager(new LinearLayoutManager(this));
  16. recyclerView.addItemDecoration(new DividerItemDecoration(this, DividerItemDecoration.VERTICAL));
  17. recyclerView.setItemAnimator(new DefaultItemAnimator());
  18. }
  19. private void initData() {
  20. mDataList = new ArrayList<>();
  21. for (int i = 1; i < 20; i++) {
  22. mDataList.add(new ItemInfo(1000 + i, "RecyclerView_测试标题_" + i, i * 20 * 1000));
  23. }
  24. // 校对倒计时
  25. long curTime = System.currentTimeMillis();
  26. for (ItemInfo itemInfo : mDataList) {
  27. itemInfo.setEndTime(curTime + itemInfo.getCountdown());
  28. }
  29. }
  30. @Override
  31. protected void onResume() {
  32. super.onResume();
  33. if (null != mMyAdapter) mMyAdapter.startRefreshTime();
  34. }
  35. @Override
  36. protected void onPause() {
  37. super.onPause();
  38. if (null != mMyAdapter) mMyAdapter.cancelRefreshTime();
  39. }
  40. @Override
  41. public void onDestroy() {
  42. super.onDestroy();
  43. if (null != mMyAdapter) mMyAdapter.cancelRefreshTime();
  44. }
  45. static class MyAdapter extends RecyclerView.Adapter<MyViewHolder> {
  46. private Context mContext;
  47. private List<ItemInfo> mDatas;
  48. private final SparseArray<MyViewHolder> mCountdownVHList;
  49. private Handler mHandler = new Handler();
  50. private Timer mTimer;
  51. private boolean isCancel = true;
  52. public MyAdapter(Context context, List<ItemInfo> datas) {
  53. this.mContext = context;
  54. this.mDatas = datas;
  55. mCountdownVHList = new SparseArray<>();
  56. startRefreshTime();
  57. }
  58. public void startRefreshTime() {
  59. if (!isCancel) return;
  60. if (null != mTimer) mTimer.cancel();
  61. isCancel = false;
  62. mTimer = new Timer();
  63. mTimer.schedule(new TimerTask() {
  64. @Override
  65. public void run() {
  66. mHandler.post(mRefreshTimeRunnable);
  67. }
  68. }, 0, 10);
  69. }
  70. public void cancelRefreshTime() {
  71. isCancel = true;
  72. if (null != mTimer) {
  73. mTimer.cancel();
  74. }
  75. mHandler.removeCallbacks(mRefreshTimeRunnable);
  76. }
  77. @Override
  78. public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
  79. return new MyViewHolder(LayoutInflater.from(mContext).inflate(R.layout.list_item, parent, false));
  80. }
  81. @Override
  82. public void onBindViewHolder(MyViewHolder holder, int position) {
  83. ItemInfo curItemInfo = mDatas.get(position);
  84. holder.bindData(curItemInfo);
  85. // 处理倒计时
  86. if (curItemInfo.getCountdown() > 0) {
  87. synchronized (mCountdownVHList) {
  88. mCountdownVHList.put(curItemInfo.getId(), holder);
  89. }
  90. }
  91. }
  92. @Override
  93. public int getItemCount() {
  94. return mDatas.size();
  95. }
  96. @Override
  97. public void onViewRecycled(MyViewHolder holder) {
  98. super.onViewRecycled(holder);
  99. ItemInfo curAnnounceGoodsInfo = holder.getBean();
  100. if (null != curAnnounceGoodsInfo && curAnnounceGoodsInfo.getCountdown() > 0) {
  101. mCountdownVHList.remove(curAnnounceGoodsInfo.getId());
  102. }
  103. }
  104. private Runnable mRefreshTimeRunnable = new Runnable() {
  105. @Override
  106. public void run() {
  107. if (mCountdownVHList.size() == 0) return;
  108. synchronized (mCountdownVHList) {
  109. long currentTime = System.currentTimeMillis();
  110. int key;
  111. for (int i = 0; i < mCountdownVHList.size(); i++) {
  112. key = mCountdownVHList.keyAt(i);
  113. MyViewHolder curMyViewHolder = mCountdownVHList.get(key);
  114. if (currentTime >= curMyViewHolder.getBean().getEndTime()) {
  115. curMyViewHolder.getBean().setCountdown(0);// 倒计时结束
  116. mCountdownVHList.remove(key);
  117. notifyDataSetChanged();
  118. } else {
  119. curMyViewHolder.refreshTime(currentTime);
  120. }
  121. }
  122. }
  123. }
  124. };
  125. }
  126. static class MyViewHolder extends RecyclerView.ViewHolder {
  127. private TextView mTvTitle;
  128. private CountdownView mCvCountdownView;
  129. private ItemInfo mItemInfo;
  130. public MyViewHolder(View itemView) {
  131. super(itemView);
  132. mTvTitle = (TextView) itemView.findViewById(R.id.tv_title);
  133. mCvCountdownView = (CountdownView) itemView.findViewById(R.id.cv_countdownView);
  134. }
  135. public void bindData(ItemInfo itemInfo) {
  136. mItemInfo = itemInfo;
  137. if (itemInfo.getCountdown() > 0) {
  138. refreshTime(System.currentTimeMillis());
  139. } else {
  140. mCvCountdownView.allShowZero();
  141. }
  142. mTvTitle.setText(itemInfo.getTitle());
  143. }
  144. public void refreshTime(long curTimeMillis) {
  145. if (null == mItemInfo || mItemInfo.getCountdown() <= 0) return;
  146. mCvCountdownView.updateShow(mItemInfo.getEndTime() - curTimeMillis);
  147. }
  148. public ItemInfo getBean() {
  149. return mItemInfo;
  150. }
  151. }
  152. static class ItemInfo {
  153. private int id;
  154. private String title;
  155. private long countdown;
  156. /*
  157. 根据服务器返回的countdown换算成手机对应的开奖时间 (毫秒)
  158. [正常情况最好由服务器返回countdown字段,然后客户端再校对成该手机对应的时间,不然误差很大]
  159. */
  160. private long endTime;
  161. public ItemInfo(int id, String title, long countdown) {
  162. this.id = id;
  163. this.title = title;
  164. this.countdown = countdown;
  165. }
  166. public int getId() {
  167. return id;
  168. }
  169. public void setId(int id) {
  170. this.id = id;
  171. }
  172. public String getTitle() {
  173. return title;
  174. }
  175. public void setTitle(String title) {
  176. this.title = title;
  177. }
  178. public long getCountdown() {
  179. return countdown;
  180. }
  181. public void setCountdown(long countdown) {
  182. this.countdown = countdown;
  183. }
  184. public long getEndTime() {
  185. return endTime;
  186. }
  187. public void setEndTime(long endTime) {
  188. this.endTime = endTime;
  189. }
  190. }
  191. }
2017-6-12




原文地址:https://www.cnblogs.com/baiqiantao/p/7897ca6555535885bc83fe27d207a9b4.html