1、Activity.setContentView
Activity.setContentView(layoutResID:int)PhoneWindow.setContentView(layoutResID:int)PhoneWindow.installDecor//mContentParent为DecorViewLayoutInflater.inflate(layoutResID:int, mContentParent:ViewGroup)//attachToRoot为 root != nullLayoutInflater.inflate(layoutResID:int, root:ViewGroup, attachToRoot:boolean) Resource res = getContext().getResources()XmlResourceParser parser = res.getLayout(layoutResID)LayoutInflater.inflate(parser:XmlResourceParser, root:ViewGroup, attachToRoot:boolean)//获取layoutResID对应布局中的属性信息AttributeSet attrs = Xml.asAttributeSet(parser)String name = parser.getName()//createViewFromTag方法根据tag标签属性来创建对应的ViewView temp = createViewFromTag(root, name, inflaterContext, attrs)ViewGroup.LayoutParams params = root.generateLayoutParams(attrs)//temp为xml布局文件的根View//rInflateChildren,递归加载根View的所有子ViewLayoutInflater.rInflateChildren(parser, temp, attrs, true)LayoutInflater.rInflateView view = LayoutInflater.createViewFromTag//这里的parent为tempViewGroup viewGroup = (ViewGroup)parent ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs)//view会作为rInflateChildren方法的其中一个参数LayoutInflater.rInflateChildren ... ...ViewGroup.addViewroot.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.handleResumeActivityWindowManagerImpl.addViewWindowManagerGlobal.addView ... ... //将DecorView添加到Window中new ViewRootImpl(view.getContext(), display) //创建ViewRootImpl对象ViewRootImpl.setView //将ViewRootImpl对象与DecorView相互关联起来ViewRootImpl.requestLayout //完成应用程序用户界面的初次布局ViewRootImpl.scheduleTraversalsViewRootImpl.doTraversalViewRootImpl.performTraversalsViewRootImpl.measureHierarchyViewRootImpl.performLayout ViewRootImpl.performDrawIWindowSession.addToDisplay //跨进程调用,最终在系统进程使用WindowManagerService.addWindow()来实现更新window的逻辑
(1)measure 测量
//Ask host how big it wants to be
ViewRootImpl.measureHierarchy//获取根MeasureSpec,该值代表了对decorView的宽高的约束信息ViewRootImpl.getRootMeasureSpec ViewRootImpl.performMeasureDecorView.measure//只有满足forceLayout或needsLayout为true这两种情况才会进行实际的测量工作View.measure DecorView.onMeasureFrameLayout.onMeasure//遍历DecorView的子View,对每个子View执行measureChildWithMargins(child) //目的是找到maxHeight和maxWidth,表示当前容器View用这个尺寸就能正常显示所有子View(同时考虑了padding和margin) ViewGroup.measureChildWithMargins ViewGroup.getChildMeasureSpecchild.measure //child:View 若此时的子View为ViewGroup的子类,便会调用相应容器类的onMeasure()方法,其他容器View的onMeasure()方法与FrameLayout的onMeasure()方法执行过程相似... //递归执行所有子View的测量工作View.resolveSizeAndState //根据之前的测量结果确定最终对FrameLayout的测量结果并存储起来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的尺寸不作限制,通常用于系统内部。
//ViewRootImpl.getRootMeasureSpec private static int getRootMeasureSpec(int windowSize, int rootDimension) {int measureSpec;switch (rootDimension) {case ViewGroup.LayoutParams.MATCH_PARENT:// Window can't resize. Force root view to be windowSize.measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.EXACTLY);break;case ViewGroup.LayoutParams.WRAP_CONTENT:// Window can resize. Set max size for root view.measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.AT_MOST);break;default:// Window wants to be an exact size. Force root view to be that size.measureSpec = MeasureSpec.makeMeasureSpec(rootDimension, MeasureSpec.EXACTLY);break;}return measureSpec; }
//ViewGroup.measureChildWithMargins
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); //参数是父View用于约束其测量的
}
//ViewGroup.getChildMeasureSpec
//展现了根据父View的MeasureSpec和子View的LayoutParams生成子View的MeasureSpec的过程//参数:// spec为父View的MeasureSpec// padding为父View在相应方向的已用尺寸加上父View的padding和子View的margin// childDimension为子View的LayoutParams的值
public static int getChildMeasureSpec(int spec, int padding, int childDimension) {int specMode = MeasureSpec.getMode(spec);int specSize = MeasureSpec.getSize(spec);// 现在size的值为父View相应方向上的可用大小int size = Math.max(0, specSize - padding);int resultSize = 0;int resultMode = 0;switch (specMode) {// Parent has imposed an exact size on uscase MeasureSpec.EXACTLY:if (childDimension >= 0) {// 表示子View的LayoutParams指定了具体大小值(xx dp)resultSize = childDimension;resultMode = MeasureSpec.EXACTLY;} else if (childDimension == LayoutParams.MATCH_PARENT) {// 子View想和父View一样大resultSize = size;resultMode = MeasureSpec.EXACTLY;} else if (childDimension == LayoutParams.WRAP_CONTENT) {// 子View想自己决定其尺寸,但不能比父View大 resultSize = size;resultMode = MeasureSpec.AT_MOST;}break;// Parent has imposed a maximum size on uscase MeasureSpec.AT_MOST:if (childDimension >= 0) {// 子View指定了具体大小resultSize = childDimension;resultMode = MeasureSpec.EXACTLY;} else if (childDimension == LayoutParams.MATCH_PARENT) {// 子View想跟父View一样大,但是父View的大小未固定下来// 所以指定约束子View不能比父View大resultSize = size;resultMode = MeasureSpec.AT_MOST;} else if (childDimension == LayoutParams.WRAP_CONTENT) {// 子View想要自己决定尺寸,但不能比父View大resultSize = size;resultMode = MeasureSpec.AT_MOST;}break;. . .}//noinspection ResourceTypereturn MeasureSpec.makeMeasureSpec(resultSize, resultMode);
}
(2)layout 布局
ViewRootImpl.performLayoutDecorView.layoutView.layout //layout方法确定View本身的位置View.setFrame //设定View的四个顶点的位置,即初始化l、r、t、b四个值View.onLayout //父容器确定子元素的位置 View和ViewGroup都没有真正实现此方法FrameLayout.onLayoutFrameLayout.layoutChildrenchild.layout //child:ViewView.setFrameView.onLayoutLinearLayout.onLayout //以LinearLayout为例LinearLayout.layoutVerticalLinearLayout.setChildFrame //调用子元素的layout方法child.layout... //重复上述调用
layout方法又会调用自身的onLayout方法。onLayout方法在View类中是空实现,大部分情况下View都无需重写该方法;onLayout方法在ViewGroup中为抽象方法,即每个ViewGroup子类都需要通过实现该方法来管理自己的所有childView的摆放位置。
(3)draw 绘制
ViewRootImpl.performDrawViewRootImpl.draw(boolean fullRedrawNeeded) //视图重绘ViewRootImpl.drawSoftwareDecorView.draw(Canvas canvas)View.draw //ViewGroup没有重写draw//绘制包含7步,2/5可略View.drawBackground //Step 1, draw the background, if neededView.onDraw //Step 3, draw the content 不同的View有不同的实现View.dispatchDraw //Step 4, draw the children ViewGroup重写了此方法View.onDrawForeground //Step 6, draw decorations (foreground, scrollbars)View.drawDefaultFocusHighlight // Step 7, draw the default focus highlight
draw是绘制视图的过程,在这个过程中View需要通过操作Canvas来实现自己的UI效果。
参考文章:
深入理解Android之View的绘制流程
一文读懂 View 的 Measure、Layout、Draw 流程
探索 Android View 绘制流程