Android 关于倒计时功能的说说

本文详细介绍了倒计时功能的多种实现方法,包括使用Timer+TimerTask+Handler,Runnable,以及CountDownTimer类,并提供了一个封装类MyCountDownTimer简化使用。通过实例演示了如何在Activity中应用这些方法,同时展示了CountDownTimer类的简洁实现。最后,提供了使用CountDownTimer类的代码示例,使得实现倒计时功能变得更加便捷。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

    关于倒计时的实现,可以说有很多的方法,比较常见的就是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;
	
	@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 秒钟为周期 的重复执行任务。

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

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;

	@Override
	public 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() {
			@Override
			public 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() {
								@Override
								public void onClick(DialogInterface dialog,
										int which) {
									dialog.cancel();
									
									
									mlCount = 600;
									btnselecttime.setText("开始");
									SaveRun.setisdaojishi(false);
								}
							}).setCancelable(false).create().show();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	@Override
	protected void onStart() {
		
		daojishijicubutton.setOnClickListener(new OnClickListener() {
			@Override
			public 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() {
			@Override
			public 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() {
								@Override
								public 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;

	@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);
	}
}
    布局文件:

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

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值