自定義組件

自定義組件

Android提供了一套複雜而強大的組件化模型來建立你自己的UI。其依賴於基本的佈局類:View和ViewGroup。首先,這個平臺提供了各種各樣的View和ViewGroup的子類——被稱作窗口小部件(widgets)和佈局(layout),你可以自由的使用它們來創建你自己的UI。

​ 部分可用的窗口小部件如下:Button, TextView, EditText, ListView, CheckBox, RadioButton, Gallery, Spinner和專用的AutoCompleteTextView, ImageSwitcher, 和TextSwitcher等。

​ 在佈局中可得到的有:LinearLayout, FrameLayout, RelativeLayout等。更多示例請參閱通用佈局對象

​ 如果這些預置的widgets和layouts中沒有符合你的需求的,你可以創建自己的View子類。如果你只需要對現有的widgets和layouts進行很小的調整,你可以簡單的繼承那個widget或layout,然後重寫它的某些方法。

​ 系統爲你提供了精確的控制屏幕上呈現元素外觀和功能的能力來創建你自己的View子類。爲了給出一個通過自定義View你可以做些什麼的引子,下面是自定義View可以做些什麼的例子。

  • 你可以完全創建一個自定義繪製的View類型,例如用2D 繪圖繪製一個像模擬電子器件的“音量控制”旋鈕。

  • 你可以把一組View組件組合到一個新的組件當中,就像製作一個組合框(彈出列表和自由文本域的組合),一個雙窗口選擇器(左右兩個窗口都具有一個列表,在每個列表中你可以重新賦值其中的每一條內容)等等

  • 你可以覆寫EditText組件在屏幕上呈現的方式(記事本教程使用此功能)。

  • 你可以捕獲其他事件,例如按鍵按下的事件,並且自定義處理方式(就像遊戲一樣)

下面的部分介紹如何創建自定義的view並且將它應用到應用中。詳細信息請看View.class

##1.基本方法##

這裏是開始創建你的自定義View組件所需要知道的高級概述:

  1. 你自己的View需要繼承一個View類或其子類。
  2. 重寫父類中的一些方法。父類中可以重寫的方法都已on...開頭,就像onDraw(), onMeasure(), 和onKeyDown()等等。這些和 Activity 或 ListActivity中的on...很像。
  3. 使用你的新擴展類。一旦完成,你的擴展類可以用於代替你的父類的位置。

注意:擴展類可以以內部類的方式定義在使用它的Activity中。這是很有用處的,因爲Activity可以控制他們的訪問權限,但這不是必須的(因爲或許你想創建一個使用更廣泛的公共View應用在你的應用中)。

2.完全自定義組件##

​ 完全自定義組件可以被用來繪製你想顯示的任何樣子的組件。或許讓一個圖形VU表看起來像一箇舊的模擬量表。或許是一個很長的文字View,有一個彈跳球沿着這些詞移動,所以你可以隨着這個KTV機器一起唱歌。無論哪種原因,你想要做某些無論內置組件如何組合也不能實現的工作,可以採用這種方式定義組件。

​ 幸運的是,你可以輕鬆的以你喜歡的方式創建出一個組件,能限制你的只有你的想象力、屏幕的大小、和可得到的處理的能力(記住,在最後你的應用或許不得不運行在一些比你的機器處理能力差的機器上)。

創建一個完全自定義的組件所需如下:

  1. 你可以擴展的最通用的View,不出所料的是View類,所以通常首先擴展它來創建新的超級組件。
  2. 你需要提供一個可以從XML中獲取屬性和參數的構造器,你也可以指定自己的屬性和參數。
  3. 你或許會也想要在你的組件中創建自己的事件監聽器,修改和訪問屬性,和更多更復雜的行爲。
  4. 如果你想要組件展示一些東西,幾乎大多數情況你要重寫 onMeasure()onDraw()方法 ,儘管他們都有默認的行爲,默認的onDraw()方法是空方法,默認的onMeasure()會將大小設置爲100*100 這可能不是你想要的。
  5. 其他on...方法也可以根據需要進行重寫。

3.重寫onDraw()onMeasure()##

onDraw()提供給你一個畫布(Canvas)你可以在上面實現你想做的任何事:2D繪圖、其他的標準或自定義的組件,字體樣式,或者任何你可以想到的。

**注意:**這不適用於3D繪圖。如果要使用3D圖形,則必須擴展SurfaceView 而不是View,並從單獨的線程繪製。有關詳細信息,請參閱GLSurfaceViewActivity示例。

