Android 用戶界面---定制組件(Custom Components)
基于布局類View和ViewGroup的基本功能,Android為創建自己的UI界面提供了先進和強大的定制化模式。首先,平臺包含了各種預置的View和ViewGroup子類---Widget和layout,可以使用它們來構造自己的UI界面。
部分的可以利用的widget包括:Button、TextView、EditText、ListView、CheckBox、RadioButton、Gallery、Spinner、以及比較特殊用途的AutoCompleteTextView、ImageSwitcher和TextSwitcher。
其中可利用的布局是:LinearLayout、FrameLayout、RelativeLayout以及其他的布局。更多的例子請看“共通布局對象”。http://developer.android.com/guide/topics/ui/layout-objects.html
如果遇到了沒有預置的widget或layout的需求,可以創建自己的View子類。如果只需要對既存的widget或layout進行小的調整,那么只需簡單的繼承widget或layout,并且重寫它們的方法。
創建自己的View子類,以便能夠精準的控制屏幕元素的外觀和功能。以下是用定制View對象來實現這種控制想法的一些例子:
1. 創建一個完全的定制化渲染的View類型,如用類似模擬電子控制的2D圖形來渲染的音量控制按鈕。
2. 把一組View組件組合成一個新的單一組件,制作一些像ComboBox(一個下拉列表和文本輸入域的組合)、雙面板選擇器(左右兩個列表面板,右邊的列表面板中的項目與左邊列表面板中的一個項目相關聯)等組件。
3. 重寫一個EditText組件在屏幕上的渲染的方法。
4. 捕獲一些像按鍵一樣的事件,并在某些定制的方法中處理它們(如游戲)。
基本方法
以下是創建自定義View組件需要要了解基本概要:
1. 自定義的View類要繼承一個既存的View類或其子類;
2. 在子類重寫父類的一些方法。要覆寫的父類方法是用‘on’開頭的,例如,onDraw()、onMeasure()和onKeyDown()等,這有點類似于重寫Activity或ListActivity的生存周期回調的on…事件。
3. 使用新的擴展類,一旦完成,新擴展的類就能被用于替換基本的View對象。
提示:擴展類能夠作為使用它們的Acticity的內部類來定義。這樣對控制對它們的訪問是有益的,當然可以創建一個新的公共的View類,這樣就可以在應用程序范圍內來使用。
完全定制化的組件
完全定制化的組件能夠用于創建你所期望的顯示效果的圖形化組件。可以是看上去像舊的模擬儀表的圖形化VU儀表,或者是一個長的歌詞視圖,有一個跳動的球沿著歌詞移動,以便跟著這卡拉OK機歌唱,這兩種情況,無論如何組織內置的組件都無法滿足要求。
幸運的是,能夠使用任意自己喜歡的方法來創建組件的外觀和行為,唯一的限制就是你的想象力、屏幕的尺寸和可利用的處理能力(因為應用程序最終可能運行在比桌面工作站處理能力要弱的設備上)。
以下是創建完全定制組件的步驟:
1. 毋庸置疑,能夠擴展的最通用的視圖是View類,因此通常是繼承這個View類來創建自己的新的組件;
2. 提供一個能夠從XML中獲取屬性和參數的構造器,并且也能夠使用自己屬性和參數(如VU儀表的顏色和范圍,指針的寬度和阻尼等);
3. 創建組件中可能的事件監聽器、屬性訪問器和修飾符以及盡可能準確的行為等;
4. 覆寫onMeasure()回調方法,如果想要組件顯示一些東西,也要覆寫onDraw()回調。雖然它們都有默認的行為,onDraw()回調默認什么也不做,onMeasure()方法默認的要設置組件的尺寸為100x100;
5. 覆寫其他的需要on…方法。
擴展onDraw()和onMeasure()
onDraw()方法會把能夠實現的任何想要的東西放到一個Canvas對象上,如2D圖形、標準或定制的組件、樣式化的文本、或其他任何能夠想到的東西。
注意:View類不能使用3D圖形。如果要使用3D圖形,必須繼承SurfaceView類,而不是View類,并且要在一個獨立的線程中描畫。
onMeasure()方法有點復雜,它是組件和它的容器之間的渲染約束的關鍵部分。覆寫onMeasure(),以便準確高效的報告組件被包含部分的尺寸。由于來自父容器限制的要求,使得尺寸的測量有些復雜,并且組件的尺寸一旦被計算完成,就要調用setMeasureDimension()方法來保存測量的寬度和高度。如果在onMeasure()方法中調用setMeasureDimension()方法失敗,這個結果在測量時將是一個異常的值。
在上層看,實現onMeasure()方法的步驟如下:
1. 要用父容器的寬度和高度的計量規格來調用被覆寫的onMensure()方法(widthMeasureSpec和heightMeasureSpec參數都是代表了尺寸的整數),這兩個參數應該作為生成組件的寬度和高度的約束要求。對于這些規格約束類型的完整說明可以在View類說明的View.onMeasure(int,int)方法中找到。
2. 組件的onMeasure()方法應該計算用于渲染組件所需的尺寸(寬度和高度)。組件應該盡量保留在被傳入的規格范圍內,盡管它能夠選擇超出規格范圍(在這種情況下,父容器能夠選擇做的事情包括:裁剪、滾動、拋出異常、或者要求onMeasure()方法用不同的尺寸規格再試)。
3. 一旦組件的寬度和高度被計算完成,就必須調用setMeasuredDimension(int width, int height)方法來保存計算結果。不這樣做就會拋出一個異常。
下表是framework調用View類的其他標準方法:
分類 |
方法 |
說明 |
Creation |
Constructors |
構造器的調用有兩種類型:1.在代碼中創建View對象;2.用布局文件填充View對象。第二種類型應該解析和應用布局文件中的任何屬性定義。 |
onFinishInflate() |
View對象和它的所有子對象都用XML填充完之后,調用這個方法。 |
|
Layout |
onMeasure(int, int) |
調用這個方法決定View對象及其所有子對象的尺寸要求。 |
onLayout(boolean,int,int,int,int) |
當View對象給它的所有子對象分配尺寸和位置時,調用這個方法。 |
|
onSizeChanged(int,int,int,int) |
當View對象的尺寸發生改變時,調用這個方法。 |
|
Drawing |
onDraw(Canvas) |
當View對象渲染它的內容時,調用這個方法。 |
Event |
onKeyDown(int,KeyEvent) |
當一個鍵的按下事件發生時,調用這個方法 |
onKeyUp(int,KeyEvent) |
當一個鍵彈起事件發生時,調用這個方法 |
|
onTrackballEvent(MotionEvent) |
當鼠標軌跡球滾動事件發生時,調用這個方法。 |
|
onTouchEvent(MotionEvent) |
當觸屏事件發生時,調用這個方法。 |
|
Focus |
onFocusChanged(boolean,int,Rect) |
當View對象獲取或失去焦點時,調用這個方法。 |
onWindowFocusChanged(boolean) |
當包含View對象的窗口獲得或失去焦點時,調用這個方法。 |
|
Attaching |
onAttachedToWindow() |
當View對象被綁定到一個窗口時,調用這個方法。 |
onDetachedFromWindow() |
當View對象被從它的窗口中分離的時候,調用這個方法。 |
|
onWindowVisibilityChanged(int) |
當包含View對象的窗口的可見性發生改變時,調用這個方法。 |
|
|
|
|
定制View的例子
在API Demos中提供了一個定制的View對象的例子:CustomView。這個定制的View定義在LabelView類中。
LabelView示例展示了很多定制組件的不同特征:
1. 繼承View類的完全定制化的組件;
2. 參數化的帶有View填充參數(在XML中定義的參數)方式構造View對象。有一些填充參數使用通過這個View的父類傳遞過來的,還有一些用于labelView對象而定義的定制的屬性;
3. 你所期望看到的標準的公共類型的方法,如setText()、setTextSize()、setTextColor()等等;
4. 一個重寫的onMeasure()方法,它決定和設置了組件的渲染尺寸。(注意:在LabelView類中,實際的工作是由一個私有的measureWidth()方法來做的。)
5. 一個重寫的onDraw()方法,它在提供的Canvas上描畫標簽。
從這個示例的custom_view_1.xml中,能夠看到一些LabelView定制View的用法。實際上,可以看到android:命名空間參數和定制的app:命名空間的組合。這些app:參數是LabelView類所承認的并用于工作的一些定制化的屬性,并且這些參數在示例的R資源定義類的styleable內部類中被定義。