第三章終于進(jìn)入開發(fā)的正題了伟众!本章主要介紹自定義控件:
1.控件分為兩類:View
和Viewgroup
,通過ViewGroup
整個界面形成一個樹形結(jié)構(gòu)警儒,并且ViewGroup
負(fù)責(zé)對子View
的測量與繪制以及傳遞交互事件眉踱。
2.Activity
包含一個Window
對象,Window
對象又將一個DecorView
設(shè)置為整個應(yīng)用的根View歹鱼。這里所有View
的監(jiān)聽事件都通過WindowManagerService
來接收,并通過Activity
對象來回調(diào)onClickListener
卜高。DecorView
在顯示上分為TitleView
和ContentView
兩部分弥姻。可以通過如下代碼獲得ContentView
:
ViewGroup content=(ViewGroup)findViewById(android.R.id.content);
3.View
的測量在onMeasure
中進(jìn)行掺涛,系統(tǒng)提供了MeasureSpec
類庭敦,是一個32位的int值,其高2位為測量模式薪缆,低30位為測量的大小螺捐。測量模式有以下三種:
-
EXACTLY
:精確模式,當(dāng)控件指定精確值(例如android:layout_width="50dp")或者指定為match_parent
屬性時系統(tǒng)使用該模式矮燎。 -
AT_MOST
:最大值模式,指定wrap_content
時系統(tǒng)使用該屬性赔癌,View
類默認(rèn)只支持EXACTLY
诞外,如果想使用wrap_content
需自己在onMeasure
中實(shí)現(xiàn)。 -
UNSPECIFIED
:自定義模式灾票,View
想多大就多大峡谊,通常在繪制自定義View的時候才使用。
下面是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);// 最終將測量的值傳入該方法完成測量
}
4.View
的繪制是通過onDraw
方法實(shí)現(xiàn)的刊苍,具體是通過對onDraw
方法中canvas
參數(shù)操作執(zhí)行繪圖既们。在其他地方,則需要自己創(chuàng)建canvas
對象正什,創(chuàng)建時需傳入一個bitmap
對象啥纸, bitmap
是用來保存Canvas.drawXXX
繪制的像素信息的,通過這些繪圖操作改變的實(shí)際上就是bitmap
對象而不是canvas
婴氮。
5.當(dāng)ViewGroup
的大小為wrap_content
時斯棒,它就會遍歷所有子View盾致,并調(diào)用其Measure
方法獲得其大小,來決定自身的大小荣暮,而在其他模式下則通過指定值來設(shè)置自身的大小庭惜。然后當(dāng)View
測量完畢以后,ViewGroup
會執(zhí)行它的Layout
方法穗酥,同樣是遍歷子View
并調(diào)用其Layout
方法來確定布局位置护赊。在自定義ViewGroup
時,通常會重寫onLayout()
方法來控制子View
顯示位置砾跃,若需支持wrap_content
還需重寫onMeasure()
方法骏啰。ViewGroup
通常情況下不需要繪制,但是會調(diào)用dispatchDraw()
方法來繪制其子View
蜓席,過程同樣是遍歷子View
器一。
6.自定義View時有一些比較重要的回調(diào)方法如下:
onFinishInflate();//從xml加載組件后回調(diào)
onSizeChanged();//組件大小改變時回調(diào)
onMeasure();//回調(diào)該方法進(jìn)行測量
onLayout();//回調(diào)該方法來確定顯示的位置
onTouchEvent();//監(jiān)聽到觸摸事件回調(diào)
7.自定義View通常有三種情況:
(1)對現(xiàn)有控件進(jìn)行拓展:
一般來說,會在onDraw()
方法中對原生控件行為進(jìn)行拓展
@Override
protected void onDraw(Canvas canvas) {
//在回調(diào)父類方法前厨内,實(shí)現(xiàn)自己的邏輯
super.onDraw(canvas);
//在回調(diào)父類方法后祈秕,實(shí)現(xiàn)自己的邏輯
}
(2)通過組合來實(shí)現(xiàn)新的控件:
這種方式通常需要繼承一個合適的ViewGroup
,再給它添加指定功能的控件雏胃,從而組合成新的復(fù)合控件请毛。
(3)重寫View
來實(shí)現(xiàn)全新的控件:
當(dāng)Android系統(tǒng)原生的控件無法滿足我們的需求時,就需要創(chuàng)建一個全新的自定義View
了瞭亮。通常需要繼承View
類方仿,并重寫它的onDraw()
、onMeasure()
等方法實(shí)現(xiàn)繪制邏輯统翩,同時通過重寫onTouchEvent()
等觸控事件來實(shí)現(xiàn)交互邏輯仙蚜,還可以引入自定義屬性,豐富自定義View
的可定制性厂汗。
8.本章較為淺顯的分析了下事件傳遞的機(jī)制委粉。當(dāng)ViewGroup
接收到事件,通過調(diào)用dispatchTouchEvent()
娶桦,由這個方法再調(diào)用onInterceptTouchEvent()
方法來判斷是否要攔截事件贾节,如果返回true則攔截將事件交給onTouchEvent
處理,返回false則繼續(xù)向下傳遞衷畦。當(dāng)View
在接受到事件時栗涂,通過調(diào)用dispatchTouchEvent()
,由此方法再調(diào)用onTouchEvent
方法祈争,如果返回true則攔截事件自己處理斤程,如果返回false則將事件向上傳遞回ViewGroup
并且調(diào)用其onTouchEvent
方法繼續(xù)做判斷。
本章中用代碼例舉了很多自定義View菩混,但由于本人對一些系統(tǒng)繪制的api尚不熟悉暖释,等以后弄懂了再補(bǔ)充