Android 原始碼徹底研究系列 - 鬧鐘程式, AnalogClock (1)

 

 

转自:http://ysl-paradise.blogspot.com/2009/07/android-analogclock-1.html

Android Source Code Internals - Alarm Clock, AnalogClock (1)

鬧鐘的原始程式碼在 這裡

一執行這個鬧鐘程式,畫面上第一眼看到的,就是一個大大的時鐘。因此我們今天,就先從這個時鐘下手。讓我們一探究竟,看他是如何實現的。

要了解這個時鐘,是用什麼元件做出來的。有兩個方法,第一個就是用 SDK 內自帶的 Hierarchy Viewer 工具;另一個,就是直接觀察 res/layout 中的畫面設計檔。這次,我們直接用第二個方法。

翻開 AlarmClock/res/layout 目錄的內容,你會發現有好幾個 clock_xxx.xml 的檔案。打開其中的 clock_droid2.xml 檔。內容如下所示:

  1. <? xml   version = "1.0"   encoding = "utf-8" ?>   
  2. < AnalogClock    
  3.  xmlns:android = "http://schemas.android.com/apk/res/android"   
  4.  android:id = "@+id/clock"   
  5.  style = "@style/analogClock"   
  6.  android:dial = "@drawable/clockdroid2_dial"   
  7.  android:hand_hour = "@drawable/clockdroid2_hour"   
  8.  android:hand_minute = "@drawable/clockdroid2_minute" />   

嗯,看起來,這個時鐘就是用 android.widget.AnalogClock 這個元件直接實現的。只要透過 android:dial, android:hand_hour, android:hand_minute 這三個 XML 屬性,指定鐘面,時針與分針的圖檔,一個完整的時鐘,就會顯現出來。最上方的圖片,就是這個時鐘顯示的樣子。

 

都不需要任何額外的設定,這個時鐘元件就會自己依照目前的時間,將時、分針顯示在正確的位置,那他是如何定時更新時、分針位置?另外,注意 到了嗎,時、分針的圖檔,只有一份。從這就猜到,這個時鐘還會自動將時、分針圖旋轉至正確的角度,貼在螢幕上。讓我們來看看,這個 AnalogClock 元件是如何實現這些功能的。

打開 AnalogClock 元件原始碼 ,扣掉最前面的註解不算,整個原始碼也才不到 230 行。這麼少的程式碼,卻能將這個時鐘功能做得這麼完整。這也是我常最推薦,一定要看的元件原始碼之ㄧ。

這次,我們要看的重點有兩個。

  1. 在元件程式中,要如何隨時都知道時間的改變?
  2. 在 Android 中,要如何將圖片做任意角度的旋轉,並貼在螢幕上?

在元件程式中,要如何隨時都知道時間的改變?

原來在 onAttachedToWindow() 中,AnalogClock 透過 registerReceiver() 註冊了三個由系統發出的 Actions,ACTION_TIME_TICK , ACTION_TIME_CHANGEDACTION_TIMEZONE_CHANGED

在 Android 中,系統會透過 Intent 傳送某些系統通知訊息,這些通知訊息又稱為 Action。在 Intent 類別的說明中,你可以找到所有的 Action 定義。

在你的程式中,想要收到這些系統通知訊息,有兩個方法。第一個方法就是在 AndroidManifest.xml 中定義 receiver 的類別,這個方法在日後的介紹中會提到。第二種方法就是利用 registerReceiver(),向系統註冊接收的類別。由於 AnaloClock 是以元件的方式存在,如果每次使用這個元件,都還要在 AndroidManifest.xml 中定義,那就太麻煩了。因此, AnaloClock 採用的是第二種方法。

其中,ACTION_TIME_TICK,就是由系統在每分整,傳送出來的 Action。ACTION_TIME_CHANGED 則是當使用者更改系統時間時,所送出的 Action。當你的手機跨越時區,系統則會送出 ACTION_TIMEZONE_CHANGED 給有註冊的程式。

  1. //#100 ~ #106   
  2. IntentFilter filter = new  IntentFilter();  
  3. filter.addAction(Intent.ACTION_TIME_TICK);  
  4. filter.addAction(Intent.ACTION_TIME_CHANGED);  
  5. filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);  
  6. getContext().registerReceiver(mIntentReceiver, filter, null , mHandler);  

