View的工作原理(一)——从ViewRoot和DecorView说起
来源:互联网 发布:大学java专业入门课程 编辑:程序博客网 时间:2024/04/30 08:22
前言
本文参考《Android开发艺术与探索》第四章内容及网上几篇博客,里面融入笔者的个人理解。希望能对大家理解View有所帮助。
基本概念介绍
介绍View的工作原理之前我们首先要理解DecorView和ViewRoot两个概念:
1、DecorView
DecorView是Windows中的View的最顶层View。我们可以根据下面一副图来认识它:
由这幅图我们可以看到 ,其实DecorView是一个FrameLayout,里面是一个垂直的线性布局,在线性布局中分上下两部分FrameLayout,上面一部分是TitleBar,下面是android.R.id.content,我们平常的setContentView就是将布局加载在android.R.id.content中。
2、ViewRoot
ViewRoot是连接WindowsManager和DecorView的桥梁对应于ViewRootImpl。
View的绘制流程就是从ViewRootImpl的performTraversala()方法开始的,包含三大流程:
1、Measure():[ 测量流程]
2、Layout():[布局流程]
3、Draw():[绘制流程]
这三大流程也就是View绘制的三大流程。我们可以通过下面两幅图来理解performTrarsala()方法;
什么是MeasureSpec
MeasureSpec 从名字上来看看起来是“测量规格“或是”测量说明书“。大致意思就是决定View的Measure过程。我们可以这样来理解:”MeasureSpec在很大程度上决定了一个View的尺寸规格,之所以这样说是很大程度上是因为这个过程还受父容器的影响,因为父容器影响View的MeasureSpec的创建过程。“
1、MeasureSpec
MeasureSpec代表一个32位int值,高2位代表SPecMode(测量模式),低30位代表SpecSize(在某种测量模式下的规格大小)。
- UNSPECIFIED(未指定模式)
- EXACTLY(确定模式)
- AT_MOST(最多模式)
2、MeasureSPec 和LayoutParams的对应关系
childWidthMeasureSpec = getRootMeasureSpec(desiredWindowWidth, lp.width);childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height);host.measure(childWidthMeasureSpec, childHeightMeasureSpec);
我们来再看看getRootMeasureSpec方法:
private 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; }
在android.view.ViewRootImpl 中可以看到其对应关系LayoutParams 中这三个值在内部有个对应关系,那就是
LayoutParams.MATCH_PARENT 对应 MeasureSpec.EXACTLY
.LayoutParams.WRAP_CONTENT对应 MeasureSpec.AT_MOST
默认值(也就是具体值) 对应 MeasureSpec.EXACTLY
也就是内部只有两种模式 EXACTLY 精确模式 和 AT_MOST 最大模式!
对于普通View它的measure是由ViewGroup传递而来,我们看一下ViewGroup的measureChildWidthMargins方法:
/** * Ask one of the children of this view to measure itself, taking into * account both the MeasureSpec requirements for this view and its padding * and margins. The child must have MarginLayoutParams The heavy lifting is * done in getChildMeasureSpec. * * @param child The child to measure * @param parentWidthMeasureSpec The width requirements for this view * @param widthUsed Extra space that has been used up by the parent * horizontally (possibly by other children of the parent) * @param parentHeightMeasureSpec The height requirements for this view * @param heightUsed Extra space that has been used up by the parent * vertically (possibly by other children of the parent) */ 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 // 考虑上parent的padding和child的margin + widthUsed, lp.width); final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec, mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin + heightUsed, lp.height); // 这里如果child是个ViewGroup类型,则实际会递归下去。。。 child.measure(childWidthMeasureSpec, childHeightMeasureSpec); }
我们可以清楚的看到子View的MeasureSpec创建与父容器的MeasureSpec和子元素本身的LayoutParams有关,此外还和VIew的margin 和 padding 有关,我们来看下ViewGroup的getChildMeasureSpec:
/** * Does the hard part of measureChildren: figuring out the MeasureSpec to * pass to a particular child. This method figures out the right MeasureSpec * for one dimension (height or width) of one child view. * * The goal is to combine information from our MeasureSpec with the * LayoutParams of the child to get the best possible results. For example, * if the this view knows its size (because its MeasureSpec has a mode of * EXACTLY), and the child has indicated in its LayoutParams that it wants * to be the same size as the parent, the parent should ask the child to * layout given an exact size. * * @param spec The requirements for this view * @param padding The padding of this view for the current dimension and * margins, if applicable * @param childDimension How big the child wants to be in the current * dimension * @return a MeasureSpec integer for the child */ public static int getChildMeasureSpec(int spec, int padding, int childDimension) { // 这个方法是协商型的,最终结果既可能直接由spec(parent提供的),也可能由childDimension决定 // 所以我们知道了,一个View的大小不是简单的单方面决定的,而是通过一系列条件协商的结果, // 有时会尊重parent的spec,有时会坚持自己的dimension要求 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) { // Parent has imposed an exact size on us case MeasureSpec.EXACTLY: // parent说child你应该是个确定的大小 if (childDimension >= 0) { // child正好设置了确定的大小 resultSize = childDimension; // 让child是那个确定的大小 resultMode = MeasureSpec.EXACTLY; // 设置mode为EXACTLY } else if (childDimension == LayoutParams.MATCH_PARENT) { // Child wants to be our size. So be it. resultSize = size; // 其他情况下都是parent spec中的大小,只是mode不同 resultMode = MeasureSpec.EXACTLY; } else if (childDimension == LayoutParams.WRAP_CONTENT) { // Child wants to determine its own size. It can't be // bigger than us. resultSize = size; // 不能超过size resultMode = MeasureSpec.AT_MOST; } break; // Parent has imposed a maximum size on us case MeasureSpec.AT_MOST: // parent说child你应该最大是某个值。。。 if (childDimension >= 0) { // child指定确定值了,则听child的 // Child wants a specific size... so be it resultSize = childDimension; resultMode = MeasureSpec.EXACTLY; } else if (childDimension == LayoutParams.MATCH_PARENT) { // Child wants to be our size, but our size is not fixed. // Constrain child to not be bigger than us. resultSize = size; resultMode = MeasureSpec.AT_MOST; } else if (childDimension == LayoutParams.WRAP_CONTENT) { // Child wants to determine its own size. It can't be // bigger than us. resultSize = size; resultMode = MeasureSpec.AT_MOST; } break; // Parent asked to see how big we want to be case MeasureSpec.UNSPECIFIED: // parent没对child的大小有啥要求 if (childDimension >= 0) { // child指定了确定的值,听child的 // Child wants a specific size... let him have it resultSize = childDimension; resultMode = MeasureSpec.EXACTLY; } else if (childDimension == LayoutParams.MATCH_PARENT) { // Child wants to be our size... find out how big it should // be resultSize = 0; resultMode = MeasureSpec.UNSPECIFIED; } else if (childDimension == LayoutParams.WRAP_CONTENT) { // Child wants to determine its own size.... find out how // big it should be resultSize = 0; resultMode = MeasureSpec.UNSPECIFIED; } break; } return MeasureSpec.makeMeasureSpec(resultSize, resultMode); }
parentSpecMode
childLayoutParams
EXACTLY
AT_MOST
UNSPECIFIED
dp/px
EXACTLY
chiladSize
EXACTLY
chiladSize
EXACTLY
childSize
match_parent
EXACTLY
parentSize
AT_MOST
parentSize
UNSPECIFIED
0
wrap_content
EXACTLY
parentSize
AT_MOST
parentSize
UNSPECIFIED
0
- View的工作原理(一)——从ViewRoot和DecorView说起
- ViewRoot,DecorView,MeasureSpec和View的工作原理——Android开发艺术探索笔记
- ViewRoot,DecorView,MeasureSpec和View的工作原理——Android开发艺术探索笔记
- android view的讲解 之 View的工作原理(decorView,viewRoot讲解)(一)
- 【View工作原理】ViewRoot、DecorView、MeasureSpec和LayoutParams
- 自定义View基础(一)——追根溯源,透过源码认识ViewRoot,DecorView和performTraversals方法
- View的工件原理:认识ViewRoot和DecorView
- View测量机制详解—从DecorView说起
- View测量机制详解—从DecorView说起
- ViewRoot和DecorView
- 015.ViewRoot和DecorView
- Android——View的工作原理(一)
- 读书笔记--View的工作原理(一)
- View的工作原理(一)
- 菜鸟IT技术杂谈(一)——从多任务系统原理说起
- android 的View Tree和 DecorView(Android ViewTree and DecorView)
- Android 从0开始自定义控件之 ViewRoot 与 DecorView (五)
- View的工作原理(一)之 View的三大过程 和 认识MeasureSpec
- Android Handler Message Looper
- 简单实现本地QQ聊天器(最简单版本)
- Android中Parcel的分析以及使用
- 模九的思考
- Oracle知识库学习指南
- View的工作原理(一)——从ViewRoot和DecorView说起
- 异或运算实现两个数的交换
- hdu NanoApe Loves Sequence
- Java 数字排序
- java内存模型与类加载机制
- SQLserver事务处理
- OPENCV3.0 函数学习2——cvtColor
- 逻辑运算符----java
- AFNetworking 2.x 修改适配IPV6