Android利用RecyclerView实现列表倒计时效果
最近面试时,面试官问了一个列表倒计时效果如何实现,然后脑袋突然懵的了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实现列表倒计时效果的更多相关文章
- Android利用温度传感器实现带动画效果的电子温度计
概述 Android利用温度传感器实现带动画效果的电子温度计. 详细 代码下载:http://www.demodashi.com/demo/10631.html 一.准备工作 需要准备一部带有温度传感 ...
- 我的Android进阶之旅------>Android利用温度传感器实现带动画效果的电子温度计
要想实现带动画效果的电子温度计,需要以下几个知识点: 1.温度传感器相关知识. 2.ScaleAnimation动画相关知识,来进行水印刻度的缩放效果. 3.android:layout_weight ...
- android开发 RecyclerView 瀑布列表布局
1.写一个内容的自定义小布局: <?xml version="1.0" encoding="utf-8"?> <LinearLayout xm ...
- Android 利用RecyclerView.Adapter刷新列表中的单个view问题
首先使用RecyclerView的adapter继承:RecyclerView.Adapter public class OrderListAdapter extends RecyclerView.A ...
- [Android Pro] RecyclerView实现瀑布流效果(二)
referece to : http://blog.csdn.net/u010687392 在上篇中我们知道RecyclerView中默认给我们提供了三种布局管理器,分别是LinearLayoutMa ...
- Android利用CountDownTimer类实现倒计时功能
public class MainActivity extends Activity { private MyCount mc; private TextView tv; @Override publ ...
- 利用kvo实现列表倒计时
自己稍微记录一下,方便以后用到: 先创建一个定时器的类: #import "TimeCenter.h" @interface TimeCenter () @property (no ...
- android 开发 RecyclerView 横排列列表布局
1.写一个一竖的自定义布局: <?xml version="1.0" encoding="utf-8"?> <LinearLayout xml ...
- android中RecyclerView控件的列表项横向排列
本文是在上一篇文章的基础上做的修改:android中RecyclerView控件的使用 1.修改列表项news_item.xml:我这里是把新闻标题挪到了新闻图片的下面显示 <?xml vers ...
随机推荐
- spring+springmvc+mybatis构建系统
今天和大家分享的是spring+springmvc+mybatis搭建框架的例子,说到这里不得不说现在市面上一流大公司还有很多用这种架子,创业型公司大部分都用springboot集成的mvc+myba ...
- vi/vim操作
vi/vim是unix/linux操作系统下的文本编辑器. 由于unix/linux万物届文件的特性,vi/vim可以编辑任何格式的文件. 下面是常见的知识点,仅供参考: 编辑方式:vi/vim + ...
- C# Linq GroupBy 分组过滤求和
var delOrderData = orderLogList.Where(x => (x.OlStatus == 0 && x.OlUpId == null)).GroupBy ...
- spring.resources
@EnableWebMvcNormally you would add @EnableWebMvc for a Spring MVC app, but Spring Boot adds it auto ...
- 居中 html css
<div id="main" style="width:800px;height: 600px;margin:0 auto"> <!-- 这里 ...
- IP地址和MAC地址的关系
IP地址是网络层的概念,而MAC地址是数据链路层的概念.IP地址在网络层上对不同的硬件地址类型进行了统一,从而提供网络互联的可能:而硬件地址在真正的数据传输中要用到.当应用程序把数据从源主机发送到目标 ...
- ZooKeeper 安装、配置
http://blog.csdn.net/franklysun/article/details/6424582 如题本文介绍的是ZooKeeper 的安装和配置过程,此过程非常简单,关键是如何应用(将 ...
- 16.app后端如何保证通讯安全--url签名
app和后端的通讯过程中,api请求有可能被别人截取或不小心泄露.那么,怎么保证api请求的安全呢?在这篇文章中,介绍一种常见的保证api请求安全的做法--url签名. 1. url签名详解 在前一篇 ...
- 阿里巴巴的开源项目Druid(关于数据库连接)
1 配置 和dbcp类似,druid的常用配置项如下 配置 缺省值 说明 name 配置这个属性的意义在于,如果存在多个数据源,监控的时候可以通过名字来区分开来.如果没有配置,将会生成一个名字,格 ...
- 删除外部dwg中指定的块定义
本例实现删除外部图纸中指定的块定义,在外部图纸当前模型空间中是没有该块定义的块参照存在. 代码如下: void CBlockUtil::DeleteBlockDefFormOtherDwg(const ...