Android 关于倒计时功能的实现

  关于倒计时的实现,可以说有很多的方法,比较常见的就是Timer+TimerTask+Handler了,或者还可以配合Runnable。例如下面的代码:

 

[html] view plaincopy
 
  1. import java.util.Timer;  
  2. import java.util.TimerTask;  
  3. import android.app.Activity;  
  4. import android.os.Bundle;  
  5. import android.os.Handler;  
  6. import android.os.Message;  
  7. import android.util.Log;  
  8. import android.view.View;  
  9. import android.widget.Button;  
  10. import android.widget.TextView;  
  11.   
  12. public class MainActivity extends Activity {  
  13.   
  14.     Timer timer;  
  15.       
  16.     @Override  
  17.     public void onCreate(Bundle savedInstanceState) {  
  18.         super.onCreate(savedInstanceState);  
  19.         setContentView(R.layout.main);  
  20.   
  21.         final TextView tv = (TextView) findViewById(R.id.textView1);  
  22.         Button b = (Button) findViewById(R.id.button1);  
  23.   
  24.         // 定义Handler  
  25.         final Handler handler = new Handler() {  
  26.   
  27.             @Override  
  28.             public void handleMessage(Message msg) {  
  29.                 super.handleMessage(msg);  
  30.                 //handler处理消息  
  31.                 if(msg.what>0){  
  32.                     tv1.setText("" + msg.what);  
  33.                 }else{  
  34.                     //在handler里可以更改UI组件  
  35.                     tv.setText("倒时");  
  36.                     timer.cancel();  
  37.                 }  
  38.             }  
  39.         };  
  40.   
  41.         b.setOnClickListener(new View.OnClickListener() {  
  42.   
  43.             @Override  
  44.             public void onClick(View arg0) {  
  45.                 // 定义计时器  
  46.                  timer = new Timer();  
  47.   
  48.                 // 定义计划任务,根据参数的不同可以完成以下种类的工作:在固定时间执行某任务,在固定时间开始重复执行某任务,重复时间间隔可控,在延迟多久后执行某任务,在延迟多久后重复执行某任务,重复时间间隔可控  
  49.                 timer.schedule(new TimerTask() {  
  50.                     int i = 10;  
  51.   
  52.                     // TimerTask 是个抽象类,实现的是Runable类  
  53.                     @Override  
  54.                     public void run() {  
  55.                           
  56.                         //定义一个消息传过去  
  57.                         Message msg = new Message();  
  58.                         msg.what = i--;  
  59.                         handler.sendMessage(msg);  
  60.                     }  
  61.   
  62.                 }, 1000, 200);  
  63.             }  
  64.         });  
  65.   
  66.     }  
  67.   
  68. }  

    基本逻辑就是这样,需要注意一点是 timer.schedule(task,1000,5000),如果设置为 timer.schedule(task,5000)是不会工作的。因为timer.schedule(task,5000) 是表示执行一次的任务。timer.schedule(task,1000,5000)表示1 秒钟后开始 5 秒钟为周期 的重复执行任务。

    这个例子北京简单,下面给出一个完整的例子:

 