onMeasure()是你的組件與其容器之間的渲染的關鍵部分。onMeasure()應該被重寫以有效和準確地報告其所含部件的測量結果。 onMeasure()視圖大小的控制是由父視圖、佈局文件、以及視圖本身共同完成的,父視圖會提供給子視圖參考的大小,而開發人員可以在XML文件中指定視圖的大小,然後視圖本身會對最終的大小進行拍板。在setMeasuredDimension()方法調用之後,我們才能使用getMeasuredWidth()getMeasuredHeight()來獲取視圖測量出的寬高,以此之前調用這兩個方法得到的值都會是0。如果你無法從覆蓋onMeasure()方法調用此方法,則結果將在測量時間內出現異常。

**注意:**上面這一段是從郭霖的文章上截取出來的鏈接。與原文不相符。

onMeasure()方法實現步驟如下:

  1. 重寫onMeasure()方法,調用它來測量組件的長度與寬度(他們都是以int整數來表示大小)對於你創作的組件來說,自定義他們的長寬是必須的。更全面的參考,請參見參考文檔View.onMeasure(int, int)(參考文檔很好的解釋了整個測量操作的過程)。
  2. 你的組件的onMeasure()方法應該能計算出一個所要呈現出的長寬的大小。它應該滿足傳入的規格參數的要求,雖然它可以選擇超出他們(在這種情況下,父控件可以選擇他們的處理方法,包括裁剪、滾動,拋出異常,或者根據不同的測量規則重新調用onMeasuer()方法,)
  3. 一旦控件的寬度和長度被計算出來,必須調用setMeasuredDimension(int width, int height)將計算值傳遞給它,如果失敗將會拋出異常。

以下是一些View的框架提供的回調方法

