Android 關於倒計時功能的說說

 關於倒計時的實現,可以說有很多的方法,比較常見的就是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="https://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  

 *  

 *      https://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的時間。

發佈留言