[html] view plaincopy
 
  1. import java.util.Timer;  
  2. import java.util.TimerTask;  
  3. import com.example.jishiqi.SaveRun;  
  4. import android.app.Activity;  
  5. import android.app.AlertDialog;  
  6. import android.content.DialogInterface;  
  7. import android.os.Bundle;  
  8. import android.os.Handler;  
  9. import android.os.Message;  
  10. import android.view.View;  
  11. import android.view.View.OnClickListener;  
  12. import android.widget.Button;  
  13. import android.widget.TextView;  
  14.   
  15. public class MainActivity extends Activity {  
  16.       
  17.     Button btnselecttime, daojishijicubutton;  
  18.     TextView tvTime;  
  19.       
  20.     private Timer timer = null;  
  21.     private TimerTask task = null;  
  22.     private Handler handler = null;  
  23.     private Message msg = null;  
  24.       
  25.     float predegree = 0;  
  26.     float secondpredegree = 0;  
  27.     float hourpredegree = 0;  
  28.   
  29.     int mlCount = -1;  
  30.   
  31.     @Override  
  32.     public void onCreate(Bundle icicle) {  
  33.         super.onCreate(icicle);  
  34.         setContentView(R.layout.main);  
  35.           
  36.         btnselecttime = (Button) findViewById(R.id.daojishistartbutton);  
  37.         daojishijicubutton = (Button) findViewById(R.id.daojishijicubutton);  
  38.         tvTime = (TextView) findViewById(R.id.daojishitvTime);  
  39.           
  40.         SaveRun.setisdaojishi(false);  
  41.         handler = new Handler() {  
  42.             @Override  
  43.             public void handleMessage(Message msg) {  
  44.                 switch (msg.what) {  
  45.                 case 1:  
  46.                     mlCount--;  
  47.                     if (mlCount <= 0) {  
  48.                         enddaojishi();  
  49.                     }  
  50.                     int totalSec = 0;  
  51.                     int yushu = 0;  
  52.                     totalSec = (int) (mlCount / 10);  
  53.                     yushu = (int) (mlCount % 10);  
  54.                     int min = (totalSec / 60);  
  55.                     int sec = (totalSec % 60);  
  56.                     try {  
  57.                         tvTime.setText(String.format("%1$02d:%2$02d.%3$d", min,  
  58.                                 sec, yushu));  
  59.                         predegree = (float) (0.6 * mlCount);  
  60.                         secondpredegree = (float) (36.0 * mlCount);  
  61.                         hourpredegree = (float) (mlCount / 100);  
  62.                     } catch (Exception e) {  
  63.                         tvTime.setText("" + min + ":" + sec + "." + yushu);  
  64.                         e.printStackTrace();  
  65.                     }  
  66.                     break;  
  67.                 default:  
  68.                     break;  
  69.                 }  
  70.                 super.handleMessage(msg);  
  71.             }  
  72.         };  
  73.     }  
  74.   
  75.     private void enddaojishi() {  
  76.         try {  
  77.             task.cancel();  
  78.             task = null;  
  79.             timer.cancel();  
  80.             timer.purge();  
  81.             timer = null;  
  82.             handler.removeMessages(msg.what);  
  83.             new AlertDialog.Builder(MainActivity.this)  
  84.                     .setTitle("提示 ")  
  85.                     .setMessage("倒计时结束")  
  86.                     .setPositiveButton("确定",  
  87.                             new DialogInterface.OnClickListener() {  
  88.                                 @Override  
  89.                                 public void onClick(DialogInterface dialog,  
  90.                                         int which) {  
  91.                                     dialog.cancel();  
  92.                                       
  93.                                       
  94.                                     mlCount = 600;  
  95.                                     btnselecttime.setText("开始");  
  96.                                     SaveRun.setisdaojishi(false);  
  97.                                 }  
  98.                             }).setCancelable(false).create().show();  
  99.         } catch (Exception e) {  
  100.             e.printStackTrace();  
  101.         }  
  102.     }  
  103.   
  104.     @Override  
  105.     protected void onStart() {  
  106.           
  107.         daojishijicubutton.setOnClickListener(new OnClickListener() {  
  108.             @Override  
  109.             public void onClick(View v) {  
  110.                 predegree = 0;  
  111.                 secondpredegree = 0;  
  112.                 hourpredegree = 0;  
  113.               
  114.                 mlCount = -1;  
  115.                 btnselecttime.setText("开始");  
  116.                 SaveRun.setisdaojishi(false);  
  117.                 try {  
  118.                     if (task != null) {  
  119.                         task.cancel();  
  120.                         task = null;  
  121.                         timer.cancel();  
  122.                         timer.purge();  
  123.                         timer = null;  
  124.                         handler.removeMessages(msg.what);  
  125.                     }  
  126.                 } catch (Exception e) {  
  127.                     e.printStackTrace();  
  128.                 }  
  129.             }  
  130.         });  
  131.   
  132.         btnselecttime.setOnClickListener(new OnClickListener() {  
  133.             @Override  
  134.             public void onClick(View arg0) {  
  135.                 if (null == timer) {  
  136.                     if (mlCount == -1 || mlCount == 0) {  
  137.                         mlCount = 600;  
  138.                     }  
  139.                     if (mlCount > 0) {  
  140.                         SaveRun.setisdaojishi(true);  
  141.                         btnselecttime.setText("暂停");  
  142.                         if (null == task) {  
  143.                             task = new TimerTask() {  
  144.                                 @Override  
  145.                                 public void run() {  
  146.                                     if (null == msg) {  
  147.                                         msg = new Message();  
  148.                                     } else {  
  149.                                         msg = Message.obtain();  
  150.                                     }  
  151.                                     msg.what = 1;  
  152.                                     handler.sendMessage(msg);  
  153.                                 }  
  154.                             };  
  155.                         }  
  156.                         timer = new Timer(true);  
  157.                         timer.schedule(task, 100, 100);  
  158.                     }  
  159.                 } else {  
  160.                     try {  
  161.                         SaveRun.setisdaojishi(false);  
  162.                         btnselecttime.setText("继续");  
  163.                         task.cancel();  
  164.                         task = null;  
  165.                         timer.cancel();  
  166.                         timer.purge();  
  167.                         timer = null;  
  168.                         handler.removeMessages(msg.what);  
  169.                     } catch (Exception e) {  
  170.                         e.printStackTrace();  
  171.                     }  
  172.                 }  
  173.             }  
  174.         });  
  175.         super.onStart();  
  176.     }  
  177. }  

