android構建自訂的視圖組件
android提供了精巧和有力的組件化模型構建使用者的UI部分。主要是基於布局類:View和ViewGroup。在此基礎上,android平台提供了大量的預製的View和ViewGroup子類,即布局(layout)和視窗小組件(widget)。可以用它們構建自己的UI。
如果沒有符合你需求的預製視窗小組件,你可以建立自己的視圖子類。如果只是對已存在的視窗小組件或者布局做小的調整,只需繼承該類,覆蓋相關的方法。
建立你自己的View子類可以更精確控制視圖元素的外觀和功能。
- 可建立完整的自訂渲染檢視類型,比如建立一個2d的控制條;
- 可將一組視圖組件合成為一個新的單一組件,比如雙選的列表,選擇省和市;
- 覆蓋EditText組件,比如notepad tutorial中的樣本;
- 捕捉其他事件比如按鍵事件,並執行自訂的處理方式,比如在遊戲中。
基本方法
總的來說,建立自訂的視圖組件步驟是:
- 建立自己的類,繼承已經存在的View類或者子類;
- 覆蓋超類的一些方法。這些超類的方法一般以“on”開頭,比如onDraw()方法等等;
- 使用新建立的擴充類。一旦完成,你的新擴充類就可以用於所有View使用的地方。
注意:擴充類可以定義為內部類,在你建立的Activity類之中。這很有用,因為這樣可以控制外界的訪問,但是這不是必須的,因為你可能需要一個public的自訂View類供更廣泛的使用。
完全自訂群組件
完全自訂的組件可以建立圖形組件顯示在你需要的任何地方。
步驟如下:
- 可以繼承的最通用的視圖類是View,可以繼承它建立自訂的組件超類;
- 可以提供構造方法,並通過xml檔案擷取屬性值和參數;
- 建立自己的事件監聽器,屬性訪問器和編輯器等等;
- 一般情況下會覆蓋onMeasure()方法和onDraw()方法,這會讓組件顯示一些東西。如果都用預設的行為,onDraw()方法不做任何事情,onMeasure()方法設定一個100×100的地區;
- 根據需求覆蓋其他on…方法。
擴充onDraw()和onMeasure()方法
onDraw()方法提供給你一個Canvas對象,在它之上可以實現任何你想要的東西,通過2d圖形api。比如其他標準的後者自訂的組件,風格化的文字後者其他。
注意:這裡不提供3d圖形api的支援。如果你需要3d圖形支援,必須繼承SurfaceView而不是View,並且通過單獨的線程畫圖。可以通過GLSurfaceViewActivity執行個體查看詳細資料。
onMeasure()方法有些麻煩。該方法是在容器和自訂群組件之間渲染的重要部分。該方法覆蓋,要高效率的和精確的報告被包含地區的測量值。
總的來看,實現onMeasure()方法類似如下步驟:
- 調用已經覆蓋的onMeasure()方法,傳遞長和寬規範參數;
- 自訂群組件在onMeasure()方法中計算需要渲染的組件的長和寬,應該在規範參數的範圍內;
- 一旦長和寬計算出來,必須調用setMeasuredDimension(int width, int height)方法,這步失敗會導致異常的拋出。
一個自訂視圖的樣本
自訂視圖的樣本,見:LabelView
該樣本示範了一些自訂群組件的不同方面:
- 繼承View類,用於完全自訂群組件;
- 參數化的構造方法,提供更多的參數,定義在xml檔案中;
- 標準的公開方法,用於設定標籤,比如setText()方法等;
- 覆蓋onMeasure方法確定渲染的組件尺寸;
- 覆蓋onDraw方法,在提供的canvas中畫標籤。
可以找到對樣本的一些使用,在custom_view_1.xml檔案中。
該樣本運行效果:
android樣本是混在一起的,比較亂,我這裡改寫了一下,只有相關樣本的代碼和配置。看起來比較簡單:
http://easymorse.googlecode.com/svn/tags/android.customer.view.demo_1.0
合成控制器
合成控制器,即不是完全自訂一個新的視圖組件,而是,將現有的原子級控制器(控制項?)或者視圖組件組合在一起,處理共同的商務邏輯。比如,一個combo box可以被看做,一個單行的EditText和一個相鄰的按鈕,帶一個彈出列表。
在android中還有很多其他的樣本,比如Spinner,AutoComleteTextView。
建立合成組件的步驟:
- 通常的起始步驟是,建立某種類型的Layout,即建立一個類繼承一個Layout。比如上述的combo box,可能會使用到基於垂直布局的LinearLayout。其他布局也可以嵌套在其中,因此合成組件可以任意複雜結構。和activity類似,你可以用基於xml的聲明方式建立容器組件,也可以嵌入到程式碼中;
- 在新類的構造方法中,得到超類所需的參數,並傳遞給超類的構造方法。另外,也可設定其他在這個心組件當中的視圖組件,比如建立一個EditText和PopupList。注意,你也可以引入自己的參數和屬性到xml檔案中,這樣會被取出並用於你的構造方法;
- 還可以建立事件監聽器,用於容器中的視圖組件,比如一個監聽器方法,用於處理列表點擊的監聽器,更新EditText的常值內容;
- 建立自己的屬性訪問器和編輯器,比如,EditText的值可以在組件中初始設定,並能在需要的時候擷取它的值;
- 在繼承Layout類時,不需要覆蓋onDraw()和onMeasure()方法,因為它們可能已經符合你的要求,當然,也可以覆蓋它們實現自己特定的需求;
- 可能需要覆蓋其他on…方法,比如onKeyDown()方法。
總之,使用Layout作為基礎合成自訂的控制項,有一些優點:
- 可以通過xml檔案的方式聲明指定的布局,和activity類似,或者可以通過編程的方式嵌入到你的代碼中;
- onDraw()方法和onMeasure()等一般可適合需求,因此不必一定要覆蓋它們;
- 可以快速的構建任何複雜的合成視圖,重用它們為一個單一的組件。
合成控制項的樣本
在ApiDemos樣本中,示範了SpeechView,它繼承了LinearLayout,並建立了一個組件,用於顯示談話中的引號。相關的類見:
samples/ApiDemos/src/com/example/android/apis/view/List4.java
samples/ApiDemos/src/com/example/android/apis/view/List6.java
代碼
private class SpeechView extends LinearLayout {
public SpeechView(Context context, String title, String words) {
super(context);
this.setOrientation(VERTICAL);
// Here we build the child views in code. They could also have
// been specified in an XML file.
mTitle = new TextView(context);
mTitle.setText(title);
addView(mTitle, new LinearLayout.LayoutParams(
LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT));
mDialogue = new TextView(context);
mDialogue.setText(words);
addView(mDialogue, new LinearLayout.LayoutParams(
LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT));
}
/**
* Convenience method to set the title of a SpeechView
*/
public void setTitle(String title) {
mTitle.setText(title);
}
/**
* Convenience method to set the dialogue of a SpeechView
*/
public void setDialogue(String words) {
mDialogue.setText(words);
}
private TextView mTitle;
private TextView mDialogue;
}
List4樣本,見:
List6樣本,可以點擊條目,出現內容,見:
修改已存在的檢視類型
如果已存在的視圖組件已經和你的需求相差不遠,你可以只是簡單的擴充該組件,只覆蓋需要改變的行為。
比如樣本中的NotePad應用(platforms/android-1.5/samples/NotePad)。
效果如下:
在文字框視圖組件(EditText)基礎上,增加了橫線。