一但這些 Actions 發生時,他會呼叫在程式最後 new 出來的 BroadcastReceiver 物件, mIntentReceiver,中的 onReceive() 回呼函式。

首先,在 onReceive() 中,先檢查如果是時區有改變的話,當然你要重新設定 mCalendar 變數。

  1. //#233 ~ #245   
  2. private   final  BroadcastReceiver mIntentReceiver =   
  3. new  BroadcastReceiver() {  
  4.  @Override   
  5.  public   void  onReceive(Context context, Intent intent) {  
  6.   if  (intent.getAction().equals(Intent.ACTION_TIMEZONE_CHANGED)) {  
  7.    String tz = intent.getStringExtra("time-zone" );  
  8.    mCalendar = new  Time(TimeZone.getTimeZone(tz).getID());  
  9.   }  
  10.   
  11.   onTimeChanged();  
  12.           
  13.   invalidate();  
  14.  }  
  15. };  

onTimeChanged() 函式,只是重新抓出最新的時、分資料。

  1. //#221 ~ #231   
  2. private   void  onTimeChanged() {  
  3.  mCalendar.setToNow();  
  4.   
  5.  int  hour = mCalendar.hour;  
  6.  int  minute = mCalendar.minute;  
  7.  int  second = mCalendar.second;  
  8.   
  9.  mMinutes = minute + second / 60 .0f;  
  10.  mHour = hour + mMinutes / 60 .0f;  
  11.  mChanged = true ;  
  12. }    

在 onReceive() 的最後,再呼叫 View.invalidate()。這個動作,會觸發 View.onDraw() 函式的呼叫,並在螢幕上畫上最新的時,分針。

在 Android 中,要如何將圖片做任意角度的旋轉,並貼在螢幕上?

Android 的 2D 繪圖功能,其實是很強的,尤其是 matrix 這部份。透過 matrix,你就可以輕易地,控制整個繪圖座標體系的位移、旋轉、傾斜,放大等功能。舉個例子來說,如果你要將圖片旋轉 30 度後,再貼在螢幕上。傳統的作法,都是直接先將圖片的內容,逐點旋轉 30 度。但是在 Android 平台上,你不用這麼麻煩。你只要將整個座標系統轉個 30 度,之後不管你是畫線,還是貼圖,最後呈現在螢幕上的都是轉 30 度的效果。

想要了解 AnalogClock 是如何選轉時、分針圖片,那看 onDraw() 這個函式就對了。我將 onDraw() 中,和這部份選轉時、分針圖片相關的,列在下面。其中的 canvas.rotate() 是關鍵,這個函式就是在將座標系統轉個角度。看完這個例子,有沒有發覺要在 Android 上旋轉、放大或縮小圖片,其實都是只要兩三行程式,就可搞定的事。

  1. //#159 ~ #219   
  2. @Override   
  3. protected   void  onDraw(Canvas canvas) {  
  4.  ...  
  5.  canvas.save();  
  6.  canvas.rotate(mHour / 12 .0f *  360 .0f, x, y);  
  7.  final  Drawable hourHand = mHourHand;  
  8.  if  (changed) {  
  9.      w = hourHand.getIntrinsicWidth();  
  10.      h = hourHand.getIntrinsicHeight();  
  11.      hourHand.setBounds(x - (w / 2 ), y - (h /  2 ), x + (w /  2 ), y + (h /  2 ));  
  12.  }  
  13.  hourHand.draw(canvas);  
  14.  canvas.restore();  
  15.   
  16.  canvas.save();  
  17.  canvas.rotate(mMinutes / 60 .0f *  360 .0f, x, y);  
  18.  final  Drawable minuteHand = mMinuteHand;  
  19.  if  (changed) {  
  20.      w = minuteHand.getIntrinsicWidth();  
  21.      h = minuteHand.getIntrinsicHeight();  
  22.      minuteHand.setBounds(x - (w / 2 ), y - (h /  2 ), x + (w /  2 ), y + (h /  2 ));  
  23.  }  
  24.  minuteHand.draw(canvas);  
  25.  canvas.restore();  
  26.  ...  
  27. }  

AnalogClock 就剩 constructors,與其他 onMeasure(), onSizeChanged(), onDetachedFromWindow() 等函式還沒提到,我想這部份就留給你自己去發掘。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值