布局:

 

 

[html] view plaincopy
 
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     android:layout_width="fill_parent"  
  4.     android:layout_height="fill_parent"  
  5.     android:orientation="vertical" >  
  6.   
  7.     <TextView  
  8.         android:id="@+id/daojishitvTime"  
  9.         android:layout_width="wrap_content"  
  10.         android:layout_height="wrap_content"  
  11.         android:layout_above="@+id/daojishibuttonlinear"  
  12.         android:layout_centerInParent="true"  
  13.         android:text="00:00.0"  
  14.         android:textSize="35sp"  
  15.         android:textStyle="bold" />  
  16.   
  17.     <LinearLayout  
  18.         android:id="@+id/daojishibuttonlinear"  
  19.         android:layout_width="match_parent"  
  20.         android:layout_height="wrap_content"  
  21.         android:layout_alignParentBottom="true"  
  22.         android:orientation="vertical" >  
  23.   
  24.         <LinearLayout  
  25.             android:layout_width="match_parent"  
  26.             android:layout_height="74sp"  
  27.             android:background="@drawable/v5_bottom_bar_bg_light"  
  28.             android:orientation="horizontal" >  
  29.   
  30.             <Button  
  31.                 android:id="@+id/daojishistartbutton"  
  32.                 android:layout_width="wrap_content"  
  33.                 android:layout_height="50sp"  
  34.                 android:layout_gravity="center_vertical"  
  35.                 android:layout_marginLeft="8sp"  
  36.                 android:layout_marginRight="3sp"  
  37.                 android:layout_weight="1"  
  38.                 android:background="@drawable/startbutton"  
  39.                 android:text="开始" />  
  40.   
  41.             <Button  
  42.                 android:id="@+id/daojishijicubutton"  
  43.                 android:layout_width="wrap_content"  
  44.                 android:layout_height="50sp"  
  45.                 android:layout_gravity="center_vertical"  
  46.                 android:layout_marginLeft="3sp"  
  47.                 android:layout_marginRight="8sp"  
  48.                 android:layout_weight="1"  
  49.                 android:background="@drawable/startbutton"  
  50.                 android:text="取消" />  
  51.         </LinearLayout>  
  52.     </LinearLayout>  
  53.   
  54. </RelativeLayout>  
    

 

 



 

    显然,这个方式比较笨拙,我们可以对此进行一个封装,利用Handler和Eunnable,看下面的代码:

 

 

