Handler实现倒计时功能
作者:木子@喵 发布时间:2021-06-01 15:24:31
本文实例为大家分享了Handler实现倒计时功能的具体代码,供大家参考,具体内容如下
1、需求
1.1 实现目标
当后台传递一个时间戳时,与当前系统时间做时间差,并转换为时分秒,作为商品活动的倒计时;
如下图所示:
1.2 实现步骤
自定义View
1、实现倒计时功能,封装成方法;
2、初始化倒计时功能,及布局文件;
3、通过Handler中的post()或sendMessage()方法向主线程传递消息,不对刷新UI;
4、对外暴露一个方法,接收后台传入的时间戳;
在Activity中实现
通过自定义View中的方法,接收时间戳;
2、封装成自定义view
2.1 倒计时功能
方法名 processCountMsg()
private boolean processCountMsg() {
if (hou == 0 && min == 0 && sec == 0) {
Toast.makeText(getContext(), "时间到", Toast.LENGTH_SHORT).show();
return false;
}
if (sec > 0) {
sec--;
} else {
sec = 59;
if (min == 0) {
min = 59;
hou--;
} else {
min--;
}
}
String hour, minute, second;
hour = (hou < 10) ? "0" + hou : "" + hou;
minute = (min < 10) ? "0" + min : "" + min;
second = (sec < 10) ? "0" + sec : "" + sec;
tv_hour.setText(hour);
tv_min.setText(minute);
tv_sec.setText(second);
return true;
}
2.2 初始化倒计时功能及布局文件
初始化代码 init()
private void init() {
//TODO LayoutInflater中inflate三个参数代表含义
LayoutInflater.from(getContext()).inflate(R.layout.layout_countdown_time, this, true);
tv_hour = findViewById(R.id.btn_countdown_hour);
tv_min = findViewById(R.id.countdown_min);
tv_sec = findViewById(R.id.countdown_sec);
runnable = new Runnable() {
@Override
public void run() {
boolean needProcess = processCountMsg();
if(!needProcess)return;
//没隔一秒再次执行一次run方法,实现倒计时功能
mHandler.postDelayed(this, 1000);
}
};
}
布局文件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="horizontal"
android:layout_margin="10dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="距结束:"
android:textColor="#DAA520"
android:textSize="20dp"/>
<TextView
android:id="@+id/btn_countdown_hour"
android:layout_width="31dp"
android:layout_height="30dp"
android:layout_marginRight="2dp"
android:background="@drawable/countdown_shape"
android:gravity="center"
android:textColor="@color/white" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text=":"/>
<TextView
android:id="@+id/countdown_min"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_marginRight="2dp"
android:background="@drawable/countdown_shape"
android:textColor="@color/white"
android:gravity="center"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text=":"/>
<TextView
android:id="@+id/countdown_sec"
android:layout_width="30dp"
android:layout_height="30dp"
android:background="@drawable/countdown_shape"
android:textColor="@color/white"
android:gravity="center"/>
</LinearLayout>
2.3 提供对外方法,处理时间戳
使用post() 发送消息
public void setData(long curDate) {
//TODO
String time;
//计算时间戳与系统时间的时间差,单位为秒
int timeDifference = (int) (curDate - System.currentTimeMillis());
//将总秒数转化为时分秒
if (timeDifference < 60) {
time = String.format("00:00:%02d", timeDifference % 60);
} else if (timeDifference < 3600) {
time = String.format("00:%02d:%02d", timeDifference / 60, timeDifference % 60);
} else {
time = String.format("%02d:%02d:%02d", timeDifference / 3600, timeDifference % 3600 / 60, timeDifference % 60);
}
//通过“:”分离时、分、秒
String[] sArray = time.split(":");
hou = Integer.parseInt(sArray[0]);
min = Integer.parseInt(sArray[1]);
sec = Integer.parseInt(sArray[2]);
//通过Handler中的post()方法传递message
mHandler.post(runnable);
}
使用sendMessage发送消息
private Handler mHandler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case COUNT_MSG:
boolean needProcess = processCountMsg();
if(!needProcess)return;
Message message = Message.obtain();
message.what = COUNT_MSG;
mHandler.sendMessageDelayed(message, 1000);
break;
}
}
};
public void setData(long curDate) {
...............
Message msg = Message.obtain();
msg.what = COUNT_MSG;
mHandler.sendMessage(msg);
}
3、在Activity中实现
public class MainHandlerActivity extends AppCompatActivity {
private CountDown mTime;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main_handler);
mTime = findViewById(R.id.view_countdown);
mTime.setData(System.currentTimeMillis() + 10);
}
}
4、遇到的问题总结
4.1 LayoutInflate
inflate(int resource,ViewGroup root,boolean attachToRoot)
resource:加载的布局id; root:在该布局的外部再嵌套一层父布局,但不是把当前布局放入到界面已有的布局中,比如xml界面,这个方法只是单穿的返回一个view对象。默认attachToRoot是true。
1、如果root为null,attachToRoot将失去作用,设置任何职都没有意义;
2、如果root不为null,attachToRoot设为true,则会给加载布局文件指定一个父布局,即root;
3、如果root不为null,attachToRoot设为false,则会将布局文件最外层的所有layout属性进行设置,当该view被添加到父view当中时,这些layout属性则自动生效。
4、在不设置attachToRoot参数的情况下,如果root不为null,attachToRoot参数默认为true。
4.2 Handler中post()与sendMessage()区别
post(Runnable r)
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
sendMessage(msg)
public final boolean sendMessage(Message msg)
{
return sendMessageDelayed(msg, 0);
}
总结:从源码分析,post(runnable)与sendMessage(msg)本质是一样的,最后返回的都是sendMessageDelayed(msg,0);post()通过调用getPostMessage()方法将Runnable赋值到Message的callback变量中;
消息处理:Looper从MessageQueue中取出Message之后,会调用dispatchMessage方法进行处理;
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
dispatchMessage两种情况
1、如果Message的callback不为null,一般为通过post(Runnable)方式,会直接执行Runnable的run()。因此这里的Runnable实际上就是一个回调接口,跟线程Thread没有任何关系;
2、如果Message的callback为null,这种一般为sendMessage的方式,则会调用handlerMessage()方法进行处理;
4.3 Handler如何实现线程隔离的
final MessageQueue mQueue;
public static @Nullable Looper myLooper(){
return sThreadLocal.get();
}
ThreadLocal是一个能创建线程局部变量的类。通过ThreadLocal提供的get和set方法,可以为每一个使用该变量的线程保存一份数据副本,且线程之间是不能相互访问,从而达到变量在线程间隔离、封闭的效果。
4.4 sendMessageDelayed()是如何实现的
向Message队列中插入Message时,会根据Message的执行时间排序,而消息的延时处理的核心实现是在获取Message的阶段,MessageQueue的next方法如下:
Message next(){
if (msg != null) {
if (now < msg.when) {
// Next message is not ready. Set a timeout to wake up when it is ready.
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// Got a message.
mBlocked = false;
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
if (DEBUG) Log.v(TAG, "Returning message: " + msg);
msg.markInUse();
return msg;
}
} else {
// No more messages.
nextPollTimeoutMillis = -1;
}
}
从MessageQueue中取出一个Message,但是当前的系统时间小于Message.when,因此会计算一个timeout,目的是实现在timeout时间段后再将UI线程唤醒,因此后续处理Message的代码只会在timeout时间之后才会被CPU执行;
如果当前系统时间大于或等于Message.when,那么会返回Message给Looper.loop().但是这个逻辑只能保证在when之前的消息不被处理,不能保证一定在when时被处理。
来源:https://blog.csdn.net/qq_20647053/article/details/115858676
猜你喜欢
- 1. JAVA源文件的命名JAVA源文件名必须和源文件中所定义的类的类名相同。2. Package的命名Package名的第一部分应是小写A
- isInstance和isAssignableFromobj instanceof Class判断obj是不是Class或者Class的子类
- 我就废话不多说了,大家还是直接看代码吧~/* *es配置类 * */@Configurationpublic class ElasticSe
- 一、概述我们对于这个图片肯定会非常熟悉,这两幅图片我们都可以看做是一个文件结构,对于这样的结构我们称之为树形结构。在数据结构中我们了解到可以
- 1、打开IntelliJ IDEA,新建一个Maven项目2、导入Jmeter的依赖包在idea中导入jmeter下的ApacheJMete
- 本文实例讲述了C#通过指针读取文件的方法。分享给大家供大家参考。具体如下:// readfile.cs// 编译时使用:/unsafe//
- 前言:在我们使用C# WinForm中,我们有时候是需要或者自己本机的IP地址进行处理,今天我们学习一下如何使用C# Winform获取主机
- 本文实例讲述了Android SQLite数据库操作方法。分享给大家供大家参考,具体如下:SQLite and AndroidSQLite简
- 本文实例讲述了Android使用ContentResolver搜索手机通讯录的方法。分享给大家供大家参考,具体如下:在这个程序中使用Cont
- Java中获取整点时间戳在实际的开发过程中,前端给后端传时间的时候,有时候传的是整点数值,比如:timeList=[00,01,02,03,
- 前言 spring事务管理包含两种情况,编程式事务、声明
- Java.util.Collections类下有一个静态的shuffle()方法,如下:1)static void shuffle(List
- 什么是WebSocket?WebSocket协议是基于TCP的一种新的网络协议。它实现了浏览器与服务器全双工(full-duplex)通信—
- 方式一:在所有mapper接口使用@Mapper注解@Mapper(将包中的所有接口都标注为DAO层接口)public interface
- 写应用程序的过程中,弹窗是个避免不了的功能,显示中,假设弹窗背景色和主窗口背景色相差不多,甚至是一样的时候,就会存在一个比较严重的人机交互和
- 配置文件形式pom.xml<?xml version="1.0" encoding="UTF-8&quo
- 本篇随笔主要介绍用Java实现简单的装饰器设计模式:先来看一下装饰器设计模式的类图:从图中可以看到,我们可以装饰Component接口的任何
- ①概念二叉搜索树又称二叉排序树,它或者是一棵空树**,或者是具有以下性质的二叉树:若它的左子树不为空,则左子树上所有节点的值都小于根节点的值
- SpringBoot@DeleteMapping(/xxx/{id})请求报405在学习SpringBoot2.x实现 restful 的d
- 这篇文章主要介绍了SpringBoot整合Druid数据源过程详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价