Android ApiDemos示例解析(109):Views->Custom

Android 系統提供瞭很多功能強大的UI組件,包括Button,TextView, EditText, ListView, CheckBox, RadioButton, Gallery, Spinner ,AutoCompleteTestView 等以及LinerLayout, FrameLayout ,RelativeLayout 等佈局管理組件可以應用於大部分的應用。

如果執行系統自帶的UI控件不能滿足應用的需要,Android允許你自定義UI控件,可以有多種方法來構造新的自定義UI控件:

修改或擴展系統自帶的UI控件,這些系統定義的UI控件定義其對應的基本UI功能,比如Button 提供瞭按鈕的功能,可以通過重載這些UI控件的事件和onDraw事件來擴充UI功能以及顯示。
提供組合多個基本UI控件構造較為復雜的UI直接,比如可以使用TextView和Button(點擊後顯示一個浮點的ListView)來創建一個Dropdown 組合框控件。
以View 或ViewGroup 為基類構造一個全新的UI控件。
本例采用瞭第三種方法,創建瞭一個全新的LabelView:

<com.example.android.apis.view.LabelView
android:background=”@drawable/red”
android:layout_width=”match_parent”
android:layout_height=”wrap_content”
app:text=”Red”/>

下面為構造完全自定義UI控件的一般步驟和註意事項,完全自定義UI控件的給開發人員最大的控制能力,可以顯示任意的外觀或實現系統定義的UI控件無法實現的功能。

以View 為基類派生自定義View組件。
實現使用Context, ArributeSet 為參數的構造函數,這些函數是使用XML描述UI時需調用的。
創建所需要的自定義的事件Listener,屬性等這一步不是必須的,可以根據UI組件功能自行決定。
重載onMeasure 和onDraw ,缺省的onMeasure 將UI控件的大小定義為100X100大小,如果這個大小恰巧正是你需要的,你可以使用缺省的onMeasure,
根據需要重載其它所需的on…方法。
這個自定義LabelView可以用來顯示一行文字,定義瞭三個屬性:

text: 文字內容
textSize:文字大小
textColor:文字顏色
這些屬性在layout 中使用 app:text=”Red”,app:textSize=”20dp”,app:textColor=”#ffffffff”

來看看LabelView 的具體實現:

首先以View為基類,並定義瞭LabelView的幾個屬性,用來保存文字的大小,內容和顏色,mText為文字內容,其它兩個保存瞭顏色和大小。

[java] 
public class LabelView extends View { 
 private Paint mTextPaint; 
 private String mText; 
 private int mAscent; 

public class LabelView extends View {
 private Paint mTextPaint;
 private String mText;
 private int mAscent;

然後實現構造函數

[java] 
public LabelView(Context context) { 
 super(context); 
 initLabelView(); 

 
public LabelView(Context context, AttributeSet attrs) { 
 super(context, attrs); 
 initLabelView(); 
 … 

public LabelView(Context context) {
 super(context);
 initLabelView();
}

public LabelView(Context context, AttributeSet attrs) {
 super(context, attrs);
 initLabelView();
 …
}

其中public LabelView(Context context, AttributeSet attrs) 是使用Layout定義LabelView必須實現的。而且一般需要調用基類的對應方法。如果不定義該構造函數,此時如果在Layout定義LabelView,Android展開Layout資源文件時找不到所需構造函數,應用將拋出異常退出。

因為LabelView定義瞭三個自定義屬性Text, TextSize,TextColor ,並可以用在Layout定義中,在構造函數中可以獲取這些屬性值。

自定義屬性的步驟如下:

1. 在 values\attrs.xml 中定義屬性 ,如LabelView定義的屬性如下:

<declare-styleable name=”LabelView”>
<attr name=”text” format=”string” />
<attr name=”textColor” format=”color” />
<attr name=”textSize” format=”dimension” />
< /declare-styleable>

如果使用系統支持的屬性如android:text 則不需要定義format 屬性。這個attrs 對應的ID為 R.styleable.LableView (自動生成在R.java 中)

2. 使用Context 的 obtainStyledAttributes 獲取屬性值

[java] 
 TypedArray a = context.obtainStyledAttributes(attrs, 
 R.styleable.LabelView); 
 
CharSequence s 
 = a.getString(R.styleable.LabelView_text); 
 
setTextColor(a.getColor(R.styleable.LabelView_textColor, 
 0xFF000000)); 
 
int textSize 
 = a.getDimensionPixelOffset(R.styleable.LabelView_textSize, 0); 

 TypedArray a = context.obtainStyledAttributes(attrs,
 R.styleable.LabelView);

CharSequence s
 = a.getString(R.styleable.LabelView_text);

setTextColor(a.getColor(R.styleable.LabelView_textColor,
 0xFF000000));

int textSize
 = a.getDimensionPixelOffset(R.styleable.LabelView_textSize, 0);
obtainStyledAttributes 返回的類型為TypedArray ,然後就可以使用TypedArray的getXXX方法來取得所需的屬性值,第一個參數為屬性的ID,第二個參數為找不到對應屬性時缺省值。

3.在Layout XML 文件中添加 屬性對應的namespace 定義,如

xmlns:app=”http://schemas.android.com/apk/res/com.example.android.apis

然後就可以使用app:xxx 來引用自定義控件的屬性瞭,如:

<com.example.android.apis.view.LabelView
android:background=”@drawable/blue”
android:layout_width=”match_parent”
android:layout_height=”wrap_content”
app:text=”Blue” app:textSize=”20dp”/>

4.屬性property對應的代碼為 getProperty, setProperty

如text 的設置方法:

[java]
public void setText(String text) { 
 mText = text; 
 requestLayout(); 
 invalidate(); 

public void setText(String text) {
 mText = text;
 requestLayout();
 invalidate();
}
然後一個重要的方法是onMeausre ,在這個方法在必須調用setMeasureDimension 方法來告訴本View的Container類(各種Layout類型)所需占據的大小,如果沒有設置setMeasureDimension ,Layout將會拋出異常。

[java] 
 protected void onMeasure(int widthMeasureSpec, 
 int heightMeasureSpec) { 
 setMeasuredDimension(measureWidth(widthMeasureSpec), 
 measureHeight(heightMeasureSpec)); 

 protected void onMeasure(int widthMeasureSpec,
 int heightMeasureSpec) {
 setMeasuredDimension(measureWidth(widthMeasureSpec),
 measureHeight(heightMeasureSpec));
}
onMeasure方法來父Layout類為其子類佈局時調用,它為某個子類“你需要多大的空間”,傳入兩個參數:widthMeasureSpec 和heighMeasureSpec ,它們給出瞭這個UI控件可以使用的空間大小規格參數。 本例使用的measureWidth和measureHeight 代碼基本上可以重用到所有的自定義控件中。

最後一步是重載onDraw方法,在屏幕上顯示控件的內容:

[java] 
protected void onDraw(Canvas canvas) { 
 super.onDraw(canvas); 
 canvas.drawText(mText, getPaddingLeft(), 
 getPaddingTop() – mAscent, mTextPaint); 

protected void onDraw(Canvas canvas) {
 super.onDraw(canvas);
 canvas.drawText(mText, getPaddingLeft(),
 getPaddingTop() – mAscent, mTextPaint);
}
onDraw 傳入的參數類型為Canvas ,可以使用Canvas繪制任意圖形,可以參見ApiDemos 中Graphics 例子。本例根據文本的顏色,大小繪制文本。www.aiwalls.com

如果需要使用3D (OpenGL ES )圖形,就不能從View 派生,而應使用SurfaceView 作為基類。

 


作者:mapdigit

 

發佈留言