我们平时在开发APP的过程中,可能会经常遇到需要计时功能实现的问题,比如:我们设计一个竞速的游戏,那就需要计算玩家游戏用时,这时我们就需要计时并把时间实时显示在屏幕上,那么我们应该怎么去实现呢?
今天cggwz就详细地为大家讲解一下计时功能的开发。
效果图
下面是效果图,计时器在右上角。
这是从我的一个项目里抠出来的,所以接下类的教学做出的效果可能不是这样,当然了我接下来的教学只是围绕核心的功能设计,个性化的设计留给你们。因为是从一个项目里抠出来的,所以原谅我没有给完整的代码,但是我保证,该给的变量定义,所有需要用到的代码都已经给了,每个变量和方法都会进行解释,放心食用就好了。
主要工具
我们主要需要利用的工具有四个:Timer、TimerTask、Handler、Message。
我们先简单介绍一下这些东西都是什么:
首先TimerTask和Timer,我们想实时显示时间,那么我们就需要,每隔一秒就更新一次界面上的时间显示组件,换言之,我们就是要每隔一秒就执行一段代码,而这段代码就由TimerTask来提供,顾名思义,时间器的任务,自然就是Timer需要执行的代码。
而Timer就是主要的控制器,它可以每隔一段固定的时间就执行一次TimerTask,这个时间间隔可以自己设置,这里因为要实现计时功能,我们会设置为1000毫秒。
接下来是Handler和Message,我们知道,我们的TimerTask需要执行获取时间并更新显示时间的组件的任务,但是它本身是没法对组件直接更新的(可以试试,然后你会发现APP会闪退),那么就需要Handler的帮助,那么我们怎么让Handler知道更新组件呢?这就要用Message了,消息。发送Message来通知Handler来更新显示时间的组件。
代码实现
首先创建一个Activity
在对应的xml文件里加上一个TextView即可。我们就会用它来显示时间。
我们再添加一个按钮用来暂停计时。(注意是暂停,不是停止)
代码如下:
<TextView
android:id="@+id/timeshow"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="right"
android:layout_marginRight="30dp"
android:text="00:00"/>
<ImageButton
android:id="@+id/stop"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_gravity="right"
android:scaleType="fitXY"
android:background="@color/transparent"
app:srcCompat="@drawable/stop" />
我们的布局文件添加这两个组件就够了。
下面我们来编写这个Activity对应的java文件。
这里我们首先需要定义一个TextView和一个ImageView,然后通过findViewById()来找到我们在布局文件中放置的两个组件。
ImageButton stop;
TextView time_show;
stop=findViewById(R.id.stop);
time_show=findViewById(R.id.timeshow);
接下来我们再配置一下Handler:
int count=0;
final static int UPDATE_TEXTVIEW=0;
Handler handler=new Handler(){
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
if(msg.what==UPDATE_TEXTVIEW){
int minutes=count/60,seconds=count%60;
time_show.setText(String.valueOf(minutes)+":"+String.valueOf(seconds));
}
}
};
这里我们来解释一下,首先count变量,是我们用来数秒的,也就是每过一秒就会自增,这个会写在TimerTask中,也就是说我们程序已经启动count秒。
接下来是handleMessage()方法,这个方法就是处理消息的方法,而它的参数就是它接收到的Message。
接下来是一个判断语句,判断我们捕获的消息是不是我们需要的消息,因为一个程序中也许会由很多的消息,而消息的what就好像是他们的ID一样,用来标识他们,让程序可以区分他们。而这里的UPDATE_TEXTVIEW
是随便设置的,你只要确保你后面TimerTask发送的消息的what和你处理的一致即可。
然后我们计算出分钟和秒,显示即可。
下面我们来配置一下我们的核心工具:Timer和TimerTask:
Timer timer=null;
TimerTask timerTask=null;
boolean isPause=false;
private void startTimer(){
if (timer==null){
timer=new Timer();
}
if(timerTask==null){
timerTask=new TimerTask() {
@Override
public void run() {
Message message=new Message();
message.what=UPDATE_TEXTVIEW;
handler.sendMessage(message);
do{
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}while(isPause);
count++;
}
};
}
if(timerTask!=null&&timer!=null){
timer.schedule(timerTask,1000,1000);
}
}
首先我们创建了Timer和TimerTask的对象,默认设置为null,便于判断。
然后我们定义了一个布尔型的数据isPause,看名字应该能明白什么意思,就是用来标识是否暂停。
接下就是我们启动时间器的函数了,我们只要在想开始计时的地方调用这个函数即可。
这里首先判断Timer是不是null,如果是,初始化Timer。
再创建TimerTask,相信你已经看出来了,TimerTask就是一个线程,里面有一个run方法,那就是我们要执行的代码。每次执行TimerTask,首先新建一个Message,然后把Message的what设置为我们之前设置的常量,再发送信息,让hanler去更新时间。然后把count自增,这就完成了基本的计时功能,接下来是我们的暂停功能,就是这个循环,正常情况下,isPause是false,这个循环不会执行,但是,一旦isPause变成true,这个线程就会进入睡眠。你也许会疑问,为什么还要写sleep,直接死循环不好吗?这时为了保证我们一定是每个一秒更新一次,保证时间的准确性,这也就注定sleep只能是1秒,而不能是其他数值。
最后是schedule方法。它的第一个参数是我们的Timer需要执行的任务。第二个参数是多少毫秒后开始计时,第三个参数就是时间间隔了,就是每多长时间执行一次我们的任务。
最后我们来设置一下我们控制按钮的事件监听器:
stop.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View view) {
if(state==0){
isPause=true;
stop.setImageDrawable(getResources().getDrawable(R.drawable.goon));
}else{
isPause=false;
stop.setImageDrawable(getResources().getDrawable(R.drawable.stop));
}
}
});
这就很明显了,我们只需要改动isPause的值就可以完成暂停和继续的功能了。
这里我用了一些图片资源,具体的大家可以用普通Button也可以。
结束语
我觉得我已经解释得很详细了,如果有问题可以在评论区问,基本每天都在,一定会回答的。