關於倒計時的實現,可以說有很多的方法,比較常見的就是Timer+TimerTask+Handler了,或者還可以配合Runnable。例如下面的代碼:
[html]
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 秒鐘為周期 的重復執行任務。顯然,這個方式比較笨拙,我們可以對此進行一個封裝,利用Handler和Eunnable,看下面的代碼:
[html]
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,看一下怎麼用:
[html]
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);
}
}
布局文件:
[html]
<?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並實現它的方法。
[html]
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來實現的。
[html]
/*
* 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的時間。