最近面试时,面试官问了一个列表倒计时效果如何实现,然后脑袋突然懵的了O(∩_∩)O,现在记录一下。

运行效果图

实现思路

实现方法主要有两个:

1.为每个开始倒计时的item启动一个定时器,再做更新item处理;

2.只启动一个定时器,然后遍历数据,再做再做更新item处理。

经过思考,包括性能、实现等方面,决定使用第2种方式实现。

实现过程

  • 数据实体
/**
* 总共的倒计时的时间(结束时间-开始时间),单位:毫秒
* 例: 2019-02-23 11:00:30 与 2019-02-23 11:00:00 之间的相差的毫秒数
*/
private long totalTime;
/**
* 倒计时是否在暂停状态
*/
private boolean isPause = true;
  • 倒计时

    Timer
mTimer.schedule(mTask, 0, 1000);

TimerTask

 class MyTask extends TimerTask {
@Override
public void run() {
if (mList.isEmpty()) {
return;
}
int size = mList.size();
CountDownTimerBean bean;
long totalTime;
for (int i = 0; i < size; i++) {
bean = mList.get(i);
if (!bean.isPause()) {//不处于暂停状态
totalTime = bean.getTotalTime() - 1000;
if (totalTime <= 0) {
bean.setPause(true);
bean.setTotalTime(0);
}
bean.setTotalTime(totalTime);
Message message = mHandler.obtainMessage(1);
message.arg1 = i;
mHandler.sendMessage(message);
}
}
}
}
  • 线程交互更新item
 mHandler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 1:
notifyItemChanged(msg.arg1, "update-time");
break;
}
}
};
  • 性能优化方面

    1.调用notifyItemChanged()方法后,不要更新整个item(比如说item包含图片,不需要变的),所以要重写onBindViewHolder( Holder , int , List )方法:
@Override
public void onBindViewHolder(@NonNull Holder holder, int position, @NonNull List<Object> payloads) {
if (payloads.isEmpty()) {
onBindViewHolder(holder, position);
return;
}
//更新某个控件,比如说只需要更新时间信息,其他不用动
CountDownTimerBean bean = mList.get(position);
long day = bean.getTotalTime() / (1000 * 60 * 60 * 24);
long hour = (bean.getTotalTime() / (1000 * 60 * 60) - day * 24);
long min = ((bean.getTotalTime() / (60 * 1000)) - day * 24 * 60 - hour * 60);
long s = (bean.getTotalTime() / 1000 - day * 24 * 60 * 60 - hour * 60 * 60 - min * 60);
holder.tvTime.setText("剩余时间: " + day + "天" + hour + "小时" + min + "分" + s + "秒");
holder.btnAction.setText(bean.isPause() ? "开始" : "暂停");
holder.btnAction.setEnabled(bean.getTotalTime() != 0);
}

2.销毁资源操作:

   /**
* 销毁资源
*/
public void destroy() {
mHandler.removeMessages(1);
if (mTimer != null) {
mTimer.cancel();
mTimer.purge();
mTimer = null;
}
}
  • RecyclerView.Adapter部分源码
