1、Activity.setContentView
Activity.setContentView(layoutResID:int)
PhoneWindow.setContentView(layoutResID:int)
PhoneWindow.installDecor
LayoutInflater.inflate(layoutResID:int, mContentParent:ViewGroup)
LayoutInflater.inflate(layoutResID:int, root:ViewGroup, attachToRoot:boolean)
Resource res = getContext().getResources()
XmlResourceParser parser = res.getLayout(layoutResID)
LayoutInflater.inflate(parser:XmlResourceParser, root:ViewGroup, attachToRoot:boolean)
AttributeSet attrs = Xml.asAttributeSet(parser)
String name = parser.getName()
View temp = createViewFromTag(root, name, inflaterContext, attrs)
ViewGroup.LayoutParams params = root.generateLayoutParams(attrs)
LayoutInflater.rInflateChildren(parser, temp, attrs, true)
LayoutInflater.rInflate
View view = LayoutInflater.createViewFromTag
ViewGroup viewGroup = (ViewGroup)parent
ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs)
LayoutInflater.rInflateChildren
... ...
ViewGroup.addView
root.addView(temp, params)
(1)DecorView的创建时机:
Activity.setContentView->PhoneWindow.setContentView->PhoneWindow.installDecor -> PhoneWindow.generateDecor
(2)SetContentView主要做的事情:
①创建DecorView;②根据layoutResId创建View并添加到DecorView中
(3)LayoutParams
? 在View绘制中,MeasureSpec封装了从父布局传递给子布局的布局要求。对于DecorView来说,其MeasureSpec由它自身的LayoutParams决定;对于除DecorView之外的普通View,其MeasureSpec由父容器的MeasureSpec和自身的LayoutParams共同决定。
? 每个View和ViewGroup都需要通过其父容器ViewGroup的generateLayoutParams方法来生成LayoutParams对象,且每个ViewGroup子类返回的LayoutParams一般来说都需要继承于MarginLayoutParams,这样才能具备解析layout_margin的能力,且还需要再根据自身ViewGroup提供的标签属性来进一步扩展MarginLayoutParams的功能。
2、View的绘制工作
ViewRoot负责执行View绘制的整个流程,每个应用程序窗口的decorView都有一个与之关联的ViewRoot对象,这种关联关系是由WindowManager来维护的,关联关系是在Activity启动时建立的:
ActivityThread.handleResumeActivity
WindowManagerImpl.addView
WindowManagerGlobal.addView
... ...
new ViewRootImpl(view.getContext(), display)
ViewRootImpl.setView
ViewRootImpl.requestLayout
ViewRootImpl.scheduleTraversals
ViewRootImpl.doTraversal
ViewRootImpl.performTraversals
ViewRootImpl.measureHierarchy
ViewRootImpl.performLayout
ViewRootImpl.performDraw
IWindowSession.addToDisplay
(1)measure 测量
ViewRootImpl.measureHierarchy
ViewRootImpl.getRootMeasureSpec
ViewRootImpl.performMeasure
DecorView.measure
View.measure
DecorView.onMeasure
FrameLayout.onMeasure
ViewGroup.measureChildWithMargins
ViewGroup.getChildMeasureSpec
child.measure
...
View.resolveSizeAndState
ViewGroup.setMeasuredDimension
① ViewGroup没有像View一样对onMeasure方法做统一实现,ViewGroup是一个抽象类,其测量过程的onMeasure方法需要各个子类去具体实现,比如LinearLayout、RelativeLayout等。
② MeasureSpec并不是指View的测量宽高,而是根据MeasureSpec测出测量宽高。MeasureSpec是一个32位整数,由SpecMode和SpecSize两部分组成,其中,高2位为SpecMode,低30位为SpecSize。SpecMode为测量模式,SpecSize为相应测量模式下的测量尺寸。View(包括普通View和ViewGroup)的SpecMode由本View的LayoutParams结合父View的MeasureSpec生成。
SpecMode的取值可为以下三种:
-
EXACTLY: 对子View提出了一个确切的建议尺寸(SpecSize); -
AT_MOST: 子View的大小不得超过SpecSize; -
UNSPECIFIED: 对子View的尺寸不作限制,通常用于系统内部。
private static int getRootMeasureSpec(int windowSize, int rootDimension) {
int measureSpec;
switch (rootDimension) {
case ViewGroup.LayoutParams.MATCH_PARENT:
measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.EXACTLY);
break;
case ViewGroup.LayoutParams.WRAP_CONTENT:
measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.AT_MOST);
break;
default:
measureSpec = MeasureSpec.makeMeasureSpec(rootDimension, MeasureSpec.EXACTLY);
break;
}
return measureSpec;
}
protected void measureChildWithMargins(View child, int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec, int heightUsed) {
final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin + widthUsed, lp.width);
final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin + heightUsed, lp.height);
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
public static int getChildMeasureSpec(int spec, int padding, int childDimension) {
int specMode = MeasureSpec.getMode(spec);
int specSize = MeasureSpec.getSize(spec);
int size = Math.max(0, specSize - padding);
int resultSize = 0;
int resultMode = 0;
switch (specMode) {
case MeasureSpec.EXACTLY:
if (childDimension >= 0) {
resultSize = childDimension;
resultMode = MeasureSpec.EXACTLY;
} else if (childDimension == LayoutParams.MATCH_PARENT) {
resultSize = size;
resultMode = MeasureSpec.EXACTLY;
} else if (childDimension == LayoutParams.WRAP_CONTENT) {
resultSize = size;
resultMode = MeasureSpec.AT_MOST;
}
break;
case MeasureSpec.AT_MOST:
if (childDimension >= 0) {
resultSize = childDimension;
resultMode = MeasureSpec.EXACTLY;
} else if (childDimension == LayoutParams.MATCH_PARENT) {
resultSize = size;
resultMode = MeasureSpec.AT_MOST;
} else if (childDimension == LayoutParams.WRAP_CONTENT) {
resultSize = size;
resultMode = MeasureSpec.AT_MOST;
}
break;
. . .
}
return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
}
(2)layout 布局
ViewRootImpl.performLayout
DecorView.layout
View.layout
View.setFrame
View.onLayout
FrameLayout.onLayout
FrameLayout.layoutChildren
child.layout
View.setFrame
View.onLayout
LinearLayout.onLayout
LinearLayout.layoutVertical
LinearLayout.setChildFrame
child.layout
...
layout方法又会调用自身的onLayout方法。onLayout方法在View类中是空实现,大部分情况下View都无需重写该方法;onLayout方法在ViewGroup中为抽象方法,即每个ViewGroup子类都需要通过实现该方法来管理自己的所有childView的摆放位置。
(3)draw 绘制
ViewRootImpl.performDraw
ViewRootImpl.draw(boolean fullRedrawNeeded)
ViewRootImpl.drawSoftware
DecorView.draw(Canvas canvas)
View.draw
View.drawBackground
View.onDraw
View.dispatchDraw
View.onDrawForeground
View.drawDefaultFocusHighlight
draw是绘制视图的过程,在这个过程中View需要通过操作Canvas来实现自己的UI效果。
参考文章: 深入理解Android之View的绘制流程 一文读懂 View 的 Measure、Layout、Draw 流程 探索 Android View 绘制流程
|