Android 會根據基本的版面配置類別 View
和 ViewGroup
,提供複雜且強大的元件化模型來建構 UI。平台包含各種預先建構的 View
和 ViewGroup
子類別,分別稱為小工具和版面配置,可用來建構 UI。
部分可用小工具清單包括 Button
、TextView
、EditText
、ListView
、CheckBox
、RadioButton
、Gallery
、Spinner
,以及專用 AutoCompleteTextView
、ImageSwitcher
和 TextSwitcher
。
可用的版面配置包括 LinearLayout
、FrameLayout
、RelativeLayout
等。如需更多範例,請參閱「常見版面配置」。
如果所有預先建構的小工具或版面配置都不符合您的需求,您可以自行建立 View
子類別。如果您只需要稍微調整現有的小工具或版面配置,可以將小工具或版面配置加入子類別,並覆寫其方法。
建立自己的 View
子類別可讓您精確控管畫面元素的外觀和功能。以下舉例說明自訂檢視區塊的功用,有助您瞭解自訂檢視區塊的功能:
-
您可以建立完全自訂的
View
類型,例如使用 2D 圖形算繪的「音量控制」旋鈕,類似類比電子控制項。 -
您可以將一組
View
元件合併成一個新的單一元件,像是建立組合方塊 (包含彈出式清單和任意輸入文字欄位的組合)、雙窗格選取器控制項 (左右窗格,每個窗格內均含有清單,可讓您重新指派該清單所屬的項目),以此類推。 -
您可以覆寫
EditText
元件在畫面上的算繪方式。在 NotePad 範例應用程式中,我們可以運用這項特效建立內嵌筆記本頁面。 - 您可以擷取其他事件 (例如按鍵操作),並以自訂方式處理,例如遊戲。
以下章節將說明如何建立自訂檢視畫面,並在應用程式中使用。如需詳細的參考資訊,請參閱 View
類別。
基本方法
以下是建立自己的 View
元件的重點總覽:
-
使用您自己的類別擴充現有的
View
類別或子類別, -
覆寫父類別中的某些方法。要覆寫的父類別方法開頭是
on
,例如onDraw()
、onMeasure()
和onKeyDown()
。這與Activity
或ListActivity
中的on
事件類似,您可以覆寫生命週期和其他功能掛鉤。 - 使用新的擴充功能類別。完成後,您可以使用新的擴充功能類別取代其基於的檢視畫面。
完全自訂元件
您可以建立完全自訂的圖形元件,以您想要的方式顯示。您可能會希望圖形 VU 計量器看起來像是舊式的類比量表,或者是跟唱文字檢視區塊,當您邊唱卡拉 OK 機器時,彈跳球會隨著字詞移動。您可能會希望內建元件無法執行某些操作,無論您採用的方式為何。
幸好,您可以建立具有您所需外觀和行為的元件,而僅限於您的想像力、螢幕尺寸以及可用的處理能力。請注意,應用程式可能需要在耗電量遠比桌面工作站上執行。
如要建立完全自訂的元件,請考慮下列幾點:
-
可擴充的一般檢視區塊是
View
,因此通常您可以先擴充該檢視區塊來建立新的超級元件。 - 您可以提供建構函式,其中包含來自 XML 的屬性和參數,以及使用自己的屬性和參數,例如 VU 計量器的顏色和範圍,或是針的寬度和阻斷。
- 您可能會想要在元件類別中建立自己的事件監聽器、屬性存取子和修飾符,以及更複雜的行為。
-
您幾乎當然會想要覆寫
onMeasure()
,但如果您希望元件顯示某些內容,可能也需要覆寫onDraw()
。雖然兩者都有預設行為,但預設的onDraw()
不會執行任何動作,而預設的onMeasure()
一律會設定 100x100 的大小,這您可能不想要這個大小。 -
您還可以視需要覆寫其他
on
方法。
擴充 onDraw() 和 onMeasure()
onDraw()
方法提供 Canvas
,您可在上面實作任何所需項目,例如 2D 圖形、其他標準或自訂元件、樣式化文字,或是您能想到的任何項目。
onMeasure()
有點複雜。onMeasure()
是元件及其容器之間轉譯合約的重要環節。必須覆寫 onMeasure()
,才能有效且準確地回報所含零件的測量結果。這會因父項的限制要求 (傳遞至 onMeasure()
方法) 稍複雜,而後者需要在計算後,使用測量的寬度和高度呼叫 setMeasuredDimension()
方法。如果您未從覆寫的 onMeasure()
方法呼叫此方法,會在測量時產生例外狀況。
大致來說,實作 onMeasure()
看起來會像這樣:
-
系統會以寬度和高度規格呼叫覆寫的
onMeasure()
方法,系統會將其視為您產生的寬度和高度測量結果設有限制的要求。widthMeasureSpec
和heightMeasureSpec
參數都是代表尺寸的整數代碼。如需這些規格要求的完整限制清單,請參閱「View.onMeasure(int, int)
」的參考說明文件。本參考說明文件也會說明整個測量作業。 -
元件的
onMeasure()
方法會計算測量寬度和高度,這是轉譯元件所需的項目。即使超過傳遞規格,仍必須盡量避免超過傳遞規格。在這種情況下,父項可以選擇要執行的操作,包括裁剪、捲動、擲回例外狀況,或要求onMeasure()
再試一次,這可能需要採用不同的測量規格。 -
計算寬度和高度時,請使用計算出的測量結果呼叫
setMeasuredDimension(int width, int height)
方法。否則就會發生例外狀況。
以下摘要列出架構在檢視區塊上呼叫的其他標準方法:
類別 | 方法 | 說明 |
---|---|---|
創作 | 建構函式 | 透過程式碼建立檢視畫面時,系統會呼叫一種形式的建構函式;當透過版面配置檔案加載檢視畫面時,系統會呼叫一種形式。第二種形式會剖析並套用版面配置檔案中定義的屬性。 |
|
在從 XML 加載檢視區塊及其所有子項之後呼叫。 | |
版面配置 |
|
呼叫此項以確定該檢視區塊及其所有子項的大小要求。 |
|
當這個檢視區塊必須將大小和位置指派給其所有子項時呼叫。 | |
|
當這個檢視區塊的大小變更時呼叫。 | |
繪圖 |
|
當檢視區塊必須轉譯其內容時呼叫。 |
事件處理 |
|
在發生按鍵向下事件時呼叫。 |
|
在發生按鍵抬起事件時呼叫。 | |
|
發生軌跡球動作事件時呼叫。 | |
|
在觸控螢幕動作事件發生時呼叫。 | |
焦距 |
|
當檢視區塊獲得或失去焦點時呼叫。 |
|
當包含檢視畫面的視窗獲得或失去焦點時呼叫。 | |
附加 |
|
當檢視區塊附加至視窗時呼叫。 |
|
當檢視區塊從視窗卸離時呼叫。 | |
|
當包含檢視區塊的視窗可見性變更時呼叫。 |
複合控制項
如果您不想建立完全自訂的元件,而是希望將一組現有控制項納入可重複使用的元件,那麼建立複合元件 (或複合控制項) 可能是最合適的做法。總而言之,這會將一些不可分割的控制項或檢視畫面整合成一個邏輯項目群組,也就是可視為單一項目的項目。舉例來說,組合方塊可以結合單行 EditText
欄位和帶有彈出式清單的相鄰按鈕組合。當使用者輕觸按鈕並從清單中選取內容時,系統就會填入 EditText
欄位,但使用者如果有需要,也可以直接在 EditText
中輸入文字。
在 Android 中,還有兩種檢視畫面可以用來執行此操作:Spinner
和 AutoCompleteTextView
。無論如何,這個組合方塊的概念就是個很好的例子。
如要建立複合元件,請按照下列步驟操作:
-
和
Activity
一樣,請採用宣告式 (基於 XML) 方法建立內含的元件,或是透過程式輔助方式使用程式碼建立元件。常見的起點是某種類型的Layout
,因此請建立擴充Layout
的類別。如果是下拉式方塊,您可以使用水平方向的LinearLayout
。您可以在內部建立其他版面配置的巢狀結構,因此複合元件可以任意複雜且結構化。 -
在新類別的建構函式中,擷取父類別預期使用的任何參數,並先將其傳遞至父類別建構函式。接著,您可以設定要在新元件中使用的其他檢視畫面。您可以在這裡建立
EditText
欄位和彈出式清單。建議您在 XML 中導入自己的屬性和參數,以供建構函式提取及使用。 -
或者,您也可以為內含的檢視區塊可能產生的事件建立事件監聽器。一個例子,清單項目點擊事件監聽器是一種事件監聽器方法,可在選取清單時更新
EditText
的內容。 -
您可以選擇使用存取子和修飾符建立自己的屬性。例如,讓元件一開始就設定
EditText
值,並視需要查詢其內容。 -
視需要覆寫
onDraw()
和onMeasure()
。擴充Layout
時,通常不必進行這項操作,因為版面配置預設行為應該都能正常運作。 -
視需要覆寫
onKeyDown()
等其他on
方法,例如在輕觸特定按鍵時,從下拉式方塊的彈出式清單中選擇特定預設值。
使用 Layout
做為自訂控制項的基礎有很多好處,包括:
- 您可以使用宣告式 XML 檔案指定版面配置,就像使用活動畫面一樣;也可以用程式輔助方式建立檢視畫面,然後從程式碼中以巢狀方式將其嵌入版面配置中。
-
onDraw()
和onMeasure()
方法,以及大多數其他on
方法都有合適的行為,因此您不必覆寫這些方法。 - 您可以快速建構任意複雜的複合檢視畫面,並將這些檢視畫面當做單一元件重複使用。
修改現有檢視畫面類型
如果有元件與所需元件類似,您可以擴充該元件,並覆寫想變更的行為。您可以利用完全自訂元件執行所有操作,但先在 View
階層中使用更專業的類別,就可以免費獲得某些行為。
舉例來說,NotePad 範例應用程式示範了使用 Android 平台的許多層面。其中之一是擴充 EditText
檢視區塊,以建立內嵌記事。這並不是一個完美的範例,而且執行此操作的 API 可能會改變,但示範了這些原則。
如果您還沒有將 NotePad 範例匯入 Android Studio,或透過提供的連結查看來源,請這麼做。請特別參閱 NoteEditor.java
檔案中 LinedEditText
的定義。
在這個檔案中,需要留意以下事項:
-
定義
該類別的定義如下:
public static class LinedEditText extends EditText
LinedEditText
定義為NoteEditor
活動中的內部類別,但會對外公開,因此可以從NoteEditor
類別外部以NoteEditor.LinedEditText
形式存取。此外,
LinedEditText
為static
,表示它不會產生所謂的「合成方法」,供其存取父項類別的資料。這表示其行為為個別類別,而不是與NoteEditor
密切相關的類別。如果內部類別不需要從外部類別存取狀態,這是建立內部類別更為簡潔的方法。這會使產生的類別較小,且可輕鬆與其他類別搭配使用。LinedEditText
會擴充EditText
,這是在本例中要自訂的檢視畫面。完成後,新類別可以取代一般的EditText
檢視畫面。 -
類別初始化
如同往常,系統會先呼叫父類別。這不是預設建構函式,但它屬於參數化的建構函式。
EditText
從 XML 版面配置檔案加載時,會使用這些參數建立。因此,建構函式必須擷取這些變數,並傳遞至父類別建構函式。 -
已覆寫的方法
這個範例只會覆寫
onDraw()
方法,但您可能需要在建立自訂元件時覆寫其他方法。在這個範例中,覆寫
onDraw()
方法可讓您在EditText
檢視畫面畫布上繪製藍線。畫布會傳遞至覆寫的onDraw()
方法。系統會在方法結束前呼叫super.onDraw()
方法。必須叫用父類別方法。在此情況下,請在繪製要包含的線條後,在末端叫用此項目。 -
自訂元件
現在您已擁有自訂元件,但要如何使用呢?在 NotePad 範例中,系統會直接從宣告式版面配置使用自訂元件,因此請查看
res/layout
資料夾中的note_editor.xml
:<view xmlns:android="http://schemas.android.com/apk/res/android" class="com.example.android.notepad.NoteEditor$LinedEditText" android:id="@+id/note" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@android:color/transparent" android:padding="5dp" android:scrollbars="vertical" android:fadingEdge="vertical" android:gravity="top" android:textSize="22sp" android:capitalize="sentences" />
自訂元件會建立為 XML 中的一般檢視區塊,並使用完整套件指定類別。系統會使用
NoteEditor$LinedEditText
標記法參照您定義的內部類別,這是在 Java 程式設計語言中參照內部類別的標準方法。如果自訂檢視區塊元件並未定義為內部類別,您可以使用 XML 元素名稱宣告檢視區塊元件,並排除
class
屬性。例如:<com.example.android.notepad.LinedEditText id="@+id/note" ... />
可以看到,
LinedEditText
類別現已成為獨立的類別檔案。如果類別以巢狀形式在NoteEditor
類別中,這項技術無法運作。定義中的其他屬性和參數是傳遞至自訂元件建構函式,然後傳遞至
EditText
建構函式,因此它們與用於EditText
檢視畫面的參數相同。您也可以新增自己的參數。
建立自訂元件也依需求那麼複雜。
更複雜的元件可以覆寫更多 on
方法,並導入其自己的輔助方法,大幅自訂屬性和行為。唯一的限制是您的想像力,以及元件需要執行的操作。