android中-自定義View對象| 我們知道android SDK中的UI控件都是View或ViewGroup的子類(ViewGroup也是View的子類),我們將View細(xì)分為單獨(dú)View和容器View 兩種,所以就衍生兩個視圖基類:View和ViewGroup。通過擴(kuò)展這兩個基類,Android SDK提供了一系列功能強(qiáng)大,設(shè)計(jì)巧妙的UI控件,但是用戶的需求是千變?nèi)f化的,SDK中不可能提供所有可能用到的UI形式,所以這就需要允許用戶自定義 View對象來完成他們所需要的效果。 幸好,Android SDK有支持自定義View對象,它允許你自定義一個類,繼承android.view.View對象,然后你只需要調(diào)整它里面的一些方法即可,那到底要 重寫哪些方法呢?如果你對android.view.View本身都不是很熟悉的話,那怎么會自定義view呢,就像是還不會走路,怎么就想學(xué)跑步了,所 以我們首先要先了解一些關(guān)于View的東西。 了解android.view.View我們在了解Activity時(shí)就有提到,只要有能看到的UI界面,就一定有Activity。這邊所說的UI界面就是指View對象,所以View 和Activity離不開關(guān)系。當(dāng)Activity處于Active狀態(tài)時(shí)(Activity獲取了當(dāng)前的焦點(diǎn)),它就發(fā)出請求要求繪制Activity 中的View對象。系統(tǒng)根據(jù)視圖的關(guān)系層次從根節(jié)點(diǎn)出發(fā)開始繪制直到所有的葉子節(jié)點(diǎn)(因?yàn)?a title="Android應(yīng)用程序原理" href="http://www./index.php/tutorial/android-to-new/principles-of-android-applications.html" target="_blank">View是有層次關(guān)系的), 整個繪制過程中View和ViewGroup的實(shí)現(xiàn)形式是不一樣的,因?yàn)閂iewGroup里面會有子視圖對象,所以它需要請求子視圖執(zhí)行繪制,整個視圖 樹執(zhí)行繪圖的順序是中序遍歷,從根節(jié)點(diǎn)先畫,最后才是子節(jié)點(diǎn)。如果是View那么只要確定了它的大小即可執(zhí)行繪制方法。 繪制ViewGroup時(shí)可以分為兩個步驟:measure測量過程和layout布局過程。measure是用來測量ViewGroup所占用的 尺寸,它按照中序遍歷的方式遞歸調(diào)用子視圖的measure方法,然后保存各個子視圖對應(yīng)測量出來的結(jié)果。當(dāng)measure測量完成后就會調(diào)用 layout過程,layout過程也是使用中序遍歷遞歸調(diào)用,它做的工作是確定每個父視圖的尺寸,因?yàn)樵趍easure階段已經(jīng)完成了所有的子視圖尺 寸,所以父視圖的尺寸也可以通過它計(jì)算出來。 當(dāng)measure測量執(zhí)行完成后,你就已經(jīng)知道每個視圖所需的尺寸了,所以你需要設(shè)置值,讓getMeasureWidth和 getMeasureHeight方法可以獲取測量得到的結(jié)果。measure測量的結(jié)果必須考慮父視圖設(shè)置的長度,確保測量完成后得到的結(jié)果滿足:所有 的父視圖的尺寸都能裝得下它們各自的所有子視圖。一個父視圖可能會多次調(diào)用它的子視圖的measure方法,因?yàn)橐晥D可以設(shè)置成最大尺寸 (fill_parent),它首先會不限制子視圖的大小,讓子視圖能盡量大,但是如果有兩個子視圖都采用最大尺寸(fill_parent),那么只能 采取折中方式。 measure測量過程用兩個類來協(xié)助確定尺寸:MeasureSpec和LayoutParams。MeasureSpec用來對父視圖描述它需要的尺寸或位置,LayoutParams用來描述它需要的寬度和高度,它可定義的尺寸分為3種模型:
MeasureSpec用來讓父視圖通知子視圖,子視圖所能分配的大小,它分為下面3種模型:
創(chuàng)建自定義View對象 了解了View測量和繪制的原理后,你就可以通過繼承它,重寫特定的方法創(chuàng)建一個自定義View對象。一般創(chuàng)建自定義View的用途是: 1. 自定義視圖對象,創(chuàng)建一個SDK沒提供的布局形式,如Accordion。 2. 將多個View對象組合起來封裝成一個新的自定義View對象,如Combox就是由一個下拉列表和一個輸入框和輸入框右邊一個按鈕組成的。 3. 改寫EditText對象,Android SDK中的記事本例子就使用了擴(kuò)展EditText的視圖,它在每行文字的下面畫一條藍(lán)色的線。 4. 創(chuàng)建一個游戲面板,捕捉一些鍵盤或觸摸事件做出一些特定的處理。 如何創(chuàng)建自定義View對象 主要有下面幾個步驟: 1. 創(chuàng)建一個自定義類,繼承android.view.View。 2. 重寫父類構(gòu)造方法,你可以在構(gòu)造方法中獲取在xml布局文件里設(shè)置的屬性,你可以從這里讀取一些自定義的屬性配置View對象。 3. 重寫父類(View)的方法,這邊可以根據(jù)需要修改的程度有選擇的重寫,這些重寫的方法都是on開頭的方法如:onDraw、onMeasure或 onKeyDown(這些跟Activity生命周期中的onCreate,onPause…一樣也算繪制過程的生命周期)。如果你沒有重寫onDraw 默認(rèn)它不做任何事,所以你會看到一片空白區(qū)域,onMeasure默認(rèn)會創(chuàng)建一個100 * 100的區(qū)域 4. 使用這個對象 一般我們?nèi)绻麤]有在xml布局文件中使用一些自定義的配置屬性可以不用重寫構(gòu)造方法,這時(shí)候我們只需要重寫onMeasure和onDraw方法。 onMeasure方法被調(diào)用時(shí)有兩個參數(shù):widthMeasureSpec和heightMeasureSpec這個參數(shù)是int型,用來表示寬度和 高度的尺寸,在onMeasure中必須根據(jù)這兩個參數(shù)確定View的寬度和高度值,然后調(diào)用setMeasureDimension(int width,int height)方法設(shè)置視圖的顯示區(qū)域范圍,否則將會拋出異常。在onDraw方法中,會有一個調(diào)用參數(shù):Canvas,你可以使用它進(jìn)行繪制View的 操作。 下面我們使用一個簡單的例子演示下如何自定義一個View對象,我們先講述下這個例子實(shí)現(xiàn)的效果:例子將展現(xiàn)一個藍(lán)色背景的面板,當(dāng)你點(diǎn)擊面板中的 某點(diǎn)時(shí)會以這個點(diǎn)擊點(diǎn)為中心隨機(jī)產(chǎn)生一個 半徑是1-10的紅色實(shí)心圓。我們首先自定義一個View對象,CustomView.java: package com.android777.demo.uicontroller.view;
import java.util.ArrayList;
import java.util.List;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
public class CustomView extends View {
Paint paint;
List<Dot> dots;
public CustomView(Context context) {
super(context);
init();
}
public CustomView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public void init(){
paint = new Paint();
//設(shè)置繪制的顏色是紅色
paint.setColor(Color.RED);
paint.setAntiAlias(true);
//視圖對象背景色是藍(lán)色
setBackgroundColor(Color.BLUE);
//初始化時(shí)里面沒有顯示任何的紅點(diǎn)
dots = new ArrayList<CustomView.Dot>();
}
@Override
public boolean onTouchEvent(MotionEvent event) {
//獲取隨機(jī)點(diǎn)擊位置的坐標(biāo)值
float x = event.getX();
float y = event.getY();
//生成一個隨機(jī)半徑值范圍是1 - 10
float radius = (float) (1 + Math.random() * 9);
//將點(diǎn)擊的坐標(biāo)和隨機(jī)生成的半徑值構(gòu)造成Dot對象存放到視圖里
dots.add(new Dot(x,y,radius));
//掉用這個方法 重新繪制視圖, 這樣就能把上面剛添加的點(diǎn)顯示出來
invalidate();
return super.onTouchEvent(event);
}
@Override
protected void onDraw(Canvas canvas) {
//繪制所有視圖對象里保存的Dot點(diǎn)信息
for(Dot dot : dots){
canvas.drawCircle(dot.x, dot.y, dot.radius, paint);
}
}
/**
*
* 這個靜態(tài)內(nèi)部類用來封裝一個紅色圓點(diǎn)的位置和半徑
*
*/
static class Dot {
public float x;
public float y;
public float radius;
public Dot(float x,float y,float radius){
this.x = x;
this.y = y;
this.radius = radius;
}
}
}
然后我們編寫一個Activity,在里面使用上面自定義的視圖,CustomViewActivity.java: package com.android777.demo.uicontroller.view;
import android.app.Activity;
import android.os.Bundle;
public class CustomViewActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(new CustomView(this));
}
}
運(yùn)行后,點(diǎn)擊屏幕中的某點(diǎn)就會產(chǎn)生一個隨機(jī)半徑范圍是1-10的紅色小實(shí)心圓:
|
|
|