Android基础学习(二十四)——View绘制-创新互联
Activity.setContentView(layoutResID:int)
PhoneWindow.setContentView(layoutResID:int)
PhoneWindow.installDecor
//mContentParent为DecorView
LayoutInflater.inflate(layoutResID:int, mContentParent:ViewGroup)
//attachToRoot为 root != null
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)
//获取layoutResID对应布局中的属性信息
AttributeSet attrs = Xml.asAttributeSet(parser)
String name = parser.getName()
//createViewFromTag方法根据tag标签属性来创建对应的View
View temp = createViewFromTag(root, name, inflaterContext, attrs)
ViewGroup.LayoutParams params = root.generateLayoutParams(attrs)
//temp为xml布局文件的根View
//rInflateChildren,递归加载根View的所有子View
LayoutInflater.rInflateChildren(parser, temp, attrs, true)
LayoutInflater.rInflate
View view = LayoutInflater.createViewFromTag
//这里的parent为temp
ViewGroup viewGroup = (ViewGroup)parent
ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs)
//view会作为rInflateChildren方法的其中一个参数
LayoutInflater.rInflateChildren
... ...
ViewGroup.addView
root.addView(temp, params)
(1)DecorView的创建时机:
成都创新互联公司服务紧随时代发展步伐,进行技术革新和技术进步,经过10年的发展和积累,已经汇集了一批资深网站策划师、设计师、专业的网站实施团队以及高素质售后服务人员,并且完全形成了一套成熟的业务流程,能够完全依照客户要求对网站进行网站设计、成都做网站、建设、维护、更新和改版,实现客户网站对外宣传展示的首要目的,并为客户企业品牌互联网化提供全面的解决方案。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
... ... //将DecorView添加到Window中
new ViewRootImpl(view.getContext(), display) //创建ViewRootImpl对象
ViewRootImpl.setView //将ViewRootImpl对象与DecorView相互关联起来
ViewRootImpl.requestLayout //完成应用程序用户界面的初次布局
ViewRootImpl.scheduleTraversals
ViewRootImpl.doTraversal
ViewRootImpl.performTraversals
ViewRootImpl.measureHierarchy
ViewRootImpl.performLayout
ViewRootImpl.performDraw
IWindowSession.addToDisplay //跨进程调用,最终在系统进程使用WindowManagerService.addWindow()来实现更新window的逻辑
(1)measure 测量
//Ask host how big it wants to be
ViewRootImpl.measureHierarchy
//获取根MeasureSpec,该值代表了对decorView的宽高的约束信息
ViewRootImpl.getRootMeasureSpec
ViewRootImpl.performMeasure
DecorView.measure
//只有满足forceLayout或needsLayout为true这两种情况才会进行实际的测量工作
View.measure
DecorView.onMeasure
FrameLayout.onMeasure
//遍历DecorView的子View,对每个子View执行measureChildWithMargins(child)
//目的是找到maxHeight和maxWidth,表示当前容器View用这个尺寸就能正常显示所有子View(同时考虑了padding和margin)
ViewGroup.measureChildWithMargins
ViewGroup.getChildMeasureSpec
child.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 us
case 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 us
case 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 ResourceType
return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
}
(2)layout 布局
ViewRootImpl.performLayout
DecorView.layout
View.layout //layout方法确定View本身的位置
View.setFrame //设定View的四个顶点的位置,即初始化l、r、t、b四个值
View.onLayout //父容器确定子元素的位置 View和ViewGroup都没有真正实现此方法
FrameLayout.onLayout
FrameLayout.layoutChildren
child.layout //child:View
View.setFrame
View.onLayout
LinearLayout.onLayout //以LinearLayout为例
LinearLayout.layoutVertical
LinearLayout.setChildFrame //调用子元素的layout方法
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 //ViewGroup没有重写draw
//绘制包含7步,2/5可略
View.drawBackground //Step 1, draw the background, if needed
View.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 绘制流程
你是否还在寻找稳定的海外服务器提供商?创新互联www.cdcxhl.cn海外机房具备T级流量清洗系统配攻击溯源,准确流量调度确保服务器高可用性,企业级服务器适合批量采购,新人活动首月15元起,快前往官网查看详情吧
标题名称:Android基础学习(二十四)——View绘制-创新互联
本文路径:http://ybzwz.com/article/djppgs.html