Android(安卓)关于倒计时功能的实现
关于倒计时的实现,可以说有很多的方法,比较常见的就是Timer+TimerTask+Handler了,或者还可以配合Runnable。例如下面的代码:
[html] view plain copy- importjava.util.Timer;
- importjava.util.TimerTask;
- importandroid.app.Activity;
- importandroid.os.Bundle;
- importandroid.os.Handler;
- importandroid.os.Message;
- importandroid.util.Log;
- importandroid.view.View;
- importandroid.widget.Button;
- importandroid.widget.TextView;
- publicclassMainActivityextendsActivity{
- Timertimer;
- @Override
- publicvoidonCreate(BundlesavedInstanceState){
- super.onCreate(savedInstanceState);
- setContentView(R.layout.main);
- finalTextViewtv=(TextView)findViewById(R.id.textView1);
- Buttonb=(Button)findViewById(R.id.button1);
- //定义Handler
- finalHandlerhandler=newHandler(){
- @Override
- publicvoidhandleMessage(Messagemsg){
- super.handleMessage(msg);
- //handler处理消息
- if(msg.what>0){
- tv1.setText(""+msg.what);
- }else{
- //在handler里可以更改UI组件
- tv.setText("倒时");
- timer.cancel();
- }
- }
- };
- b.setOnClickListener(newView.OnClickListener(){
- @Override
- publicvoidonClick(Viewarg0){
- //定义计时器
- timer=newTimer();
- //定义计划任务,根据参数的不同可以完成以下种类的工作:在固定时间执行某任务,在固定时间开始重复执行某任务,重复时间间隔可控,在延迟多久后执行某任务,在延迟多久后重复执行某任务,重复时间间隔可控
- timer.schedule(newTimerTask(){
- inti=10;
- //TimerTask是个抽象类,实现的是Runable类
- @Override
- publicvoidrun(){
- //定义一个消息传过去
- Messagemsg=newMessage();
- 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 秒钟为周期 的重复执行任务。
这个例子北京简单,下面给出一个完整的例子:
[html] view plain copy- importjava.util.Timer;
- importjava.util.TimerTask;
- importcom.example.jishiqi.SaveRun;
- importandroid.app.Activity;
- importandroid.app.AlertDialog;
- importandroid.content.DialogInterface;
- importandroid.os.Bundle;
- importandroid.os.Handler;
- importandroid.os.Message;
- importandroid.view.View;
- importandroid.view.View.OnClickListener;
- importandroid.widget.Button;
- importandroid.widget.TextView;
- publicclassMainActivityextendsActivity{
- Buttonbtnselecttime,daojishijicubutton;
- TextViewtvTime;
- privateTimertimer=null;
- privateTimerTasktask=null;
- privateHandlerhandler=null;
- privateMessagemsg=null;
- floatpredegree=0;
- floatsecondpredegree=0;
- floathourpredegree=0;
- intmlCount=-1;
- @Override
- publicvoidonCreate(Bundleicicle){
- 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=newHandler(){
- @Override
- publicvoidhandleMessage(Messagemsg){
- switch(msg.what){
- case1:
- mlCount--;
- if(mlCount<=0){
- enddaojishi();
- }
- inttotalSec=0;
- intyushu=0;
- totalSec=(int)(mlCount/10);
- yushu=(int)(mlCount%10);
- intmin=(totalSec/60);
- intsec=(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(Exceptione){
- tvTime.setText(""+min+":"+sec+"."+yushu);
- e.printStackTrace();
- }
- break;
- default:
- break;
- }
- super.handleMessage(msg);
- }
- };
- }
- privatevoidenddaojishi(){
- try{
- task.cancel();
- task=null;
- timer.cancel();
- timer.purge();
- timer=null;
- handler.removeMessages(msg.what);
- newAlertDialog.Builder(MainActivity.this)
- .setTitle("提示")
- .setMessage("倒计时结束")
- .setPositiveButton("确定",
- newDialogInterface.OnClickListener(){
- @Override
- publicvoidonClick(DialogInterfacedialog,
- intwhich){
- dialog.cancel();
- mlCount=600;
- btnselecttime.setText("开始");
- SaveRun.setisdaojishi(false);
- }
- }).setCancelable(false).create().show();
- }catch(Exceptione){
- e.printStackTrace();
- }
- }
- @Override
- protectedvoidonStart(){
- daojishijicubutton.setOnClickListener(newOnClickListener(){
- @Override
- publicvoidonClick(Viewv){
- 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(Exceptione){
- e.printStackTrace();
- }
- }
- });
- btnselecttime.setOnClickListener(newOnClickListener(){
- @Override
- publicvoidonClick(Viewarg0){
- if(null==timer){
- if(mlCount==-1||mlCount==0){
- mlCount=600;
- }
- if(mlCount>0){
- SaveRun.setisdaojishi(true);
- btnselecttime.setText("暂停");
- if(null==task){
- task=newTimerTask(){
- @Override
- publicvoidrun(){
- if(null==msg){
- msg=newMessage();
- }else{
- msg=Message.obtain();
- }
- msg.what=1;
- handler.sendMessage(msg);
- }
- };
- }
- timer=newTimer(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(Exceptione){
- e.printStackTrace();
- }
- }
- }
- });
- super.onStart();
- }
- }
布局:
[html] view plain copy- <?xmlversion="1.0"encoding="utf-8"?>
- <RelativeLayoutxmlns: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,看下面的代码:
[html] view plain copy- packagecom.example.daojishi;
- importandroid.os.Handler;
- importandroid.util.Log;
- publicclassMyCountDownTimer{
- privatelongmillisInFuture;
- privatelongcountDownInterval;
- privatebooleanstatus;
- publicMyCountDownTimer(longpMillisInFuture,longpCountDownInterval){
- this.millisInFuture=pMillisInFuture;
- this.countDownInterval=pCountDownInterval;
- status=false;
- Initialize();
- }
- publicvoidStop(){
- status=false;
- }
- publiclonggetCurrentTime(){
- returnmillisInFuture;
- }
- publicvoidStart(){
- status=true;
- }
- publicvoidInitialize(){
- finalHandlerhandler=newHandler();
- Log.v("status","starting");
- finalRunnablecounter=newRunnable(){
- publicvoidrun(){
- longsec=millisInFuture/1000;
- if(status){
- if(millisInFuture<=0){
- Log.v("status","done");
- }else{
- Log.v("status",Long.toString(sec)+"secondsremain");
- millisInFuture-=countDownInterval;
- handler.postDelayed(this,countDownInterval);
- }
- }else{
- Log.v("status",Long.toString(sec)
- +"secondsremainandtimerhasstopped!");
- handler.postDelayed(this,countDownInterval);
- }
- }
- };
- handler.postDelayed(counter,countDownInterval);
- }
- }
这个类就是负责倒计时的类,下面结合Activity,看一下怎么用:
[html] view plain copy- packagecom.example.daojishi;
- importandroid.app.Activity;
- importandroid.os.Bundle;
- importandroid.os.Handler;
- importandroid.util.Log;
- importandroid.view.View;
- importandroid.widget.Button;
- importandroid.widget.TextView;
- publicclassCounterActivityextendsActivity{
- /**Calledwhentheactivityisfirstcreated.*/
- TextViewtimeText;
- ButtonstartBut;
- ButtonstopBut;
- MyCountDownTimermycounter;
- @Override
- publicvoidonCreate(BundlesavedInstanceState){
- 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=newMyCountDownTimer(20000,1000);
- RefreshTimer();
- }
- publicvoidStartTimer(Viewv){
- Log.v("startbutton","开始倒计时");
- mycounter.Start();
- }
- publicvoidStopTimer(Viewv){
- Log.v("stopbutton","暂停倒计时");
- mycounter.Stop();
- }
- publicvoidRefreshTimer(){
- finalHandlerhandler=newHandler();
- finalRunnablecounter=newRunnable(){
- publicvoidrun(){
- timeText.setText(Long.toString(mycounter.getCurrentTime()));
- handler.postDelayed(this,100);
- }
- };
- handler.postDelayed(counter,100);
- }
- }
布局文件:
[html] view plain copy- <?xmlversion="1.0"encoding="utf-8"?>
- <LinearLayoutxmlns: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并实现它的方法。
[html] view plain copy
- importandroid.app.Activity;
- importandroid.os.Bundle;
- importandroid.content.Intent;
- importandroid.os.CountDownTimer;
- importandroid.widget.TextView;
- importandroid.widget.Toast;
- publicclassNewActivityextendsActivity{
- privateMyCountmc;
- privateTextViewtv;
- @Override
- protectedvoidonCreate(BundlesavedInstanceState){
- super.onCreate(savedInstanceState);
- setContentView(R.layout.main);
- tv=(TextView)findViewById(R.id.show);
- mc=newMyCount(30000,1000);
- mc.start();
- }
- /*定义一个倒计时的内部类*/
- classMyCountextendsCountDownTimer{
- publicMyCount(longmillisInFuture,longcountDownInterval){
- super(millisInFuture,countDownInterval);
- }
- @Override
- publicvoidonFinish(){
- tv.setText("done");
- }
- @Override
- publicvoidonTick(longmillisUntilFinished){
- tv.setText("secondsremaining:"+millisUntilFinished/1000);
- }
- }
- }
onFinish()方法是本次倒计时结束的时候调用的,onTick是每隔1秒钟执行的,我们就是在这里执行重复的任务,像本例子的显示时间。执行完后会自动取消,如果在期间停止的话,可以调用cancel()方法。看一下它的源码就会发现,它是使用Handler+SystemClock来实现的。
[html] view plain copy- /*
- *Copyright(C)2008TheAndroidOpenSourceProject
- *
- *LicensedundertheApacheLicense,Version2.0(the"License");
- *youmaynotusethisfileexceptincompliancewiththeLicense.
- *YoumayobtainacopyoftheLicenseat
- *
- *http://www.apache.org/licenses/LICENSE-2.0
- *
- *Unlessrequiredbyapplicablelaworagreedtoinwriting,software
- *distributedundertheLicenseisdistributedonan"ASIS"BASIS,
- *WITHOUTWARRANTIESORCONDITIONSOFANYKIND,eitherexpressorimplied.
- *SeetheLicenseforthespecificlanguagegoverningpermissionsand
- *limitationsundertheLicense.
- */
- packageandroid.os;
- importandroid.util.Log;
- /**
- *Scheduleacountdownuntilatimeinthefuture,with
- *regularnotificationsonintervalsalongtheway.
- *
- *Exampleofshowinga30secondcountdowninatextfield:
- *
- *<preclass="prettyprint">
- *newCountDownTimer(30000,1000){
- *
- *publicvoidonTick(longmillisUntilFinished){
- *mTextField.setText("secondsremaining:"+millisUntilFinished/1000);
- *}
- *
- *publicvoidonFinish(){
- *mTextField.setText("done!");
- *}
- *}.start();
- *</pre>
- *
- *Thecallsto{@link#onTick(long)}aresynchronizedtothisobjectsothat
- *onecallto{@link#onTick(long)}won'teveroccurbeforetheprevious
- *callbackiscomplete.Thisisonlyrelevantwhentheimplementationof
- *{@link#onTick(long)}takesanamountoftimetoexecutethatissignificant
- *comparedtothecountdowninterval.
- */
- publicabstractclassCountDownTimer{
- /**
- *Millissinceepochwhenalarmshouldstop.
- */
- privatefinallongmMillisInFuture;
- /**
- *Theintervalinmillisthattheuserreceivescallbacks
- */
- privatefinallongmCountdownInterval;
- privatelongmStopTimeInFuture;
- /**
- *@parammillisInFutureThenumberofmillisinthefuturefromthecall
- *to{@link#start()}untilthecountdownisdoneand{@link#onFinish()}
- *iscalled.
- *@paramcountDownIntervalTheintervalalongthewaytoreceive
- *{@link#onTick(long)}callbacks.
- */
- publicCountDownTimer(longmillisInFuture,longcountDownInterval){
- mMillisInFuture=millisInFuture;
- mCountdownInterval=countDownInterval;
- }
- /**
- *Cancelthecountdown.
- */
- publicfinalvoidcancel(){
- mHandler.removeMessages(MSG);
- }
- /**
- *Startthecountdown.
- */
- publicsynchronizedfinalCountDownTimerstart(){
- if(mMillisInFuture<=0){
- onFinish();
- returnthis;
- }
- mStopTimeInFuture=SystemClock.elapsedRealtime()+mMillisInFuture;
- mHandler.sendMessage(mHandler.obtainMessage(MSG));
- returnthis;
- }
- /**
- *Callbackfiredonregularinterval.
- *@parammillisUntilFinishedTheamountoftimeuntilfinished.
- */
- publicabstractvoidonTick(longmillisUntilFinished);
- /**
- *Callbackfiredwhenthetimeisup.
- */
- publicabstractvoidonFinish();
- privatestaticfinalintMSG=1;
- //handlescountingdown
- privateHandlermHandler=newHandler(){
- @Override
- publicvoidhandleMessage(Messagemsg){
- synchronized(CountDownTimer.this){
- finallongmillisLeft=mStopTimeInFuture-SystemClock.elapsedRealtime();
- if(millisLeft<=0){
- onFinish();
- }elseif(millisLeft<mCountdownInterval){
- //notick,justdelayuntildone
- sendMessageDelayed(obtainMessage(MSG),millisLeft);
- }else{
- longlastTickStart=SystemClock.elapsedRealtime();
- onTick(millisLeft);
- //takeintoaccountuser'sonTicktakingtimetoexecute
- longdelay=lastTickStart+mCountdownInterval-SystemClock.elapsedRealtime();
- //specialcase:user'sonTicktookmorethanintervalto
- //complete,skiptonextinterval
- while(delay<0)delay+=mCountdownInterval;
- sendMessageDelayed(obtainMessage(MSG),delay);
- }
- }
- }
- };
- }
所以,如果你的程序需要执行一些周期性的任务,就可以考虑使用CountDownTimer这个类了。需要注意的是,在上面的这个例子中,最后显示时间是1,也就是说其实上执行了29次。所以这个地方一定要注意,如果你的任务次数是n,那么设置的时候一定要注意设置成n+1的时间。
最后,欢迎大家评论交流,谢谢。
更多相关文章
- Android自定义闹钟
- Android:activity,fragment和service之我见(准备更新)
- 关于com.actionbarsherlock.widget.SearchView的使用无法调用Sea
- Android(安卓)自定义View实现波浪动画
- Android(安卓)日期和时间的使用实例详解
- Android(安卓)自定义Application
- 用Activity实现定制化的Dialog
- Android(安卓)ARouter路由中传对象遇到的坑
- ActivityGroup返回键获取焦点处理