Android 关于倒计时功能的说说

来源:互联网 发布:网络推广电话营销话术 编辑:程序博客网 时间:2024/06/06 05:04

    关于倒计时的实现,可以说有很多的方法,比较常见的就是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;@Overridepublic 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);// 定义Handlerfinal Handler handler = new Handler() {@Overridepublic 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() {@Overridepublic void onClick(View arg0) {// 定义计时器 timer = new Timer();// 定义计划任务,根据参数的不同可以完成以下种类的工作:在固定时间执行某任务,在固定时间开始重复执行某任务,重复时间间隔可控,在延迟多久后执行某任务,在延迟多久后重复执行某任务,重复时间间隔可控timer.schedule(new TimerTask() {int i = 10;// TimerTask 是个抽象类,实现的是Runable类@Overridepublic 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;@Overridepublic 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() {@Overridepublic 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() {@Overridepublic void onClick(DialogInterface dialog,int which) {dialog.cancel();mlCount = 600;btnselecttime.setText("开始");SaveRun.setisdaojishi(false);}}).setCancelable(false).create().show();} catch (Exception e) {e.printStackTrace();}}@Overrideprotected void onStart() {daojishijicubutton.setOnClickListener(new OnClickListener() {@Overridepublic 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() {@Overridepublic 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() {@Overridepublic 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;@Overridepublic 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的时间。
    
    最后,欢迎大家评论交流,谢谢。

原创粉丝点击