利用Timer实现倒计时
@BindView(R.id.send) Button send;//发送验证码private int time = 60;//倒计时private Timer timer;private Handler handler = new Handler() {public void handleMessage(android.os.Message msg) {switch (msg.what) {case 1:send.setText(time + "S");break;case 2:send.setEnabled(true);send.setText("重新发送");break;}}};@OnClick({R.id.send, R.id.next})public void onClickIv(View v) {switch (v.getId()) {case R.id.send:send.setEnabled(false);getVerificationCode();break;}}//定时器private void setTimer() {timer = new Timer();TimerTask task = new TimerTask() {@Overridepublic void run() {time--;if (time > 0) handler.sendEmptyMessage(1);else {handler.sendEmptyMessage(2);destoryTimer();}}};timer.schedule(task, 0, 1000);}private void destoryTimer() {if (timer != null) {timer.cancel();timer = null;}}@Overrideprotected void onDestroy() {super.onDestroy();destoryTimer();}
当然也可以不用Handler而用其他更精简的API,比如:
TimerTask task = new TimerTask() {
@Override
public void run() {
runOnUiThread(new Runnable() {
@Override
public void run() {
time--;
if (time > 0) send.setText(time + "S");
else {
send.setEnabled(true);
send.setText("重新发送");
destoryTimer();
}
}
});
}
};
利用Handler实现倒计时
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 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("重新发送");}}};
利用CountDownTimer实现
CountDownTimer timer = new CountDownTimer(60000, 1000) {@Overridepublic void onTick(long millisUntilFinished) {send.setText(time + "S");}@Overridepublic void onFinish() {send.setEnabled(true);send.setText("重新发送");}};
GitHub上星星最多的倒计时控件
CountdownView:Android倒计时控件,使用Canvas绘制,支持多种样式
compile 'com.github.iwgang:countdownview:2.1.3'
XML
<cn.iwgang.countdownview.CountdownViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"/>
基本使用
CountdownView mCountdownView = (CountdownView)findViewById(R.id.countdownView);mCountdownView.start(995550000); // 毫秒// 或者自己编写倒计时逻辑,然后调用updateShow来更新UIfor (int time=0; time<1000; time++) {mCountdownView.updateShow(time);}
动态显示设置, 支持所有xml中的配置项来使用java代码设置
mCountdownView.dynamicShow(DynamicConfig dynamicConfig)
mCountdownView.setOnCountdownEndListener(OnCountdownEndListener);
mCountdownView.setOnCountdownIntervalListener(long, OnCountdownIntervalListener);
RecyclerView中倒计时方案1【最简单】
这种方案在数据量特别小(即List的size()特别小),且刷新item及计算倒计时耗费的时间特别短时适用,否则,将会产生巨大的时间延迟。
1、更改数据源(即重新设置倒计时剩余时间)
//定时器,用于刷新GridView的数据源private void setQryTimer() {cancelQryTimer();qryTimer = new Timer();qryTimer.schedule(new TimerTask() {@Overridepublic void run() {runOnUiThread(new Runnable() {public void run() {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);}
2、刷新item(建议使用RecyclerView的局部刷新功能)
public void upDate(List<FixRpBean> redPacketList) {list.clear();list.addAll(redPacketList);mRecyclerView.getAdapter().notifyDataSetChanged();//建议使用RecyclerView的局部刷新功能}
RecyclerView中倒计时方案2【推荐】
核心思想为:利用System.currentTimeMillis()帮我们计算倒计时。
并且:在onViewAttachedToWindow时重新开始倒计时,在onViewDetachedFromWindow时关闭倒计时。
/*** 复用 本地的计时器 —— System.currentTimeMillis(), 不必自行计时*/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;}}}
RecyclerView中倒计时方案3【最麻烦】
这种方案为:自己维护倒计时,再调用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)mMyAdapter.startRefreshTime();}@Overrideprotected void onPause() {super.onPause();if (null != mMyAdapter)mMyAdapter.cancelRefreshTime();}@Overridepublic void onDestroy() {super.onDestroy();if (null != mMyAdapter)mMyAdapter.cancelRefreshTime();}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;}public int getId() {return id;}public void setId(int id) {this.id = id;}public String getTitle() {return title;}public void setTitle(String title) {this.title = title;}public long getCountdown() {return countdown;}public void setCountdown(long countdown) {this.countdown = countdown;}public long getEndTime() {return endTime;}public void setEndTime(long endTime) {this.endTime = endTime;}}}
2017-6-12
