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 ...
随机推荐
- vh、vw、vmin、vmax 知多少
介绍一些 CSS3 新增的单位,平时可能用的比较少,但是由于单位的特性,在一些特殊场合会有妙用. vw and vh 1vw 等于1/100的视口宽度 (Viewport Width) 1vh 等于1 ...
- 使用 GNU profiler 来提高代码运行速度
各种软件对于性能的需求可能会有很大的区别,但是很多应用程序都有非常严格的性能需求,这一点并不奇怪.电影播放器就是一个很好的例子:如果一个电影播放器只能以所需要速度的 75% 来播放电影,那么它几乎就没 ...
- Struts2中的值栈
一 什么是值栈 值栈: struts2中提供的一种类似于域对象的工具, 用于struts2中的存值和取值. 每次访问Action的时候, 都会创建一个action对象, 而每个action对象中都存在 ...
- Tiny4412之按键驱动
一:按键驱动 按键驱动跟之前的LED,蜂鸣器的方法类似:通过底板,核心板我们可以看到按键的电路图: 通过电路图我们可以看出,当按键按下去为低电平,松开为高电平:所以我们要检测XEINT26的状态,通过 ...
- SpringCloud实战-Zuul网关服务
为什么需要网关呢? 我们知道我们要进入一个服务本身,很明显我们没有特别好的办法,直接输入IP地址+端口号,我们知道这样的做法很糟糕的,这样的做法大有问题,首先暴露了我们实体机器的IP地址,别人一看你的 ...
- Python Redis 的安装
安装 可以去pypi上找到redis的Python模块: http://pypi.python.org/pypi?%3Aaction=search&term=redis&submit= ...
- HTML5 CSS3专题 诱人的实例 CSS3打造百度贴吧的3D翻牌效果
首先感谢w3cfuns的老师~ 今天给大家带来一个CSS3制作的翻牌效果,就是鼠标移到元素上,感觉可以看到元素背后的信息.大家如果制作考验记忆力的连连看.扑克类的游戏神马的,甚至给女朋友写一些话语,放 ...
- Postman用法简介
转自:http://blog.csdn.net/flowerspring/article/details/52774399 Postman用法简介 转载 2016年10月10日 09:04:10 10 ...
- teeporxy.go
package,, { cer, err := tls.LoadX509KeyPair(*tlsCertificate, *tlsPrivateKey) if err ...
- mybatis批量提交
之前在做项目时,使用mybatis,批量执行sql,这里简单写下步骤 在配置数据库连接时,加入一个参数,例如 jdbc:mysql://127.0.0.1:3307/mvs-report?allowM ...