類別 方法 描述
創建 構造方法 當一個View從代碼中被創建的時候會調用一種構造器,當一個View從佈局文件中引入時會調用另一種構造方法,第二種形式可以解析一些佈局文件中定義的屬性。
onFinishInflate() 當View和它的子View全部從XML中獲取之後調用
Layout [onMeasure(int, int)](https://developer.android.com/reference/android/view/View.html#onMeasure(int, int)) 調用這個方法來確定它和它的子View的所需要的大小
[onLayout(boolean, int, int, int, int)](https://developer.android.com/reference/android/view/View.html#onLayout(boolean, int, int, int, int)) 當這個View需要爲它的子View賦值大小和位置的時候調用
[onSizeChanged(int, int, int, int)](https://developer.android.com/reference/android/view/View.html#onSizeChanged(int, int, int, int)) 當View的大小改變時調用
繪製 onDraw(Canvas) 當一個View需要繪製它自己時調用
事件處理 [onKeyDown(int, KeyEvent)](https://developer.android.com/reference/android/view/View.html#onKeyDown(int, android.view.KeyEvent)) 當一個新的鍵盤事件發生時調用
[onKeyUp(int, KeyEvent)](https://developer.android.com/reference/android/view/View.html#onKeyUp(int, android.view.KeyEvent)) 當鍵盤擡起事件發生時調用
onTrackballEvent(MotionEvent) 當軌跡球移動事件發生時調用
onTouchEvent(MotionEvent) 當觸摸屏幕的事件發生時被調用
焦點事件 [onFocusChanged(boolean, int, Rect)](https://developer.android.com/reference/android/view/View.html#onFocusChanged(boolean, int, android.graphics.Rect)) 當其獲得或失去焦點時調用
onWindowFocusChanged(boolean) 當包含這個View的窗口獲得或失去焦點時調用
綁定事件 onAttachedToWindow() 當View綁定到窗口上時調用
onDetachedFromWindow() 當View和窗口分離解綁時調用
onWindowVisibilityChanged(int) 當窗口的可見性發生變化時調用

4.複合控件##

​ 如果你不想創建一個完全自定義的組件,而是希望將 由一組現有的控件組成的可重用的組 件組合在一起。創建一個複合控件或許會滿足你的需求。簡單的來說,他將一些原生控件彙集在一起,形成一個item的邏輯組,這個item可以被當作一個單一事情看來對待。例如,一個組合框可以被看作由一行 EditText和一個相鄰的附加了一個PopupList的button按鈕組和而成的,如果你點擊這個按鈕,選擇了list中的一些數據,就會填充EditText字段,但如果用戶喜歡,用戶也可以直接將其輸入到EditText中。

​ 在Android中,實際上還有兩個其他View可以實現:Spinner 和 AutoCompleteTextView,但不管他們是如何實現的,組合框的概念是一個容易理解的例子。

創建一個複合組件需要:

  1. 通常的起點是某種佈局,因此創建一個繼承於佈局的類。也許在組合框的例子中,我們可能會使用一個水平方向的LinearLayout。請記住,其他佈局可以嵌套在內部,因此複合組件可以是任意複雜和結構化的。請注意,就像使用Activity一樣,你可以使用聲明式(基於XML)的方法來創建包含的組件,也可以使用編程方式將其嵌套在代碼中。
  2. 在新建類的構造方法中,首先將父類所需要的所有參數提交到父類的構造方法中去。然後,你就可以設置其他的View組件使用到你的新的組件當中,這裏就是你應該創建EditText 文本域和PopupList的地方。注意,你還可以從XML中將自己的屬性和參數引入,通過構造函數提取。
  3. 你還可以爲控件內部包含的View創建事件監聽器。例如,通過列表選項的Click Listener偵聽器來更新EditText的內容。
  4. 您還可以使用訪問器和修飾符創建自己的屬性,例如,允許一開始在組件中設置EditText值,並在需要時查詢其內容。
  5. 在繼承佈局的情況下,你不需要重寫 onDraw()和onMeasure()方法,因爲佈局自己的默認行爲會工作的很好,然而,如果你需要的話還是可以重寫他們。
  6. 你或許會重寫其他on…方法,例如 onKeyDown(),當按下某個鍵時,可能從組合框的彈出列表中選擇某些默認值。

總而言之,使用“佈局”作爲自定義控件的基礎具有許多優點,包括:

  • 你可以使用聲明XML文件的方式指定佈局,也可以以編程方式創建視圖,並通過代碼將其嵌入到佈局中。
  • onDraw()與onMeasure()方法(和其他on…方法)有適合的行爲,所以你不必重寫它們。
  • 最後,你可以非常快速地構建任意複雜的複合控件,並重新使用它們,就像它們是單個組件一樣。

5.修改一個現有view的類別##

​ 在某些特殊的情況下,這裏有一個更加簡單創建自定義View的方法,如果這裏有一個和你想要的組件很相似的組件,你可以簡單的繼承它,僅僅重寫你想改變的地方。通過一個完全自定義的組件,你可以做任何你想做的事情,但是從View的層次結構中繼承一個功能更專一的類,可以讓你免費獲取大量可能完全符合你需要的行爲。

​ 例如,SDK中包含了一個記事本應用的例子,這個例子從很多方面展示了對Android平臺的利用,就像擴展了EditText View來實現了一個內置的記事本。這或許不是一個很完美的例子,但是它確實展示了這一觀念。

​ 如果你還沒有這樣做,導入記事本的例子到你的工作空間中,特別的,請注意NoteEditor.java文件中的MyEditText的定義。

一些關鍵點記錄如下:

1.定義

類的定義如下

publicstatic class MyEditText extends EditText

  • 它以一個內部類的方式定義在NoteEditor 活動中,但是它是public 的所以它可以以NoteEditor.MyEditText方式被其他外部類使用。
  • 它是靜態(static)的,這意味着
  • 它繼承自 EditText,一個我們選擇用來自定義一些情況的View。當我們完成後,這個View就可以用來替代一個普通的 EditText view。

2.類初始化

​ 一般情況下父類的構造方法先被調用。此外,這不是默認的構造器,而是一個參數化的構造函數。當EditText從XML文件中引入時,通過這些參數來創建它,因此,我們的構造器也需要將他們傳遞到父構造器當中去。

3.重寫方法

​ 在這個例子中,只重寫了 onDraw()方法,但是其他項目中可能還需要重寫其他方法。

​ 對於這個記事本的例子來說,重寫 onDraw()方法允許我們在EditText的畫布上中繪製藍色線,其父類的onDraw()方法在子類的方法結束前調用。父類的方法應當被調用,但是在這個例子中,我們在繪製完成藍線後才調用的它。

4.使用自定義組件

​ 現在我們已經有了自定義的組件,但是現在我們該怎樣來使用它?在這個記事本的例子中,自定義的組件直接在聲明佈局文件中使用,詳情請在 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" />
  • XML中生命自定義組件的與普通的View一樣,並且以用完整的包名指定類。還要注意,我們定義的內部類要通過NoteEditor$MyEditText符號來引用, 這是Java編程語言中一種標準的引用內部類的方法。

如果你自定義的組件沒有定義爲內部類,就可以按如下聲明。

<com.android.notepad.MyEditText
  id="@+id/note"
  ... />

注意:MyEditText不是一個單獨的文件,他是嵌入在 NoteEditor 類內部的,上面這種方法將會不起作用。

  • 聲明的其他屬性和參數等將通過組件的構造函數傳遞進去,然後傳遞到 EditText的構造函數中。所以,這裏有很多參數是EditText使用的。請注意,你也可以添加自己的參數,下面將再次介紹。

    這就是它的一切。誠然,這是一個簡單的例子,但是記住一點—創建一個組件的難度取決於你的需求。

    ​一個更加複雜的組件可能會重寫更多on...方法,並且引入一些自己的方法,定義他們的參數和行爲。但請記住,唯一的限制是你的組件的功能的,就是你的想象力。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章