涉及到的類:ViewRootImpl,PhoneWindow,ActivityThread,View,ViewGroup, DecorView
關(guān)系:view的attachInfo中包含ViewRootImpl,PhoneWindow中包含DecorView
創(chuàng)建時機:
入口:doTraversal方法,其由mChoreographer實例定期調(diào)用或者view自己設(shè)置重會
performTraversals 方法開始整理view繪制的相關(guān)流程,涉及到:
1.測量,具體的測量規(guī)則是什么,root的測量是怎么來的
2.為啥會有relayoutWindow,涉及到的window和activity的關(guān)系是什么
3.performLayout入口和相關(guān)規(guī)則
4.performDraw入口和相關(guān)規(guī)則,activity和surface的關(guān)系
root的默認寬高是屏幕寬高,默認模式為MATCH_PARENT 生成的MeasureSpec為EXACTLY模式的屏幕寬高 =》根據(jù)LayoutParam生成MeasureSpec
MeasureSpec 為 32位int值,高2位表示模式,其余30位表示具體的值
生成root的MeasureSpec值之后,開始向子View遞歸測量,分為兩種情況:
1.View:默認實現(xiàn)是調(diào)用onMeasure設(shè)置view的寬高(在背景寬高(backgroud)和最小寬高(minHeight||minWidth)中取大值,然后通過上一級的MeasureSpec和當前默認寬高生成當前view的MeasureSpec值,生成的規(guī)則是按照上一級的mode來的,如果上一級不限制大小(MeasureSpec.UNSPECIFIED),則為當前View的默認寬高,如果上一級限制大?。∕easureSpec.AT_MOST|| MeasureSpec.EXACTLY),則使用上一級的設(shè)置的寬高(這樣的話,當前view在layout文件中設(shè)置的wrap_content本質(zhì)上就和match_parent的效果一樣,所以自定義View時需要自己處理下view的測量工作)
2.ViewGroup:
生成子類的MeasureSpec值在getChildMeasureSpec(這個方法是系統(tǒng)默認實現(xiàn),用于自定義ViewGroup是的測量,系統(tǒng)不調(diào)用,而是重寫者自己調(diào)用的)方法中,生成邏輯是根據(jù)父View的MeasureSpec+子View的LayoutParam共同生成:MeasureSpec可以理解成 父View告訴子View自己當前的空間狀態(tài),請子View結(jié)合自己的要求(LayoutParam)來生成自己的空間狀態(tài)

onMeasure方法是view空間大小狀態(tài)的入口方法,結(jié)束的標志就是其寬高被設(shè)置(setMeasuredDimension是默認設(shè)置寬高的方法)
performLayout方法是在測量后調(diào)用的,其通過調(diào)用rootView的layout開始將布局事件向View層分發(fā),layout方法的主要目的是根據(jù)父View的可用空間和位置信息,計算出每個子View的位置和可用空間,為onDraw方法做鋪墊,其結(jié)束的標志就是設(shè)置view的left top right bottom的坐標。
為了實現(xiàn)上述目的,框架層設(shè)定了layout和onLayout方法,View的layout方法不能被重寫,其主要負責調(diào)用自身的onLayout方法,其中View的onLayout方法是空實現(xiàn),ViewGroup的onLayout是抽象的,具體的onLayout是有具體的ViewGroup實現(xiàn)類實現(xiàn)的,比如LinearLayout,其onLayout就自身計算了子View的位置信息:
、、、
//該方法的四個參數(shù)都由父View傳遞,其描述的是當前view在父View的位置信息,同時也告訴子View,父View的空用空間信息
void layoutVertical(int left, int top, int right, int bottom) {
final int paddingLeft = mPaddingLeft;//當前LinearLayout的左邊留白
int childTop;
int childLeft;
// Where right end of child should go
final int width = right - left;//當前LiearLayout自身的寬度
int childRight = width - mPaddingRight;//子View的右邊界
// 算出當前LinearLayout可給子View使用的空間
int childSpace = width - paddingLeft - mPaddingRight;
final int count = getVirtualChildCount();
switch (majorGravity) {
…//省略其他計算childTop(子View頂部起始點)的方式
default:
childTop = mPaddingTop;//從這里可以看出,childTop是相對于其父ViewGroup的坐標,不是所有View共用一個坐標
break;
}
for (int i = 0; i < count; i++) {
final View child = getVirtualChildAt(i);
if (child == null) {
childTop += measureNullChild(i);
} else if (child.getVisibility() != GONE) {
final int childWidth = child.getMeasuredWidth();
final int childHeight = child.getMeasuredHeight();
final LinearLayout.LayoutParams lp =
(LinearLayout.LayoutParams) child.getLayoutParams();
int gravity = lp.gravity;
if (gravity < 0) {
gravity = minorGravity;
}
final int layoutDirection = getLayoutDirection();
final int absoluteGravity = Gravity.getAbsoluteGravity(gravity, layoutDirection);
switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
case Gravity.CENTER_HORIZONTAL:
childLeft = paddingLeft + ((childSpace - childWidth) / 2)
+ lp.leftMargin - lp.rightMargin;
break;
case Gravity.RIGHT:
childLeft = childRight - childWidth - lp.rightMargin;
break;
case Gravity.LEFT:
default:
childLeft = paddingLeft + lp.leftMargin;
break;
}
if (hasDividerBeforeChildAt(i)) {
childTop += mDividerHeight;
}
childTop += lp.topMargin;//加上子View自身的topMargin,可以看出 margin屬性的值是不計算在View的高度或者寬度內(nèi)的,而padding是計算在內(nèi)的
setChildFrame(child, childLeft, childTop + getLocationOffset(child),
childWidth, childHeight);
childTop += childHeight + lp.bottomMargin + getNextLocationOffset(child);
i += getChildrenSkipCount(child, i);
}
}
、、、
private void setChildFrame(View child, int left, int top, int width, int height) {
//將View在父布局中的可用位置告訴子View,如果子View自己沒有異議,就會設(shè)置自身的位置信息為在父View的可用位置
child.layout(left, top, left + width, top + height);
}
、、、
performDraw則負責分發(fā)draw事件,其會設(shè)置相關(guān)臟區(qū)域,實現(xiàn)局部重繪,ViewRootImpl類中draw方法會在硬件繪制和軟件繪制中選擇一個進行繪制,這里只關(guān)注下軟件繪制步驟:
1.繪制自身背景
2.繪制自身內(nèi)容(即調(diào)用自身onDraw方法)
3.繪制子View(是一種遞歸)
4.繪制ViewOverlay覆層
5.繪制裝飾,比如滾動條,actionbar等
6.繪制焦點視圖高亮