• 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的时间。
        
        最后,欢迎大家评论交流,谢谢。

  • 相关阅读:
    满血复活
    绝望,绝望、希望
    认真生活的态度
    星期一
    户外穿越
    认真准备
    早点休息
    LeetCode OJ
    LeetCode OJ
    LeetCode OJ
  • 原文地址:https://www.cnblogs.com/dongweiq/p/4290351.html
Copyright © 2020-2023  润新知