目錄:
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>
布局文件很簡單,就是一個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 }
新開一個定時器(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 }
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>
布局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 }
邏輯代碼很簡單,調用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 }
調用了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 }
代碼較多,我就挑重點截取了,仔細看看上面哪個if(){}else{}語句,你肯定就恍然大悟了吧?
為了我們理論的正確性,將方法三 Activity中的代碼稍作修改:
chronometer.setBase(-18000000);
運行結果如下:
(最近工作中遇到相關代碼異常,就學習了一下, 如果有什么錯誤,希望大家多多探討和指教)