概述
當Android系統提供的UI組件不足以滿足我們的需求時,我們可以自己繼承View來設計自己的View。然後,選擇重寫部分的方法。通常可以被重寫的方法如下:
1)建構函式,View有三個建構函式:
public View (Context context) 當我們通過代碼建立view時需要複寫此方法。
public View (Context context, AttributeSet attrs) 當我們通過xml建立view時需要複寫此方法。
public View (Context context, AttributeSet attrs, int defStyle) 通過源碼我們可以知道其實public View (Context context, AttributeSet attrs) 調用的也是三個參數的建構函式public View (Context context, AttributeSet attrs, int defStyle)。我們一般不需要複寫此建構函式。
2)回呼函數
onFinishInflate():當應用從XML布局檔案載入該組件並用它構建完介面之後,該方法就會被回調。
onMeasure(int,int):用來檢測View及它所包含的子View的大小
onLayout(boolean,int,int,int):當此View需要分配其子組件的位置、大小時會被回調。
onSizeChanged(int,int,int,int):當該組件大小被改變時回調
onDraw(Canvas):當該組件將要繪製時被回調
onKeyDown(int,KeyEvent):當此View被按下時被回調
onKeyUp(int,KeyEvent):當此View被鬆開時被回調
onTrackballEvent(MotionEvent):當發生軌跡球事件時
onTouchEvent(MotionEvent):當發生觸摸事件時
onWindowFocusChanged(boolean):當焦點發生改變時
onAttachedToWindow():當此組件被添加到某個視窗時
onDetachedFromWindow():當從某個視窗上被分離時
onWindowVisibilityChanged(int):當包含該組件的視窗的可見度發生改變時觸發
步驟
1)繼承View(當然也可以選擇它的某些子類,根據自己的實際需求選擇)
2)重寫兩個建構函式:上面說過View有三個建構函式,我們只需要複寫前兩個即可。
注意:其實,不是我們一定要複寫那兩個建構函式,如果我們只通過代碼的方式添加我們自訂的View的話可以只複寫單參的建構函式。若我們只通過XML布局的方式往Activity中添加我們自訂的View時,可以只複寫雙參的建構函式。但為了防止出錯,建議把單參的、雙參的都複寫了。
3)選擇複寫部分回呼函數,我們根據自己的需求選擇複寫一些回呼函數。一般都 會onDraw(Canvas)方法。
4)當我們完成了上面的步驟後,我們就完成了自訂View。我們可以通過兩種方式將它添加到我們的Activity中
a.通過代碼方式
b.通過XML方式
具體請看下面的例子代碼:
執行個體
類比一個跟隨手機移動的小球。
自訂View類CustomView1
1 package com.example.customview;
2
3 import android.content.Context;
4 import android.graphics.Canvas;
5 import android.graphics.Color;
6 import android.graphics.Paint;
7 import android.util.AttributeSet;
8 import android.view.View;
9
10 public class CustomView1 extends View {
11
12 public float currentX = 46;
13 public float currentY = 57;
14 Paint p;
15
16 private void init() {
17 p = new Paint();
18 p.setColor(Color.GREEN);
19 }
20
21 public CustomView1(Context context) {
22 super(context);
23 System.out.println("---------1-----------");
24 init();
25 }
26
27 public CustomView1(Context context, AttributeSet attrs) {
28 super(context, attrs);
29 System.out.println("---------2-----------");
30 init();
31 }
32
33 @Override
34 protected void onDraw(Canvas canvas) {
35 super.onDraw(canvas);
36 canvas.drawCircle(currentX, currentY, 20, p);
37 }
38
39 }1)通過代碼引入CustomView
View Code
1 package com.example.customview;
2
3 import android.os.Bundle;
4 import android.view.MotionEvent;
5 import android.view.View;
6 import android.view.View.OnTouchListener;
7 import android.widget.RelativeLayout;
8 import android.app.Activity;
9
10 public class CustomViewActivity extends Activity {
11
12 @Override
13 protected void onCreate(Bundle savedInstanceState) {
14 super.onCreate(savedInstanceState);
15 setContentView(R.layout.activity_custom_view);
16 final CustomView1 cs = new CustomView1(this);
17 RelativeLayout Rlayout = (RelativeLayout) findViewById(R.id.RLayout);
18 Rlayout.addView(cs);
19 cs.setOnTouchListener(new OnTouchListener() {
20
21 @Override
22 public boolean onTouch(View v, MotionEvent event) {
23 cs.currentX = event.getX();
24 cs.currentY = event.getY();
25 cs.invalidate();
26 return true;
27 }
28 });
29 }
30 }此時我們的xml布局檔案如下:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/RLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".CustomViewActivity" >
<!--
<com.example.customview.CustomView1
android:id="@+id/cs"
android:layout_width="fill_parent"
android:layout_height="fill_parent" />
-->
</RelativeLayout>2)通過XML引入CustomView
View Code
1 package com.example.customview;
2
3 import android.os.Bundle;
4 import android.view.MotionEvent;
5 import android.view.View;
6 import android.view.View.OnTouchListener;
7 import android.app.Activity;
8
9 public class CustomViewActivity extends Activity {
10
11 @Override
12 protected void onCreate(Bundle savedInstanceState) {
13 super.onCreate(savedInstanceState);
14 setContentView(R.layout.activity_custom_view);
15 final CustomView1 cs = (CustomView1) findViewById(R.id.cs);
16 cs.setOnTouchListener(new OnTouchListener() {
17
18 @Override
19 public boolean onTouch(View v, MotionEvent event) {
20 cs.currentX = event.getX();
21 cs.currentY = event.getY();
22 cs.invalidate();
23 return true;
24 }
25 });
26 }
27 }此時我們的xml布局檔案如下:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/RLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".CustomViewActivity" >
<com.example.customview.CustomView1
android:id="@+id/cs"
android:layout_width="fill_parent"
android:layout_height="fill_parent" />
</RelativeLayout>大家可以注意一下Logcat的輸出,對比一下,當我們選擇這兩種不同的引入自訂View方式時建構函式的調用情況。
PS:我們知道View的子類有許多獨特的屬性,比如TextView的text屬性,color屬性等。那麼我們能不能給我們自訂的View也添加一些自訂的屬性呢?答案當然是可以的,我們放到下一講中來說。