Android listview item倒计时功能
前段时间项目有个需求是需要在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倒计时功能相关推荐
- android倒计时功能,Android 实现列表倒计时功能
Android 实现列表倒计时功能 发布时间:2020-08-21 21:47:11 来源:脚本之家 阅读:147 作者:Choi晨 单个计时器,然后遍历数据 刷新条目: 两种实现方式:1.Handl ...
- Android listView Item 高度设置
Android listView Item 高度设置 问题: 设置listview item的高度比实际内容要高,怎么做: 如果需要将listView的item设置得比包裹内容还要高,通过设置item ...
- Android—ListView Item 展开动画效果
Android-ListView Item 展开动画效果 最近在做一个关于ListView item的展开效果,类似于 "粮仓" App 的商店页面,点击Item,展开显示子Vie ...
- ListView实现倒计时功能
这几天做软件有用到这个效果,在网上找了好几个Demo,下载下来后发现都有一些小瑕玆,自己花了半天时间,想了好几种方法,最后发现这种方法实现起来的效果是最简单和最好的,倒计时功能可以实现,而且ListV ...
- Android ListView item信息显示不全
Bug:本人使用Android listview去显示排名分数,item的条数是6~8个,每添加一条item就排序一次并调用 notifyDataSetChanged()去刷新ListView,最后发 ...
- Android ListView Item与子控件焦点问题
经常会碰到ListView Item和子控件焦点冲突问题,查了很多资料后发现问题出在listview item的父容器布局中 android:descendantFocusability属性和子控件 ...
- Android ListView item设置分割线以及分割线宽度
ListView item设置分割线的方法:setDivider ListView item设置分割线宽度的方法:setDividerHeight // 设置分割线 listView.setDivid ...
- android listview item 圆角,ListView圆角 实现方法(一)
(本方法核心思想是 在适配器里面为 每一个item 添加 selector) 适配器里面代码: package com.blogtask.adapter; import com.blogtask.ac ...
- android listview item 错位,Android BaseAdapter和ViewHolder 优化 解决ListView的item抢焦点问题和item错乱问题...
首先赞下hyman大神 曾经仅仅是简单的重写个BaseAdapter,将getView方法保持抽象.而ViewHolder没有抽象过. .. ViewHolder (用了一个集合+泛型管理存取view ...
最新文章
- java aes mysql blob_使用带有ORDER子句的AES_DECRYPT在MySQL中返回BLOB数据
- MySQL中的DATE_SUB()函数和DATE_ADD()函数
- linxu /proc/stat 文件
- 自动为数字千位数,百万位数添加逗号
- 第二次 HDOJ 1003
- FPM一:简单的road map(GAF)
- 微信小程序 vs 支付宝小程序(钉钉小程序) 区别 api大全
- 2020中青杯本科题目学习
- 【C++ Builder 11】选择文件夹的三种方式
- win7设置html,Windows 7自动备份设置图解 设置Win7系统备份方式
- java是怎么分配内存和释放内存的-详解
- 初识Typora 符号
- HTML制作手风琴效果,纯js+html和纯css+html制作手风琴效果,css手风琴
- 【编程DIY】一.几个有趣的小程序
- java使用axis调用.net发布的webservice接口返回对象类型
- 蛋白质二级结构预测Linux,蛋白质稳定性预测工具-Rosetta ddg_monomer
- 缓存为王:老码农眼中的分布式缓存
- ARM基础教程 | ARM、Cortex-M与ARMv8-M什么关系?
- 三星Android手机进入工程模式
- Python基础练习day01
热门文章
- matlab中的round、ceil、floor、fix函数
- 计算机ict基础知识,ICT基本知识介绍.doc
- [ vulhub漏洞复现篇 ] Jetty WEB-INF 文件读取复现CVE-2021-34429
- EtherCAT通讯DS402协议----控制模式
- addr2line 批处理 处理
- 模拟量输入模块|CC-PAIN01|霍尼韦尔
- 宕机事件为何频发?云原生时代,需要什么样的运维保障体系?
- java迪杰斯特拉算法实例,Java 图的最短路径dijstra(迪杰斯特拉)算法和拓扑排序
- excel制图时y轴数据全为0
- 【毕业设计】基于微信小程序的在线学习平台 在线学习微信小程序