創建自定義組件 Building Custom Components
Android 提供了一個精致而強大的組件化模式來創建你的用戶界面,基于基礎的布局類:視圖 View 和視圖組 ViewGroup 。平臺包含了多種預定義視圖和視圖組子類 - 分別稱為部件和布局 - 這些可以用來構造你的用戶界面。
一部分可用部件包括按鈕 Button ,文本視圖 TextView ,編輯文本框 EditText ,列表視圖 ListView ,組合框 CheckBox , 單選按鈕 RadioButton , 畫廊 Gallery , 微調器 Spinner , 以及一些用于特定場合的自動補全文本視圖 AutoCompleteTextView , 圖片切換器 ImageSwitcher , 和文本切換器 TextSwitcher .
可用布局有線性布局 LinearLayout , 框架布局 FrameLayout , 相對布局 RelativeLayout , 以及其他。更多例子,參見常用布局對象 Common Layout Objects .
如果這些預定義的部件或布局不能滿足你的需求,你可以創建你自己的視圖類。如果你只需要在現有的部件或布局上做些調整,你只需要子類化這個部件或布局并重寫它的方法。
創建自己的視圖子類讓你可以精確控制界面元素的外形和功能。為了對這種控制有個大概的印象,下面是一些例子說明你可以用它們做什么:
· 你可以創建一個完全自定義繪制的視圖類型,比如一個用 2D 圖形繪制的“音量控制”旋鈕,使它看上去像一個模擬電子件。
· 你可以把一組視圖組件組合進一個單獨的組件里,也許像一個組合框(是彈出列表和自由輸入的文本段的組合),一個雙窗格選擇器控件(一個左窗格和右窗格,里面各有一個列表,你可以選擇哪個項應該在哪個列表中),如此等等。
· 你可以重寫一個編輯文本 EditText 組件的繪制方式(記事本指南 Notepad Tutorial 中使用這個方法創建了一個條紋狀的記事本頁面效果)。
· 你可以捕獲其他事件如按鍵然后以自定義的方式處理。(比如一個游戲所做的那樣)
下面的章節解釋了怎么去創建自定義視圖和在你的應用程序中使用它們。詳細的參考信息,請查看視圖 View 類。
基本方法 The Basic Approach
下面是關于怎么樣開始創建自定義視圖組件的一個概要性的總體描述:
1. 擴展一個現有的視圖類然后子類化它。
2. 重寫父類中的一些方法。這些方法以“ on ”開始,比如 onDraw() , onMeasure() ,和 onKeyDown() 。這和活動或列表活動中為生命周期和其他功能鉤子重寫 on… 事件類似。
3. 使用你的新擴展類。這些完成后,你的擴展類就可以替代那個基礎視圖了。
提示 : 擴展類可以被定義成使用它們的活動的內部類。這樣活動可以控制對它們的訪問,這很有用但并非必須如此(也許你想在應用程序里創建一個使用更廣的公共視圖)。
完全自定義組件 Fully Customized Components
完全自定義組件可以用來創建你想要的圖形組件。也許是一個圖形 VU 表看起來像一個老式的模擬計量器,或者是一個伴唱字幕,上面有一個彈球隨著文字移動這樣你就可以在卡拉 OK 機上跟唱。無論哪種方式,你想要的東西都是內置組件所不能完成的,不管你怎么組合它們。
幸運的是,你可以簡單的按照你的意愿來創建組件,除非你想不到,或者受限于屏幕尺寸,以及可用電源(記住,最終你的應用程序得運行在比桌面工作站電源少得多的設備上)。
要創建一個完全自定義組件:
1. 毫無意外,你可以擴展的最通用的視圖是 View , 因此你通常從擴展它開始創建你的超級組件。
2. 你可以供應一個構造器從 XML 中讀取屬性和參數,你也可以消費你自己的屬性和參數(也許是 VU 表的顏色和范圍,或者指針的寬度和阻尼,等)
3. 你可能也想在你的組件中創建自己的事件偵聽器,屬性訪問和修改器,以及其它可能更復雜的行為。
4. 你將幾乎肯定要重寫 onMeasure() 而且也很可能需要重寫 onDraw() 。 如果你希望這個組件顯示一些東西。兩者都有缺省行為,缺省的 onDraw() 方法不做任何事情,而缺省 onMeasure() 方法將總是設置一個 100x100 的尺寸 - 這或許不是你想要的。
5. 其他 on... 方法也可以按照要求重寫。
擴展 onDraw() 和 onMeasure() Extend onDraw() and onMeasure()
onDraw() 方法傳給你一個畫布 Canvas 對象,你可以在上面實現任何你想要的東西: 2D 圖形,其它基礎或自定義組件,風格文本,或其他任何你能想到的。
注意 : 這不適用于 3D 圖形。如果你想使用 3D 圖形,你必須擴展 SurfaceView 而不是 View ,并且從一個單獨的線程中繪制。參見 GLSurfaceViewActivity 示例以了解更多細節。 .
onMeasure() 會被用得更多一點。 onMeasure() 是你的組件和它的容器之間繪制約定的關鍵部分。 onMeasure() 應該被重寫來有效和準確的報告它所包含部分的尺寸。但是由于父類的一些限制性要求(被傳遞給 onMeasure() 方法)和以寬度和高度(一旦已經被計算出來)調用 setMeasuredDimension() 方法的要求,這將變得稍微復雜一些。如果你從一個重寫的 onMeasure() 方法中調用這個方法失敗,結果將返回一個測量時異常。
概要而言,實現 onMeasure() 看起來如下:
1. 給定寬度和高度測量規格 ( widthMeasureSpec 和 heightMeasureSpec ,兩者都是代表維度的整數編碼 ) 來調用重寫的 onMeasure 方法,這應該被當作度量上的限制性要求。完整的參考在 View.onMeasure(int, int) 文檔中,這篇參考文檔還很好的描述了整個測量操作)。
2. 你的組件的 onMeasure() 方法應該計算一個寬度和高度用來繪制這個組件。它應該盡可能留在傳遞的規格所指定的范圍里,盡管它可以選擇超出它們(這樣的話,父類可以選擇處理方式,包括裁剪,滾動,拋出異常,或者要求 onMeasure() 再試一次,也許會使用不同的度量規格。)
3. 一旦寬度和高度被計算出來,這個 setMeasuredDimension(int width, int height) 方法必須以計算出來的度量來調用。如果調用不成功,則將拋出異常。
下面是框架在視圖上調用的一些其他基本方法的匯總:
類別 Category |
方法 Methods |
描述 Description |
創建 |
構造器 |
有一種構造器形式是在從代碼里創建視圖時被調用,另一種是從一個布局文件中擴充視圖時被調用。第二種形式應該解析并運用任何定義在布局文件中的屬性。 |
當一個視圖及其所有子項已經在 XML 中擴充好時被調用。 |
||
布局 |
用來決定這個視圖及其所有子項的尺寸要求。 |
|
當這個視圖應該為它所有的子項分配一個尺寸和位置的時候調用。 |
||
當這個視圖的尺寸被改變時被調用。 |
||
繪畫 |
當視圖需要繪制其內容時被調用。 |
|
事件處理 |
當一個按鍵事件發生時被調用。 |
|
當一個按鍵釋放事件發生時調用。 |
||
當一個跟蹤球動作事件發生時被調用。 |
||
當一個觸摸屏動作事件發生時被調用。 |
||
Focus |
當視圖獲取或丟失焦點時被調用。 |
|
當包含視圖的窗口獲取或丟失焦點時被調用。 |
||
Attaching |
當視圖被附著到一個窗口時被調用。 |
|
當視圖從一個窗口拆分開時被調用。 |
||
當包含視圖的窗口的可見性發生改變時被調用。 |
一個自定義視圖示例 A Custom View Example
這個在 API Demos 中的 CustomView 例子提供了自定義視圖的示范。這個自定義視圖定義在 LabelView 類中。
LabelView 例子說明了自定義組件的很多不同方面:
· 為一個完全自定義組件擴展視圖類。
· 參數化的構造器,采用視圖擴充參數(這些參數定義在 XML 中)。其中一些被傳遞給視圖超類,但更重要的是,有一些自定義屬性被 LabelView 所用。
· 基本的公共方法來設置一個 label 組件你所期望的類型,比如 setText(), setTextSize(), setTextColor() 等等。
· 一個重寫的 onMeasure 方法來決定和設置這個組件的繪制尺寸。(注意在 LabelView 中,實際工作是通過一個私有的 measureWidth() 方法來完成的。)
· 一個重寫的 onDraw() 方法來把標簽( label )畫到提供的畫布 canvas 中。 method to draw the label onto the provided canvas.
你可以在 custom_view_1.xml 中查閱一些使用范例。特別是,你可以看到 android: 命名空間參數和自定義 app: 命名空間參數的一個混合。這些 app: 參數是 LabelView 認識和使用的,并被定義在 R 資源類的一個可格式化的內部類里。
復合控件 Compound Controls
如果你不想創建一個完全自定義的組件,但希望由一組已有控件來組裝成一個可復用組件,那么創建一個復合組件(或復合控件)可以滿足要求。在一個小容器里,把一些更原子的控件(或視圖)整合進一個邏輯項目組中,從而可以被當作單個控件來對待。比如,一個組合框,可以被看作是一個單行文本編輯控制和一個附有彈出列表的相鄰按鈕的組合。如果你按下這個按鈕并從列表中選擇一些項,它將被生成到文本編輯域中,不過用戶也可以直接在文本編輯域進行輸入如果愿意的話。
在 Android 里,事實上有另外兩種視圖也是這樣做的: Spinner 和 AutoCompleteTextView ,不過,組合框的概念比較容易理解。
為了創建一個復合組件:
1. 通常的起點是某種類型的布局,因此創建一個擴展布局的類。可能在組合框例子中,我們會使用一個水平方向的線性布局 LinearLayout 。記住其它布局可以被嵌套在里面,這樣這個復合組件可以任意復雜和結構化。注意就像一個視圖,你既可以使用聲明(基于 XML )方式來創建這個包含的組件,也可以通過編碼在程序中嵌入它們。
2. 在這個新類的構造函數中,采用任何超類期望的參數,并首先傳遞給超類構造函數。然后你可以建立用于新組件的其它視圖;這就是你將創建 EditText 域和 PopupList 的地方。注意你還可能需要引入你自己的屬性和參數到 XML 中,這些將被提取出來并為你的構造函數所用。
3. 你還可以為包含的視圖可能產生的事件創建偵聽器,比如,一個列表項點擊偵聽器的方法,用來在一個列表項被選中時更新相應的文本編輯框內容。
4. 你可能還想通過訪問和修改函數來創建自己的特性,比如,允許 EditText 值可以被初始化并在需要時被查詢到。
5. 在擴展一個布局時,你不需要重寫 onDraw() 和 onMeasure() 方法,因為布局會有缺省行為。不過,如果需要的話,你同樣可以重寫它們。
6. 你可能會重寫其它 on... 方法,比如 onKeyDown() ,當一個特定鍵被按下時可能會從彈出列表中選擇某個特定的默認值。
總之,基于布局創建自定義控件有如下一些好處,包括:
· 你可以像活動屏幕一樣使用聲明性的 XML 來指定布局,或者通過編程來實現視圖嵌套。
· onDraw() 和 onMeasure() 方法(加上大多數其它 on... 方法)將可能有合適的行為,這樣你就不需要重寫它們。
· 最后,你可以很快地構建任意復雜的復合視圖以及把它們當作單個組件來重用。
復合控件的例子 Examples of Compound Controls
在隨 SDK 發布的 API Demos 項目中,有兩個列表例子 - 例 4 和例 6 ,在 Views/Lists 下面,演示了一個從線性布局擴展的 SpeechView ,該組件用來顯示演講摘錄。相應的類在 List4.java 和 List6.java 里。
修改一個已有視圖類型 Modifying an Existing View Type
在某些情況下有更簡單實用的方法來創建自定義視圖。如果有一個視圖已經和你想要的很相似,你只需要在這個組件上直接擴展并重寫相關行為方法即可。通過完全自定義的組件你當然可以做任何事,不過通過一個視圖層次圖中的特定類,你也一樣可以做很多事來滿足需求。
比如, SDK 在這些例子中包含了一個記事本應用程序 NotePad application 。該程序演示了 Android 平臺應用的很多方面,其中之一就是擴展一個 EditText 視圖來創建一個條紋記事本。這并非一個完美的例子,所使用的 APIs 可能有所變化,但它的確說明了原理。
如果你沒有這么做過,導入記事本例子到 Eclipse 中。特別是看一下 NoteEditor.java 文件中的 MyEditText 定義。
下面是一些值得注意的地方:
1. 定義 The Definition
這個類以下面的代碼行定義:
public static class MyEditText extends EditText
o 它被定義為 NoteEditor 活動的內部類,但它是公共的,因此它可以在 NoteEditor 類之外以 NoteEditor.MyEditText 來訪問。
o 它是靜態的,這意味著它不產生允許它從父類中訪問數據的所謂的“偽方法”,這就意味著它的行為更像一個獨立的類而不是和 NoteEditor 有著很強的關聯。這是一個用來創建內部類的簡潔方式,如果這些類不需要從外部類中訪問狀態,這使得產生的類小巧且很容易從被其它類所使用。
o 它擴展了 EditText ,我們選擇基于這個視圖來自定義組件。這些結束后,新的類將能夠被用來代替通常的 EditText 視圖。
2. 類初始化 Class Initialization
通常,先調用超類。此外,這并非缺省構造器,而是一個參數化的構造器。這個 EditText 通過擴充在 XML 布局文件中的這些參數創建,這樣,我們的構造器同樣需要采用它們并傳遞給超類構造器。
3. 重寫方法 Overridden Methods
這個例子中,只有一個方法需要被重寫: onDraw() - 但是當創建你自己的組件時,可能需要重寫其它方法。
對于記事本 NotePad 例子,重寫 onDraw() 方法可允許我們在 EditText 視圖畫布上繪制藍線(這個畫布 canvas 被傳給這個重寫的 onDraw() 方法)。這個 super.onDraw() 方法在這個方法結束前被調用。這個超類方法應該被調用,但在這個例子里,我們是在畫好線后才調用它。
4. 使用自定義組件 Use the Custom Component
現在我們已經有了自定義組件,但我們該怎么用它呢?在記事本 NotePad 例子中,這個自定義組件直接在布局聲明中使用,所以看一下 res/layout 目錄下的 note_editor.xml 文件。
<view
class="com.android.notepad.NoteEditor$MyEditText"
id="@+id/note"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="@android:drawable/empty"
android:padding="10dip"
android:scrollbars="vertical"
android:fadingEdge="vertical" />
o 這個自定義組件在 XML 中以一個通用視圖創建,而這個類用包全名來指定。還需要注意到這個我們定義的內部類通過 NoteEditor$MyEditText 標記來引用,這在 Java 編程語言中是一個引用內部類的標準方式。
如果你的自定義視圖組件不是定義為一個內部類,那么你可以,有選擇的,以 XML 元素名來聲明這個視圖組件,并且不需要包含 class 屬性。比如:
<com.android.notepad.MyEditText
id="@+id/note"
... /> <span
發表評論
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元

評論