[html] view plaincopy
 
  1. package com.example.daojishi;  
  2.   
  3. import android.os.Handler;  
  4. import android.util.Log;  
  5.   
  6. public class MyCountDownTimer {  
  7.     private long millisInFuture;  
  8.     private long countDownInterval;  
  9.     private boolean status;  
  10.   
  11.     public MyCountDownTimer(long pMillisInFuture, long pCountDownInterval) {  
  12.         this.millisInFuture = pMillisInFuture;  
  13.         this.countDownInterval = pCountDownInterval;  
  14.         status = false;  
  15.         Initialize();  
  16.     }  
  17.   
  18.     public void Stop() {  
  19.         status = false;  
  20.     }  
  21.   
  22.     public long getCurrentTime() {  
  23.         return millisInFuture;  
  24.     }  
  25.   
  26.     public void Start() {  
  27.         status = true;  
  28.     }  
  29.   
  30.     public void Initialize() {  
  31.         final Handler handler = new Handler();  
  32.         Log.v("status", "starting");  
  33.         final Runnable counter = new Runnable() {  
  34.   
  35.             public void run() {  
  36.                 long sec = millisInFuture / 1000;  
  37.                 if (status) {  
  38.                     if (millisInFuture <= 0) {  
  39.                         Log.v("status", "done");  
  40.                     } else {  
  41.                         Log.v("status", Long.toString(sec) + " seconds remain");  
  42.                         millisInFuture -= countDownInterval;  
  43.                         handler.postDelayed(this, countDownInterval);  
  44.                     }  
  45.                 } else {  
  46.                     Log.v("status", Long.toString(sec)  
  47.                             + " seconds remain and timer has stopped!");  
  48.                     handler.postDelayed(this, countDownInterval);  
  49.                 }  
  50.             }  
  51.         };  
  52.   
  53.         handler.postDelayed(counter, countDownInterval);  
  54.     }  
  55. }  

    这个类就是负责倒计时的类,下面结合Activity,看一下怎么用:

 

 

[html] view plaincopy
 
  1. package com.example.daojishi;  
  2.   
  3. import android.app.Activity;  
  4. import android.os.Bundle;  
  5. import android.os.Handler;  
  6. import android.util.Log;  
  7. import android.view.View;  
  8. import android.widget.Button;  
  9. import android.widget.TextView;  
  10.   
  11. public class CounterActivity extends Activity {  
  12.     /** Called when the activity is first created. */  
  13.     TextView timeText;  
  14.     Button startBut;  
  15.     Button stopBut;  
  16.     MyCountDownTimer mycounter;  
  17.   
  18.     @Override  
  19.     public void onCreate(Bundle savedInstanceState) {  
  20.         super.onCreate(savedInstanceState);  
  21.         setContentView(R.layout.main);  
  22.         timeText = (TextView) findViewById(R.id.time);  
  23.         startBut = (Button) findViewById(R.id.start);  
  24.         stopBut = (Button) findViewById(R.id.stop);  
  25.         mycounter = new MyCountDownTimer(20000, 1000);  
  26.         RefreshTimer();  
  27.     }  
  28.   
  29.     public void StartTimer(View v) {  
  30.         Log.v("startbutton", "开始倒计时");  
  31.         mycounter.Start();  
  32.     }  
  33.   
  34.     public void StopTimer(View v) {  
  35.         Log.v("stopbutton", "暂停倒计时");  
  36.         mycounter.Stop();  
  37.     }  
  38.   
  39.     public void RefreshTimer() {  
  40.         final Handler handler = new Handler();  
  41.         final Runnable counter = new Runnable() {  
  42.   
  43.             public void run() {  
  44.                 timeText.setText(Long.toString(mycounter.getCurrentTime()));  
  45.                 handler.postDelayed(this, 100);  
  46.             }  
  47.         };  
  48.   
  49.         handler.postDelayed(counter, 100);  
  50.     }  
  51. }  

    布局文件:

 

 

