顯示具有 View 標籤的文章。 顯示所有文章
顯示具有 View 標籤的文章。 顯示所有文章

2015年1月6日 星期二

Material style Dialogs

如果你依照 AppCompat v21 — Material Design for Pre-Lollipop Devices! 文件中的說明,為你的 APP 加上 material style 的顏色設定 (colorPrimary, colorPrimaryDark, colorAccent)。執行時,你會發現只有 Activity 內的 ActionBar(Toolbar) 或 Views 會套用這些顏色設定。就算在 Android 5+ 上面執行,Dialog (AlertDialog) 中各 Views 的顏色並不支援 material style 的顏色設定。就像上圖中的左邊,RadioButton 不是 colorAccent 所定義的顏色。

這原因在於 AppCompatV7 (r21) 並不支援 Dialog 或 AlertDialog。要解決這個問題,有兩種方法:

第一種解法是加一個 res/values-v21/themes.xml,其內容如下範例:

這解法的的好處是簡單,不用額外的套件。壞處是在 Android 4- 平台上,Dialog 的長相與 Views 顏色仍然與原生平台相同。

第二種解法是採用 Material Dialogs 這個套件。優點是在 Android 4+ 平台上會有一致的 Dialog 長相與 Views 顏色,缺點是這位開發者三天兩頭就改個版本,看來離穩定版還有一段距離。

如果你問我,我現在是用那個解法?答案是解法一,因為用第三方套件的風險實在很高,非必要盡量少用,以免增加無謂的程式錯誤與維護程式的複雜度。


繼續閱讀全文...

2014年4月20日 星期日

好用的 Activity.getView()

在 Activity 中最常看到的函式呼叫,就是 findViewById(),常見用法如下:

由於 findViewById() 傳回的是 View 這個通用型別。因此實務上每次都要強制轉型成實際的型別,這樣的程式碼看起來實在有點礙眼。

要解決這樣的問題,你只要在 Activity 中,加入底下這個 getView() 函式。

之後,你就可以將原先的 findViewById() 呼叫改寫成底下這個方式:

一個小技巧,卻可以讓你的程式碼看起來清爽許多。當然在 Fragment 中,你也可加上自己的 getView(),至於如何寫?建議自己研究一下。


繼續閱讀全文...

2011年4月26日 星期二

使用 ListView 元件時該注意的技巧 - I

The tricks when using ListViews - I

綜觀 Android 目前所提供的 UI 元件 (Views),除了 TextView, Button 等元件之外,ListView 應該是最常被使用到的元件之一。

這主要是因為手機多是左右窄、上下長的螢幕,當要顯示很多資料時,垂直捲動會比水平捲動來得容易操作許多。另外,為了便利使用者能快速消化你所呈現的資料,你必須將資料以較為有系統與組織的方式呈現。而用 ListView 來以條列式的方式呈現你的資料,就是手機應用的最好選擇。

如果你有寫過 Android 應用,我想你一定有用過 ListView。不過,你對 ListView 的了解有多少?你知道使用 ListView 元件時,有哪些該注意的技巧嗎?

ListView 最基本的用法就是透過 ArrayAdapter,直接顯示文字的列表。

關於這,你可以閱讀 ListView with default ArrayAdapter 範例,他有很清楚的說明。

如果 ListView 中的每個項目,不是單純的文字,要有些變化時,那你一定要寫自己的 ListAdapter。

API Demos 範例集中,就有 ListView with a custom ListAdapter 這個範例,教你如何寫自己的 ListAdapter。

在上面範例中,雖然已經有處理 convertView 這件事。不過當你的 list item 很複雜 (好幾層的 UI hierarchy 或 Views 很多) 時,每次都呼叫 findViewById() 所浪費的時間,還是會讓捲動變的緩慢。要解決這個問題,這篇 Layout Tricks: Creating Efficient Layouts 會教你如何降低你的 UI hierarchy。另外,你也要看一下 ListView with an efficient ListAdapter 這個範例中所使用的技巧。

如果你的 list item 並不複雜,但捲動時還是很鈍鈍的。那我建議你看一下 ListView with lazy-loading 這個範例。

當你的 ListView 中有成千上百個項目時,除了加 android:fastScrollEnabled="true" 可以方便使用者快速捲動外。更好的解決方式是加上按鍵過濾的功能。如何實現按鍵過濾的功能?你可以參考 如何在 ListView 上加上按鍵過濾的功能 這篇文章。

除了 ListView 之外,別忘了還有 ExpandableListView 可用。尤其當你的資料是兩層式架構時,用 ExpandableListView 是不錯的選擇。

同樣的 API Demos 中已經有好幾個範例了,你可以從 ExpandableListView 這個先看起。

最後,如果你的 ListView 想要有自己的背景圖或顏色。那你要看一下 ListView Backgrounds: An Optimization 這篇文章。解法其實非常簡單,但不說你都不會想到原來 android:cacheColorHint 是用在這個地方。另外一個不說你不知道的用法是 setTranscriptMode()。如果你想要讓 ListView 捲到最後一個項目,那你要用 setTranscriptMode(ListView.TRANSCRIPT_MODE_ALWAYS_SCROLL),相關說明你可以參考 Listview Scroll to the end of the list after updating the list

下次我們再來看,用 ListView 時常犯的錯誤有哪些。


繼續閱讀全文...

2009年7月13日 星期一

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

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

鬧鐘的原始程式碼在 這裡

PS: 請不要向我要原始碼,所有Android原始碼都可已自行下載,詳情請見 https://source.android.com/source/building.html

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

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

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

嗯,看起來,這個時鐘就是用 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 給有註冊的程式。

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

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

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

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

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

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

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

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


繼續閱讀全文...