涉及到的類:ViewRootImpl字旭,PhoneWindow据沈,ActivityThread,View鸠珠,ViewGroup巍耗, DecorView
關(guān)系:view的attachInfo中包含ViewRootImpl,PhoneWindow中包含DecorView
創(chuàng)建時機(jī):
入口:doTraversal方法渐排,其由mChoreographer實(shí)例定期調(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的默認(rèn)寬高是屏幕寬高可缚,默認(rèn)模式為MATCH_PARENT 生成的MeasureSpec為EXACTLY模式的屏幕寬高 =》根據(jù)LayoutParam生成MeasureSpec
MeasureSpec 為 32位int值霎迫,高2位表示模式,其余30位表示具體的值
生成root的MeasureSpec值之后帘靡,開始向子View遞歸測量知给,分為兩種情況:
1.View:默認(rèn)實(shí)現(xiàn)是調(diào)用onMeasure設(shè)置view的寬高(在背景寬高(backgroud)和最小寬高(minHeight||minWidth)中取大值,然后通過上一級的MeasureSpec和當(dāng)前默認(rèn)寬高生成當(dāng)前view的MeasureSpec值,生成的規(guī)則是按照上一級的mode來的涩赢,如果上一級不限制大懈甏巍(MeasureSpec.UNSPECIFIED),則為當(dāng)前View的默認(rèn)寬高筒扒,如果上一級限制大星有啊(MeasureSpec.AT_MOST|| MeasureSpec.EXACTLY),則使用上一級的設(shè)置的寬高(這樣的話花墩,當(dāng)前view在layout文件中設(shè)置的wrap_content本質(zhì)上就和match_parent的效果一樣悬秉,所以自定義View時需要自己處理下view的測量工作)
2.ViewGroup:
生成子類的MeasureSpec值在getChildMeasureSpec(這個方法是系統(tǒng)默認(rèn)實(shí)現(xiàn),用于自定義ViewGroup是的測量观游,系統(tǒng)不調(diào)用搂捧,而是重寫者自己調(diào)用的)方法中,生成邏輯是根據(jù)父View的MeasureSpec+子View的LayoutParam共同生成:MeasureSpec可以理解成 父View告訴子View自己當(dāng)前的空間狀態(tài)懂缕,請子View結(jié)合自己的要求(LayoutParam)來生成自己的空間狀態(tài)
onMeasure方法是view空間大小狀態(tài)的入口方法,結(jié)束的標(biāo)志就是其寬高被設(shè)置(setMeasuredDimension是默認(rèn)設(shè)置寬高的方法)
performLayout方法是在測量后調(diào)用的王凑,其通過調(diào)用rootView的layout開始將布局事件向View層分發(fā)搪柑,layout方法的主要目的是根據(jù)父View的可用空間和位置信息,計(jì)算出每個子View的位置和可用空間索烹,為onDraw方法做鋪墊工碾,其結(jié)束的標(biāo)志就是設(shè)置view的left top right bottom的坐標(biāo)。
為了實(shí)現(xiàn)上述目的百姓,框架層設(shè)定了layout和onLayout方法渊额,View的layout方法不能被重寫,其主要負(fù)責(zé)調(diào)用自身的onLayout方法垒拢,其中View的onLayout方法是空實(shí)現(xiàn)旬迹,ViewGroup的onLayout是抽象的,具體的onLayout是有具體的ViewGroup實(shí)現(xiàn)類實(shí)現(xiàn)的求类,比如LinearLayout奔垦,其onLayout就自身計(jì)算了子View的位置信息:
、尸疆、椿猎、
//該方法的四個參數(shù)都由父View傳遞,其描述的是當(dāng)前view在父View的位置信息寿弱,同時也告訴子View犯眠,父View的空用空間信息
void layoutVertical(int left, int top, int right, int bottom) {
final int paddingLeft = mPaddingLeft;//當(dāng)前LinearLayout的左邊留白
int childTop;
int childLeft;
// Where right end of child should go
final int width = right - left;//當(dāng)前LiearLayout自身的寬度
int childRight = width - mPaddingRight;//子View的右邊界
// 算出當(dāng)前LinearLayout可給子View使用的空間
int childSpace = width - paddingLeft - mPaddingRight;
final int count = getVirtualChildCount();
switch (majorGravity) {
…//省略其他計(jì)算childTop(子View頂部起始點(diǎn))的方式
default:
childTop = mPaddingTop;//從這里可以看出,childTop是相對于其父ViewGroup的坐標(biāo)症革,不是所有View共用一個坐標(biāo)
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屬性的值是不計(jì)算在View的高度或者寬度內(nèi)的,而padding是計(jì)算在內(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ù)責(zé)分發(fā)draw事件辞居,其會設(shè)置相關(guān)臟區(qū)域,實(shí)現(xiàn)局部重繪蛋勺,ViewRootImpl類中draw方法會在硬件繪制和軟件繪制中選擇一個進(jìn)行繪制瓦灶,這里只關(guān)注下軟件繪制步驟:
1.繪制自身背景
2.繪制自身內(nèi)容(即調(diào)用自身onDraw方法)
3.繪制子View(是一種遞歸)
4.繪制ViewOverlay覆層
5.繪制裝飾,比如滾動條抱完,actionbar等
6.繪制焦點(diǎn)視圖高亮