public class CountDownTimerAdapter extends RecyclerView.Adapter<CountDownTimerAdapter.Holder> {
private static final String TAG = "CountDownTimerAdapter->";
private List<CountDownTimerBean> mList;//数据
private Handler mHandler;//线程调度,用来更新列表 private Timer mTimer;
private MyTask mTask; public CountDownTimerAdapter() {
mList = new ArrayList<>();
mHandler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 1:
notifyItemChanged(msg.arg1, "update-time");
break;
}
}
};
mTask = new MyTask();
} public void bindAdapterToRecyclerView(@NonNull RecyclerView view) {
view.setAdapter(this);
} /**
* 设置新的数据源
*
* @param list 数据
*/
public void setNewData(@NonNull List<CountDownTimerBean> list) {
destroy();
mList.clear();
mList.addAll(list);
notifyDataSetChanged();
if (mTimer == null) {
mTimer = new Timer();
}
mTimer.schedule(mTask, 0, 1000);
} /**
* 销毁资源
*/
public void destroy() {
mHandler.removeMessages(1);
if (mTimer != null) {
mTimer.cancel();
mTimer.purge();
mTimer = null;
}
} @NonNull
@Override
public Holder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.item_count_down_timer, viewGroup, false);
return new Holder(view);
} @Override
public void onBindViewHolder(@NonNull Holder holder, int position, @NonNull List<Object> payloads) {
if (payloads.isEmpty()) {
onBindViewHolder(holder, position);
return;
}
//更新某个控件,比如说只需要更新时间信息,其他不用动
CountDownTimerBean bean = mList.get(position);
long day = bean.getTotalTime() / (1000 * 60 * 60 * 24);
long hour = (bean.getTotalTime() / (1000 * 60 * 60) - day * 24);
long min = ((bean.getTotalTime() / (60 * 1000)) - day * 24 * 60 - hour * 60);
long s = (bean.getTotalTime() / 1000 - day * 24 * 60 * 60 - hour * 60 * 60 - min * 60);
holder.tvTime.setText("剩余时间: " + day + "天" + hour + "小时" + min + "分" + s + "秒");
holder.btnAction.setText(bean.isPause() ? "开始" : "暂停");
holder.btnAction.setEnabled(bean.getTotalTime() != 0);
} @Override
public void onBindViewHolder(@NonNull final Holder holder, int position) {
holder.ivIcon.setImageResource(R.mipmap.ic_launcher_round);
final CountDownTimerBean bean = mList.get(position);
long day = bean.getTotalTime() / (1000 * 60 * 60 * 24);
long hour = (bean.getTotalTime() / (1000 * 60 * 60) - day * 24);
long min = ((bean.getTotalTime() / (60 * 1000)) - day * 24 * 60 - hour * 60);
long s = (bean.getTotalTime() / 1000 - day * 24 * 60 * 60 - hour * 60 * 60 - min * 60);
holder.tvTime.setText("剩余时间: " + day + "天" + hour + "小时" + min + "分" + s + "秒");
holder.btnAction.setText(bean.isPause() ? "开始" : "暂停");
holder.btnAction.setEnabled(bean.getTotalTime() != 0);
holder.btnAction.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (bean.isPause()) {
bean.setPause(false);
holder.btnAction.setText("暂停");
} else {
bean.setPause(true);
holder.btnAction.setText("开始");
}
}
});
} @Override
public int getItemCount() {
return mList.size();
} class Holder extends RecyclerView.ViewHolder {
private ImageView ivIcon;
private TextView tvTime;
private Button btnAction; Holder(@NonNull View itemView) {
super(itemView);
ivIcon = itemView.findViewById(R.id.iv_icon);
tvTime = itemView.findViewById(R.id.tv_time);
btnAction = itemView.findViewById(R.id.btn_action);
}
} class MyTask extends TimerTask {
@Override
public void run() {
if (mList.isEmpty()) {
return;
}
int size = mList.size();
CountDownTimerBean bean;
long totalTime;
for (int i = 0; i < size; i++) {
bean = mList.get(i);
if (!bean.isPause()) {//不处于暂停状态
totalTime = bean.getTotalTime() - 1000;
if (totalTime <= 0) {
bean.setPause(true);
bean.setTotalTime(0);
}
bean.setTotalTime(totalTime);
Message message = mHandler.obtainMessage(1);
message.arg1 = i;
mHandler.sendMessage(message);
}
}
}
}
}

项目地址

源码


如有问题,欢迎及时沟通。

