今天做一个活动倒计时,挺简单的,但是在计算间隔时间到秒的时候,却发现不精确,只能精确到分,但是需求需要,之后发现在计算小时的d_time/1000/60是有余的,给丢失了,丢失的刚好是需要的秒数,不仔细很难发现,记录一下
一,倒计时
/**
* 活动倒计时
* @param end_time
* @return
*/
public static HashMap<Object,Object> activitiesCountdownTime(String end_time){
HashMap<Object,Object> map=new HashMap<>();
String start_time=getCurrentTime();
long d_time=getLongtime(end_time)-getLongtime(start_time);
if(d_time>0){
long h=d_time/1000/60/60;//时
long s=d_time/1000%60;//秒
long m=d_time/1000/60%60;//分
if(h<10){
map.put("hour","0"+h);
}else {
map.put("hour",h);
}
if(m<10){
map.put("minute","0"+m);
}else {
map.put("minute",m);
}
if(s<10){
map.put("second","0"+s);
}else {
map.put("second",s);
}
}else {//防止显示负数
map.put("hour","00");
map.put("minute","00");
map.put("second","00");
}
return map;
}
//将时间装换毫秒
public static long getLongtime(String str){
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
long millionSeconds = 0;//毫秒
try {
millionSeconds = sdf.parse(str).getTime();
} catch (ParseException e) {
e.printStackTrace();
}
return millionSeconds;
}
二,3种常用的定时器
1.Handler类的postDelayed方法:
Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
//do something
}
};
Runnable r = new Runnable() {
@Override
public void run() {
mHandler.sendEmptyMessage(msg.what);
//每隔1s循环执行run方法
mHandler.postDelayed(this, 1000);
}
};
主线程中调用: mHandler.post(r);//启动
2.用handler+timer+timeTask方法:
Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
if (msg.what == 1){
//do something
}
super.handleMessage(msg);
}
};
Timer timer = new Timer();
TimerTask timerTask = new TimerTask() {
@Override
public void run() {
Message message = new Message();
message.what = 1;
handler.sendMessage(message);
}
};
主线程中调用:timer.schedule(timerTask,0,1000);//每隔1000毫秒执行一次run方法
3.Thread+handler方法:
Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
if (msg.what == 1){
//do something
}
super.handleMessage(msg);
}
};
class MyThread extends Thread {//这里也可用Runnable接口实现
@Override
public void run() {
while (true){
try {
Thread.sleep(1000);//每隔1s执行一次
Message msg = new Message();
msg.what = 1;
handler.sendMessage(msg);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
主线程中调用:new Thread(new MyThread()).start();
三,计算活动倒计时及配合Timer在RecyclerView中的应用
最初的想法是利用Timer在adapter中开启定时器,每一个item开启一个timer,功能也是初步实现了,但是导致了一下问题:
1.下拉刷新活动倒计时会跳动显示其他时间(原因:每次下拉刷新都会重新创建Timer,导致一个item执行多个timer);
2.容易出现ANR(原因:每一个item创建一个timer,非常消耗内存);
当然有人会这么处理,及时取消Timer不久可以了吗?
private List<Timer> timerList;//用于每开启一个Timer,便添加到list中,timerList.add(timer)
public FreshProductListAdapter(@LayoutRes int layoutResId, @Nullable List<ItemMenu<ResponseSearchFresh.ListfreshBean>> data, Activity context, int width) {
super(layoutResId, data);
this.context = context;
this.width = width;
timerList = new ArrayList<>();
}
/**
* 清空资源
*/
public void cancelAllTimers() {
if (timerList == null) {
return;
}
LogUtils.e("定时器", "size : " + timerList.size());
for (int i = 0, length = timerList.size(); i < length; i++) {
Timer cdt = timerList.get(i);
if (cdt != null) {
cdt.cancel();
cdt = null;
}
}
}
//每滑动便取消不显示item的timer
@Override
public void onViewRecycled(BaseViewHolder holder) {
super.onViewRecycled(holder);
cancelAllTimers();
}
这样处理了问题一跳动的问题和滑动创建多个timer的问题,但也带来了新问题:
1.重新滑动之后发现时间不跳动了(原因把timer都给cancel()了);
2.进入页面之后不滑动,过一段时间页面还是ANR了(原因存在多个item,还是存在多个Timer);
正确处理方法:
在Activity中开启一个线程来处理所有的倒计时:
private Runnable r;//定时器线程
private boolean timerIsInit=false;//判断数据是否初始化
final Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
for (int i=0;i<productData .size();i++){
if(productData.get(i)!=null&&productData.get(i).getData()!=null&&!TextUtil.isEmpty(productData.get(i).getData().getEndTime())){
//获取显示的数据,由个人的数据实体决定
HashMap<Object, Object> map = DateUtil.activitiesCountdownTime(productData.get(i).getData().getEndTime().replace("T", " "));
productData.get(i).getData().setHour(map.get("hour").toString());
productData.get(i).getData().setMinute(map.get("minute").toString());
productData.get(i).getData().setSecond(map.get("second").toString());
BaseViewHolder holder = (BaseViewHolder) productRecycleList.findViewHolderForAdapterPosition(i);//获取当前item的位置,这个是关键
if(holder!=null){//这种方式只刷新了显示时间相关的控件,并不需要去做很复杂的操作来刷新数据,甚至都不需要 notifyDataSetChanged();
timerIsInit=true;
holder .setText(R.id.tv_limit_time_hour, productData.get(i).getData().getHour());
holder .setText(R.id.tv_limit_time_minute, productData.get(i).getData().getMinute());
holder .setText(R.id.tv_limit_time_second, productData.get(i).getData().getSecond());
}
}
}
}
};
在oncreat()方法中:
r = new Runnable() {
@Override
public void run() {
mHandler.sendEmptyMessage(200);
if(timerIsInit){//数据初始化成功,则延迟1秒
mHandler.postDelayed(this, 1000);
}else {
mHandler.post(this);
}
}
};
mHandler.post(r);
//页面销毁时,销毁线程
@Override
protected void onDestroy() {
super.onDestroy();
if(mHandler!=null&&r!=null){
mHandler.removeCallbacks(r);
}
}
完美解决。