Android控件架構(gòu)
每一個Activity包含一個Window對象,DecorView作為整個應(yīng)用窗口的根View,其下包含TitleView和ContentView,這里ContentView就是id為content的FrameLayout,我們平時寫的layout就是天生包裹著一層FrameLayout。在代碼中,Activity的OnCreate中調(diào)用setContentView()方法后,ActivityManagerService會回調(diào)onResume()方法,此時系統(tǒng)會將整個DecorView添加到PhoneWindow中,并讓其顯示出來,由此可以見,為了讓視圖盡快顯示,盡量減輕OnCreate操作。
View的測量
要想繪制一個View,我們必須先對其進(jìn)行測量,知道大小。View的onMeasure()方法
/**
* 測量
* @param widthMeasureSpec
* @param heightMeasureSpec
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
Android系統(tǒng)給我們提供了一個工具類MeasureSpec,它是一個32位的int值,高2位是測量模式,低30位就是測量大小。測量模式分為下面三個。
1、EXACTLY
精確模式,match_parant或者具體數(shù)據(jù)100dp。
2、AT_MOST
最大值模式,此時控件尺寸不超過父控件允許的最大尺寸即可,wrap_content就是此模式。
3、UNSPECIFIED
View想多大就多大。
View默認(rèn)提供了EXACTLY 模式,想要支持AT_MOST和UNSPECIFIED,必須自己自定義了。
系統(tǒng)最終還是會調(diào)用setMeasuredDimension()這個方法將測量的寬高設(shè)置進(jìn)去從而完成測量工作
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}
模板代碼,測量寬(測量高類似) 代碼注釋詳細(xì):
private int measureWidth(int measureSpec) {
int result = 0;
//測量模式
int specMode = MeasureSpec.getMode(measureSpec);
//測量大小
int specSize = MeasureSpec.getSize(measureSpec);
//如果模式是精確模式,則測量結(jié)果為測量出的大小specSize
if (specMode == MeasureSpec.EXACTLY) {
result = specSize;
} else {//不是精確模式,則給定一個值,如果是AT_MOST則選則小的作為最終測量大小。
result = 100;
if (specMode == MeasureSpec.AT_MOST) {
result = Math.min(result, specSize);
}
}
return result;
}
View的繪制
通過canvas的draw方法進(jìn)行繪制
Canvas canvas = new Canvas(Bitmap);
//繪制直線
canvas.drawLine(float startX, float startY, float stopX, float stopY, Paint paint);
//繪制矩形
canvas.drawRect(float left, float top, float right, float bottom, Paint paint);
//繪制圓形
canvas.drawCircle(float cx, float cy, float radius, Paint paint);
//繪制字符
canvas.drawText(String text, float x, float y, Paint paint);
//繪制圖形
canvas.drawBirmap(Bitmap bitmap, float left, float top, Paint paint);
ViewGroup的測量
ViewGroup 可以放置n個View,當(dāng)ViewGroup時wrap_content模式,那么其大小是通過ViewGroup遍歷所有的子View,來獲取View的大小,從而決定自身的大小,而在其他模式下,會通過具體的值來自定自身的大小。ViewGroup遍歷所有的View會調(diào)用所有的View的onMeasure()方法來獲取測量結(jié)果,
當(dāng)子View測量完畢之后,,就需要將子View放在合適的地方,這部分是由onLayout()來進(jìn)行的,
在我們自定義ViewGroup的時候,一般都要重寫onLayout()方法控制子View顯示位置的邏輯,同樣,如果需要wrap_content屬性,那就必須重寫onMeasure方法了,這點和View是相同的
View的繪制
ViewGroup是通過dispathDraw來繪制其子View的,其過程也是通過遍歷所有子View,然后調(diào)用子View的繪制方法完成繪制的
自定義View
通常情況下自定義View有三種方法
1、對現(xiàn)有控件進(jìn)行擴(kuò)展
2、通過組合來實現(xiàn)新控件
3、重寫View實現(xiàn)全新的控件
在View中通常有一些比較重要的回調(diào)方法來。
onFinishInflate();從XML加載組件后回調(diào)。
onSizeChanged();組件大小改變時回調(diào)
onMeasure();測量
onLayout();繪制位置
onTouchEvent();監(jiān)聽觸摸事件
事件攔截機(jī)制分析
假設(shè)一個場景:
BOSS -- ViewGroupA 最外層的
項目負(fù)責(zé)人 -- ViewGroupB 中間層的
程序猿 -- View 最底層
對于ViewGroup一般有一下三個方法
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
return super.dispatchTouchEvent(ev);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
return super.onInterceptTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
return super.onTouchEvent(event);
}
對于View一般重寫以下兩個方法:
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
return super.dispatchTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
return super.onTouchEvent(event);
}
對比下看到ViewGroup比View多了一個方法onInterceptTouchEvent,事件攔截的核心方法。
什么都不做,正常情況是:
事件傳遞順序:BOSS - 項目負(fù)責(zé)人 - 程序猿,傳遞過程中會先執(zhí)行dispatchTouchEvent(事件分發(fā)),在執(zhí)行onInterceptTouchEvent(攔截)。
事件處理順序:程序猿 - 項目負(fù)責(zé)人 - BOSS 。事件處理調(diào)用的是onTouchEvent。
onInterceptTouchEvent返回True則表示攔截,不讓下分發(fā),自己搞起;返回false則表示分發(fā)到下一層中,依次傳遞。
onTouchEvent 返回true表示已處理,不用上層處理;false表示上交上層處理。
簡單來說,BOSS自己想搞起,就把onInterceptTouchEvent返回true,那么就沒有項目負(fù)責(zé)人和程序猿鳥事了;
BOSS不想搞,就把onInterceptTouchEvent返回false,則分發(fā)給項目負(fù)責(zé)人,如果項目負(fù)責(zé)人想搞,則onInterceptTouchEvent返回true,那就沒有程序猿鳥事;如果項目負(fù)責(zé)人也不想搞,則onInterceptTouchEvent返回tfalse,那么苦逼的程序猿只能搞起了。
當(dāng)程序猿加班加點搞定了,不想提交上層,則onTouchEvent返回true即可,否則提交給項目負(fù)責(zé)人,依次類推;
好了好了,View和ViewGrop的一些原理都分析了一下,其實復(fù)雜的自定義View就是通過這些基礎(chǔ)一點點寫出來的,看完這篇小文,大家可以去找一下開源的自定義View進(jìn)行驗證學(xué)習(xí)。