[html] view plaincopy
 
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     android:layout_width="fill_parent"  
  4.     android:layout_height="fill_parent"  
  5.     android:orientation="vertical"  
  6.     android:weightSum="1" >  
  7.   
  8.     <TextView  
  9.         android:id="@+id/time"  
  10.         android:layout_width="wrap_content"  
  11.         android:layout_height="wrap_content"  
  12.         android:text="TextView"  
  13.         android:textAppearance="?android:attr/textAppearanceLarge" >  
  14.     </TextView>  
  15.   
  16.     <Button  
  17.         android:id="@+id/start"  
  18.         android:layout_width="wrap_content"  
  19.         android:layout_height="wrap_content"  
  20.         android:onClick="StartTimer"  
  21.         android:text="Start" >  
  22.     </Button>  
  23.   
  24.     <Button  
  25.         android:id="@+id/stop"  
  26.         android:layout_width="wrap_content"  
  27.         android:layout_height="wrap_content"  
  28.         android:onClick="StopTimer"  
  29.         android:text="Stop" >  
  30.     </Button>  
  31.   
  32. </LinearLayout>  

    这样就可以比较方便地使用倒计时功能了。但是还有一个更简单的方法。

 

    在Android中有一个CountDownTimer类,这个类就是用来实现类似倒计时方面的功能。使用的时候,只需要继承自CountDownTimer并实现它的方法。

    

[html] view plaincopy
 
  1. import android.app.Activity;    
  2. import android.os.Bundle;    
  3. import android.content.Intent;    
  4. import android.os.CountDownTimer;    
  5. import android.widget.TextView;    
  6. import android.widget.Toast;    
  7. public class NewActivity extends Activity {    
  8.     private MyCount mc;    
  9.     private TextView tv;    
  10.     @Override    
  11.     protected void onCreate(Bundle savedInstanceState) {    
  12.    
  13.         super.onCreate(savedInstanceState);    
  14.         setContentView(R.layout.main);    
  15.         tv = (TextView)findViewById(R.id.show);    
  16.         mc = new MyCount(30000, 1000);    
  17.         mc.start();    
  18.     }   
  19.     
  20.     /*定义一个倒计时的内部类*/    
  21.     class MyCount extends CountDownTimer {       
  22.         public MyCount(long millisInFuture, long countDownInterval) {       
  23.             super(millisInFuture, countDownInterval);       
  24.         }       
  25.         @Override       
  26.         public void onFinish() {       
  27.             tv.setText("done");          
  28.         }       
  29.         @Override       
  30.         public void onTick(long millisUntilFinished) {       
  31.             tv.setText("seconds remaining: " + millisUntilFinished / 1000);       
  32.              
  33.         }      
  34.     }       
  35. }    

    onFinish()方法是本次倒计时结束的时候调用的,onTick是每隔1秒钟执行的,我们就是在这里执行重复的任务,像本例子的显示时间。执行完后会自动取消,如果在期间停止的话,可以调用cancel()方法。看一下它的源码就会发现,它是使用Handler+SystemClock来实现的。

 

 