Android利用RecyclerView实现列表倒计时效果的更多相关文章

  1. Android利用温度传感器实现带动画效果的电子温度计

    概述 Android利用温度传感器实现带动画效果的电子温度计. 详细 代码下载:http://www.demodashi.com/demo/10631.html 一.准备工作 需要准备一部带有温度传感 ...

  2. 我的Android进阶之旅------>Android利用温度传感器实现带动画效果的电子温度计

    要想实现带动画效果的电子温度计,需要以下几个知识点: 1.温度传感器相关知识. 2.ScaleAnimation动画相关知识,来进行水印刻度的缩放效果. 3.android:layout_weight ...

  3. android开发 RecyclerView 瀑布列表布局

    1.写一个内容的自定义小布局: <?xml version="1.0" encoding="utf-8"?> <LinearLayout xm ...

  4. Android 利用RecyclerView.Adapter刷新列表中的单个view问题

    首先使用RecyclerView的adapter继承:RecyclerView.Adapter public class OrderListAdapter extends RecyclerView.A ...

  5. [Android Pro] RecyclerView实现瀑布流效果(二)

    referece to : http://blog.csdn.net/u010687392 在上篇中我们知道RecyclerView中默认给我们提供了三种布局管理器,分别是LinearLayoutMa ...

  6. Android利用CountDownTimer类实现倒计时功能

    public class MainActivity extends Activity { private MyCount mc; private TextView tv; @Override publ ...

  7. 利用kvo实现列表倒计时

    自己稍微记录一下,方便以后用到: 先创建一个定时器的类: #import "TimeCenter.h" @interface TimeCenter () @property (no ...

  8. android 开发 RecyclerView 横排列列表布局

    1.写一个一竖的自定义布局: <?xml version="1.0" encoding="utf-8"?> <LinearLayout xml ...

  9. android中RecyclerView控件的列表项横向排列

    本文是在上一篇文章的基础上做的修改:android中RecyclerView控件的使用 1.修改列表项news_item.xml:我这里是把新闻标题挪到了新闻图片的下面显示 <?xml vers ...

随机推荐

  1. 【转】微信小游戏接入Fundebug监控

    在SegmentFault上看到Fundebug上线小游戏监控,刚好最近开始玩微信小游戏,于是尝试接入试了一下. 接入方法 创建项目的时候选择左下角的微信小游戏图标. 点击继续进入接入插件页面. 第三 ...

  2. JAVA中写时复制(Copy-On-Write)Map实现

    1,什么是写时复制(Copy-On-Write)容器? 写时复制是指:在并发访问的情景下,当需要修改JAVA中Containers的元素时,不直接修改该容器,而是先复制一份副本,在副本上进行修改.修改 ...

  3. 分布式架构真正适用于大型互联网项目的架构! dubbo+zookeeper+springmvc+mybatis+shiro+redis

     分类: 分布式技术(3)    目录(?)[+]   平台简介 Jeesz是一个分布式的框架,提供项目模块化.服务化.热插拔的思想,高度封装安全性的Java EE快速开发平台. Jeesz本身集成D ...

  4. FutureTask理解

    一.概述 FutureTask包装器是一种非常便利的机制,同时实现了Future和Runnable接口. 类图如下: FutureTask是一种可以取消的异步的计算任务.它的计算是通过Callable ...

  5. capwap学习笔记——初识capwap(四)(转)

    2.5.7 CAPWAP传输机制 WTP和AC之间使用标准的UDP客户端/服务器模式来建立通讯. CAPWAP协议支持UDP和UDP-Lite [RFC3828]. ¢ 在IPv4上,CAPWAP控制 ...

  6. SSM-SpringMVC-21:SpringMVC中处理器方法之返回值Object篇

    ------------吾亦无他,唯手熟尔,谦卑若愚,好学若饥------------- 今天要记录的是处理方法,返回值为Object的那种,我给它分了一下类: 1.返回值为Object数值(例如1) ...

  7. linux ulimit 调优

    概要:linux系统默认open files数目为1024, 有时应用程序会报Too many open files的错误,是因为open files 数目不够.这就需要修改ulimit和file-m ...

  8. React Native系列(6) - 编译安卓私有React-Native代码

    为何要自己编译React Native安卓私有代码 我们在开发中遇到一个HTTP2的问题,React Native安卓客户端在和HTTP2支持的服务器通讯的过程中会有crash,见 React-Nat ...

  9. SpringCloud微框架系列整体模块梳理

    以下为Spring Cloud的核心功能: 分布式/版本化配置服务注册和发现路由服务和服务之间的调用负载均衡断路器分布式消息传递 通过这张图,我们来了解一下各组件配置使用运行流程: 1.请求统一通过A ...

  10. vritualenv虚拟环境迁移

    vritualenv虚拟环境迁移的简单步骤: 1.进入原虚拟环境env1,然后执行pip freeze > requirements.txt将包依赖信息保存在requirements.txt文件 ...