关于倒计时的实现,可以说有很多的方法,比较常见的就是Timer+TimerTask+Handler了,或者还可以配合Runnable。例如下面的代码:
import java.util.Timer;
import java.util.TimerTask;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
public class MainActivity extends Activity {
Timer timer;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
final TextView tv = (TextView) findViewById(R.id.textView1);
Button b = (Button) findViewById(R.id.button1);
// 定义Handler
final Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
//handler处理消息
if(msg.what>0){
tv1.setText("" + msg.what);
}else{
//在handler里可以更改UI组件
tv.setText("倒时");
timer.cancel();
}
}
};
b.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View arg0) {
// 定义计时器
timer = new Timer();
// 定义计划任务,根据参数的不同可以完成以下种类的工作:在固定时间执行某任务,在固定时间开始重复执行某任务,重复时间间隔可控,在延迟多久后执行某任务,在延迟多久后重复执行某任务,重复时间间隔可控
timer.schedule(new TimerTask() {
int i = 10;
// TimerTask 是个抽象类,实现的是Runable类
@Override
public void run() {
//定义一个消息传过去
Message msg = new Message();
msg.what = i--;
handler.sendMessage(msg);
}
}, 1000, 200);
}
});
}
}
基本逻辑就是这样,需要注意一点是 timer.schedule(task,1000,5000),如果设置为 timer.schedule(task,5000)是不会工作的。因为timer.schedule(task,5000) 是表示执行一次的任务。timer.schedule(task,1000,5000)表示1 秒钟后开始 5 秒钟为周期 的重复执行任务。
这个例子北京简单,下面给出一个完整的例子:
import java.util.Timer;
import java.util.TimerTask;
import com.example.jishiqi.SaveRun;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
public class MainActivity extends Activity {
Button btnselecttime, daojishijicubutton;
TextView tvTime;
private Timer timer = null;
private TimerTask task = null;
private Handler handler = null;
private Message msg = null;
float predegree = 0;
float secondpredegree = 0;
float hourpredegree = 0;
int mlCount = -1;
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.main);
btnselecttime = (Button) findViewById(R.id.daojishistartbutton);
daojishijicubutton = (Button) findViewById(R.id.daojishijicubutton);
tvTime = (TextView) findViewById(R.id.daojishitvTime);
SaveRun.setisdaojishi(false);
handler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 1:
mlCount--;
if (mlCount <= 0) {
enddaojishi();
}
int totalSec = 0;
int yushu = 0;
totalSec = (int) (mlCount / 10);
yushu = (int) (mlCount % 10);
int min = (totalSec / 60);
int sec = (totalSec % 60);
try {
tvTime.setText(String.format("%1$02d:%2$02d.%3$d", min,
sec, yushu));
predegree = (float) (0.6 * mlCount);
secondpredegree = (float) (36.0 * mlCount);
hourpredegree = (float) (mlCount / 100);
} catch (Exception e) {
tvTime.setText("" + min + ":" + sec + "." + yushu);
e.printStackTrace();
}
break;
default:
break;
}
super.handleMessage(msg);
}
};
}
private void enddaojishi() {
try {
task.cancel();
task = null;
timer.cancel();
timer.purge();
timer = null;
handler.removeMessages(msg.what);
new AlertDialog.Builder(MainActivity.this)
.setTitle("提示 ")
.setMessage("倒计时结束")
.setPositiveButton("确定",
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog,
int which) {
dialog.cancel();
mlCount = 600;
btnselecttime.setText("开始");
SaveRun.setisdaojishi(false);
}
}).setCancelable(false).create().show();
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
protected void onStart() {
daojishijicubutton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
predegree = 0;
secondpredegree = 0;
hourpredegree = 0;
mlCount = -1;
btnselecttime.setText("开始");
SaveRun.setisdaojishi(false);
try {
if (task != null) {
task.cancel();
task = null;
timer.cancel();
timer.purge();
timer = null;
handler.removeMessages(msg.what);
}
} catch (Exception e) {
e.printStackTrace();
}
}
});
btnselecttime.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0) {
if (null == timer) {
if (mlCount == -1 || mlCount == 0) {
mlCount = 600;
}
if (mlCount > 0) {
SaveRun.setisdaojishi(true);
btnselecttime.setText("暂停");
if (null == task) {
task = new TimerTask() {
@Override
public void run() {
if (null == msg) {
msg = new Message();
} else {
msg = Message.obtain();
}
msg.what = 1;
handler.sendMessage(msg);
}
};
}
timer = new Timer(true);
timer.schedule(task, 100, 100);
}
} else {
try {
SaveRun.setisdaojishi(false);
btnselecttime.setText("继续");
task.cancel();
task = null;
timer.cancel();
timer.purge();
timer = null;
handler.removeMessages(msg.what);
} catch (Exception e) {
e.printStackTrace();
}
}
}
});
super.onStart();
}
}
布局:<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<TextView
android:id="@+id/daojishitvTime"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_above="@+id/daojishibuttonlinear"
android:layout_centerInParent="true"
android:text="00:00.0"
android:textSize="35sp"
android:textStyle="bold" />
<LinearLayout
android:id="@+id/daojishibuttonlinear"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:orientation="vertical" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="74sp"
android:background="@drawable/v5_bottom_bar_bg_light"
android:orientation="horizontal" >
<Button
android:id="@+id/daojishistartbutton"
android:layout_width="wrap_content"
android:layout_height="50sp"
android:layout_gravity="center_vertical"
android:layout_marginLeft="8sp"
android:layout_marginRight="3sp"
android:layout_weight="1"
android:background="@drawable/startbutton"
android:text="开始" />
<Button
android:id="@+id/daojishijicubutton"
android:layout_width="wrap_content"
android:layout_height="50sp"
android:layout_gravity="center_vertical"
android:layout_marginLeft="3sp"
android:layout_marginRight="8sp"
android:layout_weight="1"
android:background="@drawable/startbutton"
android:text="取消" />
</LinearLayout>
</LinearLayout>
</RelativeLayout>
显然,这个方式比较笨拙,我们可以对此进行一个封装,利用Handler和Eunnable,看下面的代码:
package com.example.daojishi;
import android.os.Handler;
import android.util.Log;
public class MyCountDownTimer {
private long millisInFuture;
private long countDownInterval;
private boolean status;
public MyCountDownTimer(long pMillisInFuture, long pCountDownInterval) {
this.millisInFuture = pMillisInFuture;
this.countDownInterval = pCountDownInterval;
status = false;
Initialize();
}
public void Stop() {
status = false;
}
public long getCurrentTime() {
return millisInFuture;
}
public void Start() {
status = true;
}
public void Initialize() {
final Handler handler = new Handler();
Log.v("status", "starting");
final Runnable counter = new Runnable() {
public void run() {
long sec = millisInFuture / 1000;
if (status) {
if (millisInFuture <= 0) {
Log.v("status", "done");
} else {
Log.v("status", Long.toString(sec) + " seconds remain");
millisInFuture -= countDownInterval;
handler.postDelayed(this, countDownInterval);
}
} else {
Log.v("status", Long.toString(sec)
+ " seconds remain and timer has stopped!");
handler.postDelayed(this, countDownInterval);
}
}
};
handler.postDelayed(counter, countDownInterval);
}
}
这个类就是负责倒计时的类,下面结合Activity,看一下怎么用:package com.example.daojishi;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
public class CounterActivity extends Activity {
/** Called when the activity is first created. */
TextView timeText;
Button startBut;
Button stopBut;
MyCountDownTimer mycounter;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
timeText = (TextView) findViewById(R.id.time);
startBut = (Button) findViewById(R.id.start);
stopBut = (Button) findViewById(R.id.stop);
mycounter = new MyCountDownTimer(20000, 1000);
RefreshTimer();
}
public void StartTimer(View v) {
Log.v("startbutton", "开始倒计时");
mycounter.Start();
}
public void StopTimer(View v) {
Log.v("stopbutton", "暂停倒计时");
mycounter.Stop();
}
public void RefreshTimer() {
final Handler handler = new Handler();
final Runnable counter = new Runnable() {
public void run() {
timeText.setText(Long.toString(mycounter.getCurrentTime()));
handler.postDelayed(this, 100);
}
};
handler.postDelayed(counter, 100);
}
}
布局文件:<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical"
android:weightSum="1" >
<TextView
android:id="@+id/time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="TextView"
android:textAppearance="?android:attr/textAppearanceLarge" >
</TextView>
<Button
android:id="@+id/start"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="StartTimer"
android:text="Start" >
</Button>
<Button
android:id="@+id/stop"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="StopTimer"
android:text="Stop" >
</Button>
</LinearLayout>
这样就可以比较方便地使用倒计时功能了。但是还有一个更简单的方法。
在Android中有一个CountDownTimer类,这个类就是用来实现类似倒计时方面的功能。使用的时候,只需要继承自CountDownTimer并实现它的方法。
import android.app.Activity;
import android.os.Bundle;
import android.content.Intent;
import android.os.CountDownTimer;
import android.widget.TextView;
import android.widget.Toast;
public class NewActivity extends Activity {
private MyCount mc;
private TextView tv;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
tv = (TextView)findViewById(R.id.show);
mc = new MyCount(30000, 1000);
mc.start();
}
/*定义一个倒计时的内部类*/
class MyCount extends CountDownTimer {
public MyCount(long millisInFuture, long countDownInterval) {
super(millisInFuture, countDownInterval);
}
@Override
public void onFinish() {
tv.setText("done");
}
@Override
public void onTick(long millisUntilFinished) {
tv.setText("seconds remaining: " + millisUntilFinished / 1000);
}
}
}
onFinish()方法是本次倒计时结束的时候调用的,onTick是每隔1秒钟执行的,我们就是在这里执行重复的任务,像本例子的显示时间。执行完后会自动取消,如果在期间停止的话,可以调用cancel()方法。看一下它的源码就会发现,它是使用Handler+SystemClock来实现的。/*
* Copyright (C) 2008 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.os;
import android.util.Log;
/**
* Schedule a countdown until a time in the future, with
* regular notifications on intervals along the way.
*
* Example of showing a 30 second countdown in a text field:
*
* <pre class="prettyprint">
* new CountDownTimer(30000, 1000) {
*
* public void onTick(long millisUntilFinished) {
* mTextField.setText("seconds remaining: " + millisUntilFinished / 1000);
* }
*
* public void onFinish() {
* mTextField.setText("done!");
* }
* }.start();
* </pre>
*
* The calls to {@link #onTick(long)} are synchronized to this object so that
* one call to {@link #onTick(long)} won't ever occur before the previous
* callback is complete. This is only relevant when the implementation of
* {@link #onTick(long)} takes an amount of time to execute that is significant
* compared to the countdown interval.
*/
public abstract class CountDownTimer {
/**
* Millis since epoch when alarm should stop.
*/
private final long mMillisInFuture;
/**
* The interval in millis that the user receives callbacks
*/
private final long mCountdownInterval;
private long mStopTimeInFuture;
/**
* @param millisInFuture The number of millis in the future from the call
* to {@link #start()} until the countdown is done and {@link #onFinish()}
* is called.
* @param countDownInterval The interval along the way to receive
* {@link #onTick(long)} callbacks.
*/
public CountDownTimer(long millisInFuture, long countDownInterval) {
mMillisInFuture = millisInFuture;
mCountdownInterval = countDownInterval;
}
/**
* Cancel the countdown.
*/
public final void cancel() {
mHandler.removeMessages(MSG);
}
/**
* Start the countdown.
*/
public synchronized final CountDownTimer start() {
if (mMillisInFuture <= 0) {
onFinish();
return this;
}
mStopTimeInFuture = SystemClock.elapsedRealtime() + mMillisInFuture;
mHandler.sendMessage(mHandler.obtainMessage(MSG));
return this;
}
/**
* Callback fired on regular interval.
* @param millisUntilFinished The amount of time until finished.
*/
public abstract void onTick(long millisUntilFinished);
/**
* Callback fired when the time is up.
*/
public abstract void onFinish();
private static final int MSG = 1;
// handles counting down
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
synchronized (CountDownTimer.this) {
final long millisLeft = mStopTimeInFuture - SystemClock.elapsedRealtime();
if (millisLeft <= 0) {
onFinish();
} else if (millisLeft < mCountdownInterval) {
// no tick, just delay until done
sendMessageDelayed(obtainMessage(MSG), millisLeft);
} else {
long lastTickStart = SystemClock.elapsedRealtime();
onTick(millisLeft);
// take into account user's onTick taking time to execute
long delay = lastTickStart + mCountdownInterval - SystemClock.elapsedRealtime();
// special case: user's onTick took more than interval to
// complete, skip to next interval
while (delay < 0) delay += mCountdownInterval;
sendMessageDelayed(obtainMessage(MSG), delay);
}
}
}
};
}
所以,如果你的程序需要执行一些周期性的任务,就可以考虑使用CountDownTimer这个类了。需要注意的是,在上面的这个例子中,最后显示时间是1,也就是说其实上执行了29次。所以这个地方一定要注意,如果你的任务次数是n,那么设置的时候一定要注意设置成n+1的时间。
最后,欢迎大家评论交流,谢谢。