前段时间项目有个需求是需要在listview的item中做个60s倒计时功能,并且倒计时的时间由本地记录,无关服务端。网上找了一些demo,有倒计时的功能,但总有些问题,也无法满足需求,最后自己改进后满足了需求,然后就想着记录下开发过程中遇到的一些问题以及最后的成品。

项目需求背景:app是关于视频会议,视频会议列表由listview来展示。本次需求是需要在会议列表的item中加一个再次通知的功能,点击后有个60s倒计时,等倒计时结束才可以再次点击。

试了几种后,感觉使用CountDownTimer来实现是比较好的。

demo:

import android.content.Context;
import android.os.Bundle;
import android.os.CountDownTimer;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.util.SparseArray;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ListView;
import android.widget.TextView;import com.sfyc.countdownlist.R;
import com.sfyc.countdownlist.entity.TimerItem;import java.util.ArrayList;
import java.util.List;import butterknife.BindView;
import butterknife.ButterKnife;import static com.sfyc.countdownlist.R.id.toolbar;public class CountDownListActivity extends AppCompatActivity {private Context mContext;@BindView(toolbar)Toolbar mToolbar;@BindView(R.id.list_view)ListView mListView;MyAdapter mAdapter;private ArrayList<TimerItem> lstTimerItems;@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_list_view);ButterKnife.bind(this);mContext = this;mToolbar.setTitle(R.string.title_list_view_countdown);initDadas();mAdapter = new MyAdapter(mContext, lstTimerItems);mListView.setAdapter(mAdapter);}private void initDadas() {lstTimerItems = new ArrayList<>();lstTimerItems.add(new TimerItem("A", 0));lstTimerItems.add(new TimerItem("B", 0));lstTimerItems.add(new TimerItem("C", 0));lstTimerItems.add(new TimerItem("D", 0));lstTimerItems.add(new TimerItem("E", 0));lstTimerItems.add(new TimerItem("F", 0));lstTimerItems.add(new TimerItem("G", 0));lstTimerItems.add(new TimerItem("H", 0));lstTimerItems.add(new TimerItem("I", 0));lstTimerItems.add(new TimerItem("J", 0));lstTimerItems.add(new TimerItem("K", 0));lstTimerItems.add(new TimerItem("L", 0));lstTimerItems.add(new TimerItem("M", 0));lstTimerItems.add(new TimerItem("N", 0));lstTimerItems.add(new TimerItem("O", 0));}public static class MyAdapter extends BaseAdapter {private List<TimerItem> mDatas;private Context mContext;//用于退出activity,避免countdown,造成资源浪费。private SparseArray<CountDownTimer> countDownCounters;public MyAdapter(Context mContext, List<TimerItem> mDatas) {this.mContext = mContext;this.mDatas = mDatas;this.countDownCounters = new SparseArray<>();}/*** 清空资源*/public void cancelAllTimers() {if (countDownCounters == null) {return;}Log.e("TAG", "size :  " + countDownCounters.size());for (int i = 0, length = countDownCounters.size(); i < length; i++) {CountDownTimer cdt = countDownCounters.get(countDownCounters.keyAt(i));if (cdt != null) {cdt.cancel();}}}@Overridepublic int getCount() {if (mDatas != null && !mDatas.isEmpty()) {return mDatas.size();}return 0;}@Overridepublic Object getItem(int position) {if (mDatas != null && !mDatas.isEmpty()) {return mDatas.get(position);}return null;}@Overridepublic long getItemId(int position) {return position;}@Overridepublic View getView(int position, View convertView, ViewGroup parent) {final ViewHolder viewHolder;if (convertView == null) {convertView = LayoutInflater.from(mContext).inflate(R.layout.list_item_common2, parent, false);viewHolder = new ViewHolder();viewHolder.btn = (TextView) convertView.findViewById(R.id.btn);convertView.setTag(viewHolder);} else {viewHolder = (ViewHolder) convertView.getTag();}final TimerItem data = mDatas.get(position);viewHolder.btn.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {data.setExpirationTime(60*1000);notifyDataSetChanged();}});CountDownTimer countDownTimer = countDownCounters.get(viewHolder.btn.hashCode());//将前一个缓存清除if (countDownTimer != null) {countDownTimer.cancel();}long timer = data.getExpirationTime();
//            timer = timer - System.currentTimeMillis();if (timer > 0) {countDownTimer = new CountDownTimer(timer, 1000) {public void onTick(long millisUntilFinished) {viewHolder.btn.setText(millisUntilFinished/1000 +"");Log.e("TAG", data.name + " :  " + millisUntilFinished);}public void onFinish() {viewHolder.btn.setText("开始倒计时");}}.start();countDownCounters.put(viewHolder.btn.hashCode(), countDownTimer);} else {viewHolder.btn.setText("开始倒计时");}return convertView;}public class ViewHolder {public TextView btn;}}}

效果:

但是这里有几个问题:
1.当item不可见时,CountDownTimer会停止运行,直至item可见时继续运行,这样倒计时的时间就会不准确。比如在55s的时候item不可见,过了20s后item可见了,你会发现倒计时的时间还在55s。
2.刷新问题,由于本项目做的是会议列表,因此有每隔5s自动刷新会议列表,并且用户也可以手动下拉刷新列表。这样不管之前的倒计时状态是怎样,刷新后都会变为未倒计时状态。

针对上述问题的解决方案:
1.因为之前按钮点击后我是给了个60s的时间,然后利用这个时间去做递减才会出现的这个情况。后来改成点击的时候赋予当前系统+60s的时间。然后去判断这个时间与当前系统时间的差值,如果差值大于0的话就执行倒计时。这样不管item不可见多久,时间的差值都不会有问题。
2.刷新问题一直都没有很好的解决,所以后面我用了一个笨办法,就是另外建立一个数组,每次点击按钮赋值的时候,在新建的数组中保存该对象。然后在每次刷新数据的时候,将数据与保存数据的数组一一对比,如果数据一致就将时间赋值过去。因为会议系统中会议id是唯一的,因此我这里是根据id去判断的。

改进后代码:

import android.content.Context;
import android.os.Bundle;
import android.os.CountDownTimer;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.util.SparseArray;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ListView;
import android.widget.TextView;import com.sfyc.countdownlist.R;
import com.sfyc.countdownlist.entity.TimerItem;import java.util.ArrayList;
import java.util.List;import butterknife.BindView;
import butterknife.ButterKnife;import static com.sfyc.countdownlist.R.id.toolbar;public class CountDownListActivity extends AppCompatActivity {private Context mContext;@BindView(toolbar)Toolbar mToolbar;@BindView(R.id.list_view)ListView mListView;MyAdapter mAdapter;private ArrayList<TimerItem> lstTimerItems;@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_list_view);ButterKnife.bind(this);mContext = this;mToolbar.setTitle(R.string.title_list_view_countdown);initDadas();mAdapter = new MyAdapter(mContext, lstTimerItems);mListView.setAdapter(mAdapter);}private void initDadas() {lstTimerItems = new ArrayList<>();lstTimerItems.add(new TimerItem("A", 0));lstTimerItems.add(new TimerItem("B", 0));lstTimerItems.add(new TimerItem("C", 0));lstTimerItems.add(new TimerItem("D", 0));lstTimerItems.add(new TimerItem("E", 0));lstTimerItems.add(new TimerItem("F", 0));lstTimerItems.add(new TimerItem("G", 0));lstTimerItems.add(new TimerItem("H", 0));lstTimerItems.add(new TimerItem("I", 0));lstTimerItems.add(new TimerItem("J", 0));lstTimerItems.add(new TimerItem("K", 0));lstTimerItems.add(new TimerItem("L", 0));lstTimerItems.add(new TimerItem("M", 0));lstTimerItems.add(new TimerItem("N", 0));lstTimerItems.add(new TimerItem("O", 0));}public static class MyAdapter extends BaseAdapter {private ArrayList<TimerItem> timerItemsSave = new ArrayList<>();private List<TimerItem> mDatas;private Context mContext;//用于退出activity,避免countdown,造成资源浪费。private SparseArray<CountDownTimer> countDownCounters;public MyAdapter(Context mContext, List<TimerItem> mDatas) {this.mContext = mContext;this.mDatas = mDatas;this.countDownCounters = new SparseArray<>();}/*** 清空资源*/public void cancelAllTimers() {if (countDownCounters == null) {return;}Log.e("TAG", "size :  " + countDownCounters.size());for (int i = 0, length = countDownCounters.size(); i < length; i++) {CountDownTimer cdt = countDownCounters.get(countDownCounters.keyAt(i));if (cdt != null) {cdt.cancel();}}}@Overridepublic int getCount() {if (mDatas != null && !mDatas.isEmpty()) {return mDatas.size();}return 0;}@Overridepublic Object getItem(int position) {if (mDatas != null && !mDatas.isEmpty()) {return mDatas.get(position);}return null;}@Overridepublic long getItemId(int position) {return position;}@Overridepublic View getView(int position, View convertView, ViewGroup parent) {final ViewHolder viewHolder;if (convertView == null) {convertView = LayoutInflater.from(mContext).inflate(R.layout.list_item_common2, parent, false);viewHolder = new ViewHolder();viewHolder.btn = (TextView) convertView.findViewById(R.id.btn);convertView.setTag(viewHolder);} else {viewHolder = (ViewHolder) convertView.getTag();}final TimerItem data = mDatas.get(position);viewHolder.btn.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {data.setExpirationTime(System.currentTimeMillis() + 60 * 1000);notifyDataSetChanged();timerItemsSave.add(data);}});//将当前item与保存起来的会议实体一一对比,如果会议id相同,则认为是同一个会议,那么去判断//如果按钮还需要倒计时,那么将时间赋值过去,如果不需要就从保存的列表中移除for (int i = 0; i < timerItemsSave.size(); i++) {if (timerItemsSave.get(i).getName().equals(data.getName())) {if (timerItemsSave.get(i).getExpirationTime() - System.currentTimeMillis() > 0) {//如果时间差大于0,按钮还需要倒计时,赋值data.setExpirationTime(timerItemsSave.get(i).getExpirationTime());} else {//如果时间差小于等于0,不需要倒计时,将保存的对象移除(避免一次次的保存对象导致校验耗费时间)timerItemsSave.remove(timerItemsSave.get(i));}}}CountDownTimer countDownTimer = countDownCounters.get(viewHolder.btn.hashCode());//将前一个缓存清除if (countDownTimer != null) {countDownTimer.cancel();}long timer = data.getExpirationTime();
//            timer = timer - System.currentTimeMillis();if (timer > 0) {countDownTimer = new CountDownTimer(timer, 1000) {public void onTick(long millisUntilFinished) {viewHolder.btn.setText(millisUntilFinished/1000 +"");Log.e("TAG", data.name + " :  " + millisUntilFinished);}public void onFinish() {viewHolder.btn.setText("开始倒计时");}}.start();countDownCounters.put(viewHolder.btn.hashCode(), countDownTimer);} else {viewHolder.btn.setText("开始倒计时");}return convertView;}public class ViewHolder {public TextView btn;}}}

由于本身项目东西太多,因此就不贴出来而是写了demo。思路就是:点击按钮赋值同时保存对象,刷新数据的时候与保存的数据对比,需要倒计时的话就赋值。时间必须要用系统时间,可以避免倒计时数字错乱的问题。

Android listview item倒计时功能相关推荐

  1. android倒计时功能,Android 实现列表倒计时功能

    Android 实现列表倒计时功能 发布时间:2020-08-21 21:47:11 来源:脚本之家 阅读:147 作者:Choi晨 单个计时器,然后遍历数据 刷新条目: 两种实现方式:1.Handl ...

  2. Android listView Item 高度设置

    Android listView Item 高度设置 问题: 设置listview item的高度比实际内容要高,怎么做: 如果需要将listView的item设置得比包裹内容还要高,通过设置item ...

  3. Android—ListView Item 展开动画效果

    Android-ListView Item 展开动画效果 最近在做一个关于ListView item的展开效果,类似于 "粮仓" App 的商店页面,点击Item,展开显示子Vie ...

  4. ListView实现倒计时功能

    这几天做软件有用到这个效果,在网上找了好几个Demo,下载下来后发现都有一些小瑕玆,自己花了半天时间,想了好几种方法,最后发现这种方法实现起来的效果是最简单和最好的,倒计时功能可以实现,而且ListV ...

  5. Android ListView item信息显示不全

    Bug:本人使用Android listview去显示排名分数,item的条数是6~8个,每添加一条item就排序一次并调用 notifyDataSetChanged()去刷新ListView,最后发 ...

  6. Android ListView Item与子控件焦点问题

    经常会碰到ListView Item和子控件焦点冲突问题,查了很多资料后发现问题出在listview item的父容器布局中 android:descendantFocusability属性和子控件 ...

  7. Android ListView item设置分割线以及分割线宽度

    ListView item设置分割线的方法:setDivider ListView item设置分割线宽度的方法:setDividerHeight // 设置分割线 listView.setDivid ...

  8. android listview item 圆角,ListView圆角 实现方法(一)

    (本方法核心思想是 在适配器里面为 每一个item 添加 selector) 适配器里面代码: package com.blogtask.adapter; import com.blogtask.ac ...

  9. android listview item 错位,Android BaseAdapter和ViewHolder 优化 解决ListView的item抢焦点问题和item错乱问题...

    首先赞下hyman大神 曾经仅仅是简单的重写个BaseAdapter,将getView方法保持抽象.而ViewHolder没有抽象过. .. ViewHolder (用了一个集合+泛型管理存取view ...

最新文章

  1. java aes mysql blob_使用带有ORDER子句的AES_DECRYPT在MySQL中返回BLOB数据
  2. MySQL中的DATE_SUB()函数和DATE_ADD()函数
  3. linxu /proc/stat 文件
  4. 自动为数字千位数,百万位数添加逗号
  5. 第二次 HDOJ 1003
  6. FPM一:简单的road map(GAF)
  7. 微信小程序 vs 支付宝小程序(钉钉小程序) 区别 api大全
  8. 2020中青杯本科题目学习
  9. 【C++ Builder 11】选择文件夹的三种方式
  10. win7设置html,Windows 7自动备份设置图解 设置Win7系统备份方式
  11. java是怎么分配内存和释放内存的-详解
  12. 初识Typora 符号
  13. HTML制作手风琴效果,纯js+html和纯css+html制作手风琴效果,css手风琴
  14. 【编程DIY】一.几个有趣的小程序
  15. java使用axis调用.net发布的webservice接口返回对象类型
  16. 蛋白质二级结构预测Linux,蛋白质稳定性预测工具-Rosetta ddg_monomer
  17. 缓存为王:老码农眼中的分布式缓存
  18. ARM基础教程 | ARM、Cortex-M与ARMv8-M什么关系?
  19. 三星Android手机进入工程模式
  20. Python基础练习day01

热门文章

  1. matlab中的round、ceil、floor、fix函数
  2. 计算机ict基础知识,ICT基本知识介绍.doc
  3. [ vulhub漏洞复现篇 ] Jetty WEB-INF 文件读取复现CVE-2021-34429
  4. EtherCAT通讯DS402协议----控制模式
  5. addr2line 批处理 处理
  6. 模拟量输入模块|CC-PAIN01|霍尼韦尔
  7. 宕机事件为何频发?云原生时代,需要什么样的运维保障体系?
  8. java迪杰斯特拉算法实例,Java 图的最短路径dijstra(迪杰斯特拉)算法和拓扑排序
  9. excel制图时y轴数据全为0
  10. 【毕业设计】基于微信小程序的在线学习平台 在线学习微信小程序