第三章終于進(jìn)入開(kāi)發(fā)的正題了!本章主要介紹自定義控件:
1.控件分為兩類(lèi):View和Viewgroup,通過(guò)ViewGroup整個(gè)界面形成一個(gè)樹(shù)形結(jié)構(gòu),并且ViewGroup負(fù)責(zé)對(duì)子View的測(cè)量與繪制以及傳遞交互事件。
2.Activity包含一個(gè)Window對(duì)象,Window對(duì)象又將一個(gè)DecorView設(shè)置為整個(gè)應(yīng)用的根View。這里所有View的監(jiān)聽(tīng)事件都通過(guò)WindowManagerService來(lái)接收,并通過(guò)Activity對(duì)象來(lái)回調(diào)onClickListener。DecorView在顯示上分為TitleView和ContentView兩部分??梢酝ㄟ^(guò)如下代碼獲得ContentView:
ViewGroup content=(ViewGroup)findViewById(android.R.id.content);
3.View的測(cè)量在onMeasure中進(jìn)行,系統(tǒng)提供了MeasureSpec類(lèi),是一個(gè)32位的int值,其高2位為測(cè)量模式,低30位為測(cè)量的大小。測(cè)量模式有以下三種:
-
EXACTLY:精確模式,當(dāng)控件指定精確值(例如android:layout_width="50dp")或者指定為match_parent屬性時(shí)系統(tǒng)使用該模式。 -
AT_MOST:最大值模式,指定wrap_content時(shí)系統(tǒng)使用該屬性,View類(lèi)默認(rèn)只支持EXACTLY,如果想使用wrap_content需自己在onMeasure中實(shí)現(xiàn)。 -
UNSPECIFIED:自定義模式,View想多大就多大,通常在繪制自定義View的時(shí)候才使用。
下面是onMeasure的事例代碼:
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthMode = MeasureSpec.getMode(widthMeasureSpec);// 獲取寬度模式
int widthSize = MeasureSpec.getSize(widthMeasureSpec);// 獲取寬度值
int width = 0;
if (widthMode == MeasureSpec.EXACTLY) {
width = widthSize;
} else {
width = 200;// 自定義的默認(rèn)wrap_content值
if (widthMode == MeasureSpec.AT_MOST) {
width = Math.min(widthSize, width);
}
}
int heightMode = MeasureSpec.getMode(heightMeasureSpec);// 獲取高度模式
int heightSize = MeasureSpec.getSize(heightMeasureSpec);// 獲取高度值
int height = 0;
if (heightMode == MeasureSpec.EXACTLY) {
height = heightSize;
} else {
height = 200;// 自定義的默認(rèn)wrap_content值
if (heightMode == MeasureSpec.AT_MOST) {
height = Math.min(heightSize, height);
}
}
setMeasuredDimension(width, height);// 最終將測(cè)量的值傳入該方法完成測(cè)量
}
4.View的繪制是通過(guò)onDraw方法實(shí)現(xiàn)的,具體是通過(guò)對(duì)onDraw方法中canvas參數(shù)操作執(zhí)行繪圖。在其他地方,則需要自己創(chuàng)建canvas對(duì)象,創(chuàng)建時(shí)需傳入一個(gè)bitmap對(duì)象, bitmap是用來(lái)保存Canvas.drawXXX繪制的像素信息的,通過(guò)這些繪圖操作改變的實(shí)際上就是bitmap對(duì)象而不是canvas。
5.當(dāng)ViewGroup的大小為wrap_content時(shí),它就會(huì)遍歷所有子View,并調(diào)用其Measure方法獲得其大小,來(lái)決定自身的大小,而在其他模式下則通過(guò)指定值來(lái)設(shè)置自身的大小。然后當(dāng)View測(cè)量完畢以后,ViewGroup會(huì)執(zhí)行它的Layout方法,同樣是遍歷子View并調(diào)用其Layout方法來(lái)確定布局位置。在自定義ViewGroup時(shí),通常會(huì)重寫(xiě)onLayout()方法來(lái)控制子View顯示位置,若需支持wrap_content還需重寫(xiě)onMeasure()方法。ViewGroup通常情況下不需要繪制,但是會(huì)調(diào)用dispatchDraw()方法來(lái)繪制其子View,過(guò)程同樣是遍歷子View。
6.自定義View時(shí)有一些比較重要的回調(diào)方法如下:
onFinishInflate();//從xml加載組件后回調(diào)
onSizeChanged();//組件大小改變時(shí)回調(diào)
onMeasure();//回調(diào)該方法進(jìn)行測(cè)量
onLayout();//回調(diào)該方法來(lái)確定顯示的位置
onTouchEvent();//監(jiān)聽(tīng)到觸摸事件回調(diào)
7.自定義View通常有三種情況:
(1)對(duì)現(xiàn)有控件進(jìn)行拓展:
一般來(lái)說(shuō),會(huì)在onDraw()方法中對(duì)原生控件行為進(jìn)行拓展
@Override
protected void onDraw(Canvas canvas) {
//在回調(diào)父類(lèi)方法前,實(shí)現(xiàn)自己的邏輯
super.onDraw(canvas);
//在回調(diào)父類(lèi)方法后,實(shí)現(xiàn)自己的邏輯
}
(2)通過(guò)組合來(lái)實(shí)現(xiàn)新的控件:
這種方式通常需要繼承一個(gè)合適的ViewGroup,再給它添加指定功能的控件,從而組合成新的復(fù)合控件。
(3)重寫(xiě)View來(lái)實(shí)現(xiàn)全新的控件:
當(dāng)Android系統(tǒng)原生的控件無(wú)法滿足我們的需求時(shí),就需要?jiǎng)?chuàng)建一個(gè)全新的自定義View了。通常需要繼承View類(lèi),并重寫(xiě)它的onDraw()、onMeasure()等方法實(shí)現(xiàn)繪制邏輯,同時(shí)通過(guò)重寫(xiě)onTouchEvent()等觸控事件來(lái)實(shí)現(xiàn)交互邏輯,還可以引入自定義屬性,豐富自定義View的可定制性。
8.本章較為淺顯的分析了下事件傳遞的機(jī)制。當(dāng)ViewGroup接收到事件,通過(guò)調(diào)用dispatchTouchEvent(),由這個(gè)方法再調(diào)用onInterceptTouchEvent()方法來(lái)判斷是否要攔截事件,如果返回true則攔截將事件交給onTouchEvent處理,返回false則繼續(xù)向下傳遞。當(dāng)View在接受到事件時(shí),通過(guò)調(diào)用dispatchTouchEvent(),由此方法再調(diào)用onTouchEvent方法,如果返回true則攔截事件自己處理,如果返回false則將事件向上傳遞回ViewGroup并且調(diào)用其onTouchEvent方法繼續(xù)做判斷。
本章中用代碼例舉了很多自定義View,但由于本人對(duì)一些系統(tǒng)繪制的api尚不熟悉,等以后弄懂了再補(bǔ)充