android 計時器的三種實現(Chronometer、Timer、handler)


 目錄:

1、借助Timer實現

2、調用handler.sendMessagedely(Message msg, long delayMillis)

3、借助布局Chronometer

 

1、借助Timer實現

(1) 布局文件

 1 <?xml version="1.0" encoding="utf-8"?>
 2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 3     android:layout_width="match_parent"
 4     android:layout_height="match_parent"
 5     android:orientation="vertical" >
 6 
 7     <TextView
 8         android:id="@+id/timerView"
 9         android:layout_width="wrap_content"
10         android:layout_height="wrap_content"
11         android:layout_gravity="center_horizontal"
12         android:textSize="60sp" />
13 
14 </LinearLayout>
chrono.xml

布局文件很簡單,就是一個TextView用來顯示計時時間。下面看一下Activity里的邏輯實現:

(2)Activity文件

 1 public class MyChronometer extends Activity {
 2     private TextView timerView;
 3     private long baseTimer;
 4     
 5     @Override
 6     protected void onCreate(Bundle savedInstanceState) {
 7         setContentView(R.layout.chrono);
 8         MyChronometer.this.baseTimer = SystemClock.elapsedRealtime();
 9         timerView = (TextView) this.findViewById(R.id.timerView);
10         final Handler startTimehandler = new Handler(){
11         public void handleMessage(android.os.Message msg) {
12                 if (null != timerView) {
13                     timerView.setText((String) msg.obj);
14                 }
15             }
16         };
17         new Timer("開機計時器").scheduleAtFixedRate(new TimerTask() {
18             @Override
19             public void run() {
20                 int time = (int)((SystemClock.elapsedRealtime() - MyChronometer.this.baseTimer) / 1000);
21                 String hh = new DecimalFormat("00").format(time / 3600);
22                 String mm = new DecimalFormat("00").format(time % 3600 / 60);
23                 String ss = new DecimalFormat("00").format(time % 60);    
24                 String timeFormat = new String(hh + ":" + mm + ":" + ss);
25                 Message msg = new Message();
26                 msg.obj = timeFormat;
27                 startTimehandler.sendMessage(msg);
28             }
29             
30         }, 0, 1000L);
31         super.onCreate(savedInstanceState);
32     }
MyChronometer.java

新開一個定時器(Timer), 在子線程中獲取開機時間並轉成字符串格式, 利用handler傳回UI線程顯示。

(3)運行結果:

 

2.調用handler.sendMessagedely(Message msg, long delayMillis)

(1) 布局文件與方法1 相同,運行結果與方法1 相同

(2)Activity文件

 1 public class MyChronometer extends Activity {
 2     private TextView timerView;
 3     private long baseTimer;
 4     
 5     @Override
 6     protected void onCreate(Bundle savedInstanceState) {
 7         setContentView(R.layout.chrono);
 8         MyChronometer.this.baseTimer = SystemClock.elapsedRealtime();
 9         timerView = (TextView) this.findViewById(R.id.timerView);
10         Handler myhandler = new Handler(){
11             public void handleMessage(android.os.Message msg) {
12                 if (0 == MyChronometer.this.baseTimer) {
13                     MyChronometer.this.baseTimer = SystemClock.elapsedRealtime();
14                 }
15                 
16                 int time = (int)((SystemClock.elapsedRealtime() - MyChronometer.this.baseTimer) / 1000);
17                 String hh = new DecimalFormat("00").format(time / 3600);
18                 String mm = new DecimalFormat("00").format(time % 3600 / 60);
19                 String ss = new DecimalFormat("00").format(time % 60);    
20                 if (null != MyChronometer.this.timerView) {
21                     timerView.setText(hh + ":" + mm + ":" + ss);
22                 }
23                 sendMessageDelayed(Message.obtain(this, 0x0), 1000);
24             }
25         };
26         myhandler.sendMessageDelayed(Message.obtain(myhandler, 0x0), 1000);
27         super.onCreate(savedInstanceState);
28     }
MyChronometer

sendMessageDelayed (Message msg, long delayMillis):在 delayMillis/1000 秒后發送消息 msg。

在Handler 的 handleMessage()方法中調用sendMessageDelayed方法, 巧妙的實現了循環。需要注意的是,在Handler外要調用一次startTimehandler.sendMessageDelayed(Message.obtain(startTimehandler, 0x0), 1000);  以作為循環的入口。

 

3.借助布局Chronometer

(1) 布局文件

 1 <?xml version="1.0" encoding="utf-8"?>
 2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 3     android:layout_width="match_parent"
 4     android:layout_height="match_parent"
 5     android:orientation="vertical" >
 6     
 7     <Chronometer
 8         android:id="@+id/chronometer"
 9         android:layout_width="wrap_content"
10         android:layout_height="wrap_content"
11         android:layout_gravity="center_horizontal"
12         android:textSize="60sp" />
13     
14 </LinearLayout>
chono.xml

布局Chronometer繼承自TextView

(2)Activity文件

 1 public class MyChronometer extends Activity {
 2 
 3     Chronometer chronometer;
 4     @Override
 5     protected void onCreate(Bundle savedInstanceState) {
 6         setContentView(R.layout.chrono);
 7         chronometer = (Chronometer) this.findViewById(R.id.chronometer);
 8         chronometer.setBase(SystemClock.elapsedRealtime());
 9         chronometer.start();
10         super.onCreate(savedInstanceState);
11     }
12 }
MyChronometer.java

 邏輯代碼很簡單,調用chronometer.start()就可以開始計時。

chronometer.setBase(long base):設置起始計時點,這里設置的是獲取開機時間。

chronometer.start():以上面setBase()設置的時間點為起始點,開始計時,看一下start()的源碼就知道了:

public void start() {
        mStarted = true;
        updateRunning();
    }

調用了updateRunning(), 跟入updateRunning()方法:

 1     private void updateRunning() {
 2         boolean running = mVisible && mStarted;
 3         if (running != mRunning) {
 4             if (running) {
 5                 updateText(SystemClock.elapsedRealtime());
 6                 dispatchChronometerTick();
 7                 mHandler.sendMessageDelayed(Message.obtain(mHandler, TICK_WHAT), 1000);
 8             } else {
 9                 mHandler.removeMessages(TICK_WHAT);
10             }
11             mRunning = running;
12         }
13     }
14     
15     private Handler mHandler = new Handler() {
16         public void handleMessage(Message m) {
17             if (mRunning) {
18                 updateText(SystemClock.elapsedRealtime());
19                 dispatchChronometerTick();
20                 sendMessageDelayed(Message.obtain(this, TICK_WHAT), 1000);
21             }
22         }
23     };

用updateText()方法設置時間顯示。 至於計時循環機制,和方法二相同,同樣是調用了handler的handMessageDelayed方法。

(3)運行結果:

 

注意:最后說一個關於Chronometer類的常見問題,看到很多人都問用Chronometer類如何設置格式HH:MM:SS的時間。(如果您有此問題請繼續看,沒有問題請忽略)

問這個問題的童鞋先看一下官方文檔的描述:

 If the format string is null, or if you never call setFormat(), the Chronometer will simply display the timer value in "MM:SS" or "H:MM:SS" form.

也就是說默認情況下,使用的格式是"MM:SS" 或者 "H:MM:SS", 然后有童鞋又會問:那到底是"MM:SS" 還是 "H:MM:SS"。我們先看一下源碼:

updateText():

 1 private synchronized void updateText(long now) {
 2         long seconds = now - mBase;
 3         seconds /= 1000;
 4         String text = DateUtils.formatElapsedTime(mRecycle, seconds);
 5 
 6         if (mFormat != null) {
 7             Locale loc = Locale.getDefault();
 8             if (mFormatter == null || !loc.equals(mFormatterLocale)) {
 9                 mFormatterLocale = loc;
10                 mFormatter = new Formatter(mFormatBuilder, loc);
11             }
12             mFormatBuilder.setLength(0);
13             mFormatterArgs[0] = text;
14             try {
15                 mFormatter.format(mFormat, mFormatterArgs);
16                 text = mFormatBuilder.toString();
17             } catch (IllegalFormatException ex) {
18                 if (!mLogged) {
19                     Log.w(TAG, "Illegal format string: " + mFormat);
20                     mLogged = true;
21                 }
22             }
23         }
24         setText(text);
25     }
updateText(long)

調用了DateUtils.formatElapsedTime, 看一下DateUtils.formatElapsedTime里面都有啥:

1  public static String formatElapsedTime(StringBuilder recycle, long elapsedSeconds) {
2          Formatter f = new Formatter(sb, Locale.getDefault());
3         initFormatStrings();
4         if (hours > 0) {
5             return f.format(sElapsedFormatHMMSS, hours, minutes, seconds).toString();
6         } else {
7             return f.format(sElapsedFormatMMSS, minutes, seconds).toString();
8         }
9     }
formatElapsedTime

代碼較多,我就挑重點截取了,仔細看看上面哪個if(){}else{}語句,你肯定就恍然大悟了吧?

為了我們理論的正確性,將方法三 Activity中的代碼稍作修改:

 

chronometer.setBase(-18000000);

運行結果如下:

 

(最近工作中遇到相關代碼異常,就學習了一下, 如果有什么錯誤,希望大家多多探討和指教)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM