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元件在畫面上的顯示方式。 記事本範例應用程式使用此方法來建立效果良好的劃線型記事本頁面。 - 您可以擷取按下按鍵之類的其他事件,並以自訂方式進行處理,例如在遊戲中。
以下章節說明如何建立自訂檢視區塊,並在應用程式中使用。如需詳細的參考資訊,請參閱 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時,通常不需要這麼做,因為版面配置的預設行為可能運作正常。 -
您可以視需要覆寫其他
on方法,例如onKeyDown(),這樣當輕觸特定按鍵時,可以在下拉式方塊的彈出式清單中選擇某些預設值。
將 Layout 用作自訂控制項的基礎有以下優點:
- 您可以使用宣告式 XML 檔案指定版面配置,方式與活動畫面類似;或者,您也可以透過程式化方式建立檢視區塊,並利用程式碼將其嵌入版面配置中。
-
onDraw()和onMeasure()方法,以及大多數其他on方法,都有適用的行為,因此您不必覆寫這些方法。 - 您可以快速建構任何複雜程度的複合檢視區塊,並將其視為單一元件重複使用。
修改現有檢視區塊類型
如果元件與您想要的效果十分接近,只需擴充元件並覆寫想要變更的行為即可。您能夠對完全自訂元件執行的所有操作,都可以在現有元件上執行,但是從 View 階層中更專業的類別入手,您還可以免費獲得一些行為,它們可能正好能夠滿足您的需求。
舉例來說,記事本範例應用程式示範了使用 Android 平台的許多方面。其中一個就是擴充 EditText 檢視區塊,以製作劃線的記事本。這並不是一個最佳範例,而且用於執行此操作的 API 可能會變更,但這個範例表明瞭基本原則。
如果您還沒有這樣做,請將記事本範例匯入 Android Studio,或使用提供的連結查看來源。請特別參閱 NoteEditor.java 檔案中 LinedEditText 的定義。
在這個檔案中,需要留意以下事項:
-
定義
該類別的定義如下:
public static class LinedEditText extends EditTextLinedEditText定義為NoteEditor活動中的內部類別,但對外公開,因此可從NoteEditor類別外部以NoteEditor.LinedEditText身分存取該類別。此外,
LinedEditText是static,也就是說,它不會產生所謂的「合成方法」,讓它能存取父類別中的資料。這表示它會以獨立類別的形式運作,而非與NoteEditor強烈相關。如果內部類別不需要存取外部類別的狀態,則可以使用這種更簡潔的方法來建立內部類別。使產生的類別更小,並允許其他類別輕鬆使用它。LinedEditText會擴充EditText,也就是本範例中要自訂的檢視區塊。完成後,新類別就能替代常規的EditText檢視區塊。 -
類別初始化
如同往常,系統會先呼叫父類別。這不是預設建構函式,而是參數化建構函式。當從 XML 版面配置檔案加載
EditText時,使用這些參數建立了EditText。因此,建構函式必須擷取這些參數,並將其傳遞至父類別建構函式。 -
已覆寫的方法
這個範例僅覆寫
onDraw()方法,但在您建立自訂元件時,可能需要覆寫其他方法。在這個範例中,覆寫
onDraw()方法可讓您在EditText檢視區塊畫布上繪製藍線。畫布會傳遞至覆寫的onDraw()方法。在方法結束前,系統會呼叫super.onDraw()方法。必須叫用父類別方法。在本範例中,我們會在繪製想要包含的行後再執行此操作。 -
自訂元件
現在,我們擁有自訂元件了,但該怎麼使用呢?在記事本範例中,會直接從宣告式版面配置使用自訂元件,因此請查看
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 方法,並導入自己的輔助方法,大幅自訂屬性和行為。唯一的限制就是您的想像力,以及元件需要執行的操作。