[html] view plaincopy
 
  1. /*  
  2.  * Copyright (C) 2008 The Android Open Source Project  
  3.  *  
  4.  * Licensed under the Apache License, Version 2.0 (the "License");  
  5.  * you may not use this file except in compliance with the License.  
  6.  * You may obtain a copy of the License at  
  7.  *  
  8.  *      http://www.apache.org/licenses/LICENSE-2.0  
  9.  *  
  10.  * Unless required by applicable law or agreed to in writing, software  
  11.  * distributed under the License is distributed on an "AS IS" BASIS,  
  12.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  
  13.  * See the License for the specific language governing permissions and  
  14.  * limitations under the License.  
  15.  */  
  16.   
  17. package android.os;  
  18.   
  19. import android.util.Log;  
  20.   
  21. /**  
  22.  * Schedule a countdown until a time in the future, with  
  23.  * regular notifications on intervals along the way.  
  24.  *  
  25.  * Example of showing a 30 second countdown in a text field:  
  26.  *  
  27.  * <pre class="prettyprint">  
  28.  * new CountDownTimer(30000, 1000) {  
  29.  *  
  30.  *     public void onTick(long millisUntilFinished) {  
  31.  *         mTextField.setText("seconds remaining: " + millisUntilFinished / 1000);  
  32.  *     }  
  33.  *  
  34.  *     public void onFinish() {  
  35.  *         mTextField.setText("done!");  
  36.  *     }  
  37.  *  }.start();  
  38.  * </pre>  
  39.  *  
  40.  * The calls to {@link #onTick(long)} are synchronized to this object so that  
  41.  * one call to {@link #onTick(long)} won't ever occur before the previous  
  42.  * callback is complete.  This is only relevant when the implementation of  
  43.  * {@link #onTick(long)} takes an amount of time to execute that is significant  
  44.  * compared to the countdown interval.  
  45.  */  
  46. public abstract class CountDownTimer {  
  47.   
  48.     /**  
  49.      * Millis since epoch when alarm should stop.  
  50.      */  
  51.     private final long mMillisInFuture;  
  52.   
  53.     /**  
  54.      * The interval in millis that the user receives callbacks  
  55.      */  
  56.     private final long mCountdownInterval;  
  57.   
  58.     private long mStopTimeInFuture;  
  59.   
  60.     /**  
  61.      * @param millisInFuture The number of millis in the future from the call  
  62.      *   to {@link #start()} until the countdown is done and {@link #onFinish()}  
  63.      *   is called.  
  64.      * @param countDownInterval The interval along the way to receive  
  65.      *   {@link #onTick(long)} callbacks.  
  66.      */  
  67.     public CountDownTimer(long millisInFuture, long countDownInterval) {  
  68.         mMillisInFuture = millisInFuture;  
  69.         mCountdownInterval = countDownInterval;  
  70.     }  
  71.   
  72.     /**  
  73.      * Cancel the countdown.  
  74.      */  
  75.     public final void cancel() {  
  76.         mHandler.removeMessages(MSG);  
  77.     }  
  78.   
  79.     /**  
  80.      * Start the countdown.  
  81.      */  
  82.     public synchronized final CountDownTimer start() {  
  83.         if (mMillisInFuture <= 0) {  
  84.             onFinish();  
  85.             return this;  
  86.         }  
  87.         mStopTimeInFuture = SystemClock.elapsedRealtime() + mMillisInFuture;  
  88.         mHandler.sendMessage(mHandler.obtainMessage(MSG));  
  89.         return this;  
  90.     }  
  91.   
  92.   
  93.     /**  
  94.      * Callback fired on regular interval.  
  95.      * @param millisUntilFinished The amount of time until finished.  
  96.      */  
  97.     public abstract void onTick(long millisUntilFinished);  
  98.   
  99.     /**  
  100.      * Callback fired when the time is up.  
  101.      */  
  102.     public abstract void onFinish();  
  103.   
  104.   
  105.     private static final int MSG = 1;  
  106.   
  107.   
  108.     // handles counting down  
  109.     private Handler mHandler = new Handler() {  
  110.   
  111.         @Override  
  112.         public void handleMessage(Message msg) {  
  113.   
  114.             synchronized (CountDownTimer.this) {  
  115.                 final long millisLeft = mStopTimeInFuture - SystemClock.elapsedRealtime();  
  116.   
  117.                 if (millisLeft <= 0) {  
  118.                     onFinish();  
  119.                 } else if (millisLeft mCountdownInterval) {  
  120.                     // no tick, just delay until done  
  121.                     sendMessageDelayed(obtainMessage(MSG), millisLeft);  
  122.                 } else {  
  123.                     long lastTickStart = SystemClock.elapsedRealtime();  
  124.                     onTick(millisLeft);  
  125.   
  126.                     // take into account user's onTick taking time to execute  
  127.                     long delay = lastTickStart + mCountdownInterval - SystemClock.elapsedRealtime();  
  128.   
  129.                     // special case: user's onTick took more than interval to  
  130.                     // complete, skip to next interval  
  131.                     while (delay 0) delay += mCountdownInterval;  
  132.   
  133.                     sendMessageDelayed(obtainMessage(MSG), delay);  
  134.                 }  
  135.             }  
  136.         }  
  137.     };  
  138. }  

 

    所以,如果你的程序需要执行一些周期性的任务,就可以考虑使用CountDownTimer这个类了。需要注意的是,在上面的这个例子中,最后显示时间是1,也就是说其实上执行了29次。所以这个地方一定要注意,如果你的任务次数是n,那么设置的时候一定要注意设置成n+1的时间。
    
    最后,欢迎大家评论交流,谢谢。

posted @ 2015-02-13 15:20  dongweiq  阅读(384)  评论(0编辑  收藏  举报