4.1初识ViewRoot和DecorView

2020-01-16 09:05栏目:龙竞技官网
TAG:

ViewRoot和DecorView

  1. ViewRoot对应于ViewRootImpl类
  2. 是连接WindowManager和DecorView的纽带
  3. 倡导并做到View的三大流程
  4. ViewRoot供给和DecorView创建联系。

图片 1WechatIMG125.jpeg

  1. ViewRoot的performTraversals(卡塔尔国带头View的绘图流程,依次调用performMeasure(State of Qatar、performLayout(卡塔尔和performDraw(卡塔尔
  2. performMeasure(卡塔尔(قطر‎最后施行父容器的measure(卡塔尔方法,并依此推行全数子View的measure方法。
  3. performLayout()和performDraw()同理

  4. measure决定了View的宽/高,衡量后能够透过getMeasuredWidth/Height来获得View衡量后的宽/高,除特种情状外该值等于View最后的宽/高

  5. layout决定了View的极端坐标以致实际View的宽/高:实现后得以通过getTop/Bottom/Left/Right获取极限坐标,并经过getWidth/Height(卡塔尔(قطر‎拿到View的最后宽/高
  6. draw决定了View的来得,最后将View展现出来MeasuredWidth/height != getWidth/Height(卡塔尔(قطر‎的场景:修正View的布局参数并举办再一次布局后,就能够以致度量!= 实际值

  7. DecorView是生龙活虎品View,本质正是多个FrameLayout

  8. 含蓄了两个部分,标题栏和内容栏
  9. 内容栏id是content,也正是activity中setContentView所设置的一些,最后将构造增多到id为content的FrameLayout中
  10. 获取content:ViewGroup content = findViewById(R.android.id.content)
  11. 拿到设置的View:content.getChidlAt

  12. Activity对象在ActivityThread中创建完结后,会将DecorView加多到Window中

  13. 况且会创建ViewRootImpl,调用ViewRoot的setView方法将ViewRootImpl和DevorView创立关联
root = new ViewRootImpl(view.getContext(), display)root.setView(view, wparams, panelParentView)
  1. DecorView等View的三大流程须要经过ViewRoot实现

一、ViewRoot

ViewRoot对应于ViewRootImpl类,是连连WindowManage和DecorView的大桥。当Activity对象被创立,会将DecorView增加到Window中,同有的时候间创设ViewRootImpl对象并与DecorView创设关系,具体请参见源码

图片 2

源码

还要也是View绘制的骨干,肩负layout、measure和draw三大流程。接下来简单介绍一下绘制流程
View的绘图流程首先从ViewRoot的PerformTraversals开首,也正是我们耳熟能详的measure度量、layout定位和draw绘制。具体流程见下图,小编就不啰嗦太多了,终归那不是非同儿戏。

图片 3

PerformTraversals专门的学业流程

MeasureSpec

  1. MeasureSpec是风度翩翩种“度量法规”或许“度量表达书”,决定了View的度量进程
  2. View的MeasureSpec会根据本身的LayoutParamse和父容器的MeasureSpec生成。
  3. 终极依据View的MeasureSpec测量出View的宽/高(度量时数据实际不是最终宽高卡塔尔(قطر‎

  4. MeasureSpec代表三个33位int值,高2位是SpecMode,低30个人是SpecSize

  5. SpecMode是指衡量情势
  6. SpecSize是指在某种度量形式下的大大小小
  7. 类MesaureSpec提供了用于SpecMode和SpecSize打包和解包的秘诀
  • UNSPECIFIED:父容器不对View有此外限定,日常用于系统内部
  • EXACTLY:精准形式,View的结尾大小正是SpecSize钦命的值(对应于LayoutParams的match_parent和求实的数值)
  • AT_MOST:最大值情势,大小不能够超过父容器钦命的值SpecSize(对应于wrap_content)
  1. View的MeasureSpec是索要通过本身的LayoutParams和父容器的MeasureSpec一齐工夫调控
  2. DecorView是例外,其自身MeasureSpec由窗口尺寸和自作者LayoutParams协同决定
  3. MeasureSpec意气风发旦显明,onMeasure中就可以规定View的度量宽/高

  4. View自身布局参数为实际dp/px数值,方式:EXACTLY,尺寸:本人尺寸(不管父容器的MeasureSpec卡塔尔国

  5. View为match_parent, 模式:EXACTLY/AT_MOST由父容器MeasureSpec决定,尺寸:父容器近来可用大小
  6. View为wrap_content,模式:AT_MOST,尺寸:父容器可用尺寸
  7. 当父容器为UNSPECIFIED时,View为切实数值时准绳不改变;别的match_parent/wrap_content,形式均为:UNSPECIFIED,尺寸:0
  8. UNSPECIFIED平时用于系统里头频繁measure的境况,没有须要关注该格局。

图片 4WechatIMG126.jpeg

二、DecorView

DecorView作为顶层View,它本人是三个FrameLayout,其里面含有四个竖直方向的LinearLayout,在此个LinearLayout内部含有贰个titlebar和叁个content,大家能够透过ViewGroup content = findViewById(普拉多.android.id.content卡塔尔(قطر‎获取content。如若想要获得大家设置进去的View,能够通过content.getChildAt(indexState of Qatar。在Activity中大家平常应用的setContentView(GL450.layout.xxx卡塔尔本质上正是将布局文件加多进content构造。
View层的事件都要先经过DecorView,然后再传递给我们的View。

图片 5

DecorView结构图

View的做事流程

  1. measure:衡量——分明View的衡量宽/高
  2. layout:构造——鲜明View的最终宽/高和八个极点之处
  3. draw:绘制——将View绘制到荧屏上

三、理解MeasureSpec

MeasureSpec代表一个34位int值,高2位代表SpecMode,指的是度量方式;低叁十五位代表SpecSize,表示的是特定衡量方式下的规格大小。我们先看看MeasureSpec内部一些常量的定义代码

图片 6

图片 7

image.png

1、View的measure进程及大旨
  1. View的measure方法是final类型方法——申明该措施不能够被重载
  2. View的measure方法会调用onMeasure方法,onMeasure会调用setMeasuredDimension方法设置View宽/高的度量值

1、SpecMode分类

View.MeasureSpec.AT_MOST
不得超过父容器钦命的大徐熙娣(Elephant Dee卡塔尔国pecSize。对应于LayoutParams中的wrap_content

View.MeasureSpec.EXACTLY
父容器已经济检察测出View所需准确大小,即SpecSize所钦命的值。对应于LayoutParams中的match_parent

View.MeasureSpec.UNSPECIFIED
父容器不对View有别的限定,平日用于系统内部

2、View的onMeasure源码要点
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { //1. setMeasuredDimension方法设置View宽/高的测量值 setMeasuredDimension( //2. 第一个参数是获得的测量宽/高(通过getDefaultSize获取) getDefaultSize(getSuggestedMinimumWidth(), //3. 获取的建议最小的宽/高 widthMeasureSpec), getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));}
  1. setMeasuredDimension方法设置View宽/高的衡量值(度量值通过getDefaultSize获取)
  2. getDefaultSize用于获取View的衡量宽/高

2、MeasureSpec和LayoutParams对应提到

在View度量的时候,系统会将LayoutParams在父容器的约束下转账成MeasureSpec,然后再明确View度量后宽高。接下来看看MeasureSpec是什么得到的。

图片 8

image.png

对于普通View,其MeasureSpec有父容器MeasureSpec和笔者LayoutParams协同决定。因而针对富有差别MeasureSpec的父容器和View本身差别的LayoutParams,View的MeasureSpec就有各个状态:

  • 当View接受一定宽高的时候,不管父容器MeasureSpec是什么样,View的MeasureSpec都以准确情势还要服从LayoutParams的轻重。
  • 当View的宽高为match_parent,借使父容器是精准方式,那么其情势也为精准情势还要大小是父容器的剩下空间;借使父容器是最大方式,那么其形式也为最大方式还要大小不会当先父容器的多余空间。
  • 当View的宽高为wrap_content,不管父容器是怎么情势,View的方式总是最大化並且不超过父容器的盈余空间。

图片 9

image.png

3、View的getDefaultSize源码要点(决定了View宽高的度量值卡塔尔
//1. 获取View宽和高的测量值 public static int getDefaultSize(int size, int measureSpec) { int result = size; int specMode = MeasureSpec.getMode(measureSpec); int specSize = MeasureSpec.getSize(measureSpec); switch  { //2. UNSPECIFIED模式时,宽/高为第一个参数也就是getSuggestedMinimumWidth()获取的建议最小值 case MeasureSpec.UNSPECIFIED: result = size; break; //3. AT_MOST(wrap_content)和EXACTLY(match_parent/具体值dp等)这两个模式下,View宽高的测量值为当前View的MeasureSpec中指定的尺寸specsize case MeasureSpec.AT_MOST: case MeasureSpec.EXACTLY: result = specSize; break; } return result; }

四、View的干活流程

View的办事流程首要分为measure、layout和draw,即衡量、架交涉制图。个中measure鲜明View度量宽高,layout明确View最后宽高的三个顶峰坐标,draw依照最后宽高和终点坐标将View绘制到显示器上。

1、View的measure过程
View的measure方法是final类型的,意味着无法重写此措施。可是measure方法会调用onMeasure方法,由此大家最首要深入分析onMeasure方法。老规矩先上源码

图片 10

图片 11

image.png

getDefaultSize方法大家只需关怀AT_MOST和EXACTLY情势,轻便地讲,其实该方法再次回到的正是MeasureSpec的specSize,即度量后大小。必要在乎的是,纵然View的终十分大小是在layout阶段鲜明的,可是差相当少全数情状下度量大小都等于最终大小。
关于UNSPECIFIED这种情景,平时只用于系统里面衡量,所以那边临时不作表明。
从getDefaultSize方法的兑现来看,View的宽高有SpecSize决定,因而大家得以吸取如下结论,直接接轨View的自定义控件需求再行onMeasure方法并且安装wrap_content时的本身大小,不然效果雷同match_parent。那么什么样解决那一个主题材料吗?大家只需在wrap_content时钦定View的宽高就可以,至于宽高值视情状而定,具体可参谋系统控件源码。

图片 12

image.png

2、ViewGroup的measure过程
比较于View的测量进度,ViewGroup的度量进度就百废待举得多了。因为除去成功自个儿本人的度量工作,还要去遍历子View况兼达成衡量。ViewGroup提供了贰个叫measureChildren的章程

图片 13

图片 14

image.png

measureChild的思忖相当粗略,首先抽出子成分的LayoutParams,然后经过getChildMeasureSpec创造子成分的MeasureSpec,接着将MeasureSpec直接交给View的measure方法进行衡量就可以。

现行反革命杜撰风度翩翩种供给,借使大家在Activity运行时须求获得某些View的宽高值,可是在onCreate、onResume等方式中是回天无力得到View的宽高值的,那是因为View的绘图和Activity的生命周期是不一齐的。那么有怎么着措施能够消除这一个主题素材呢,肯定是风流倜傥对。

  • (1)android.app.Activity#onWindowFocusChanged
    本条格局的意义是Activity分界面包车型地铁View已经开头化完毕了,因而在这里方法内取View的宽高是没难题的。可是这么些法子会被频频调用,因而应该考虑真实意况严谨采取。

  • (2)View.post()
    因而post一个消息至音信队列尾部,然后等待Looper实施此新闻,此时早已得以取到View宽高了。标准代码如下:

![](https://upload-images.jianshu.io/upload_images/6337201-3dd9d794a300159a.png)

image.png
  • (3)ViewTreeObserver
    行使ViewTreeObserver的接口能够产生那几个效果。当View树的情况或可以预知性产生转移时,onGlobalLayoutListener内的onGlobalLayout会产生回调,那时候能够拿到View的宽高。不过此回调会爆发高频,使用也需注意。
![](https://upload-images.jianshu.io/upload_images/6337201-5b134697f9998c39.png)



![](https://upload-images.jianshu.io/upload_images/6337201-c650311c1a8ba809.png)

image.png
4、View的getSuggestedMinimumWidth/Height(State of Qatar源码要点
//获取建议的最小宽度protected int getSuggestedMinimumWidth() { return (mBackground == null) ? mMinWidth : max(mMinWidth, mBackground.getMinimumWidth;}
  1. 倘诺View未有背景,View的小小宽度就为android:minWidth那个参数钦命的值(mMinWidthState of Qatar,未有一点名则默认为0
  2. 黄金时代经View有背景,会从mMinWidth和背景的纤维宽度中取最大值。
  3. 背景的一丝一毫宽度(getMinimumWidth本质便是Drawable的原有宽度(ShapeDrawable无原始宽度,BitmapDrawable有原始宽度——图片的尺码卡塔尔国
5、View的wrap_content和match_parent效果雷同的原故解析
  1. 依赖View的onMeasure方法中的getDefaultSize方法,大家能够发将来二种方式下,View的度量值等于该View的度量规格MeasureSpec中的尺寸。
  2. View的MeasureSpec本质是由作者的LayoutParams和父容器的MeasureSpec决定的。
  3. 当View为wrap_content时,该View的方式为AT_MOST,且尺寸specSize为父容器的多余空间大小。
  4. 当View为match_parent时,该View的形式跟随父容器的格局(AT_MOST/EXACTLYState of Qatar, 且尺寸specSize为父容器的多余空间大小。
  5. 故而getDefaultSize中不管View是哪类方式,最后衡量宽/高均等于尺寸specSize,由此三种性子效果是一丝一毫平等的(View的尺寸充满了父容器的多余空间卡塔尔
  6. 除非给定View固定的宽/高,View的specSize才会等于该固定值。
6、自定义View须要重写onMeasure方法,并写明二种情势的处理措施
//1. 重写onMeasure,特殊处理wrap_content的情况 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec); int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec); int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec); int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec); if(widthSpecMode == MeasureSpec.AT_MOST && heightSpecMode == MeasureSpec.AT_MOST){ //2. 均为wrap_content时, 将值设置为android:minWidth/Height属性指定的值 setMeasuredDimension(mWidth, mHeight); }else if(widthSpecMode == MeasureSpec.AT_MOST){ //3. 哪个为wrap_content哪个就用android:minXXX属性给定的最小值 setMeasuredDimension(mWidth, heightSpecSize); }else if(heightSpecMode == MeasureSpec.AT_MOST){ setMeasuredDimension(widthSpecSize, mHeight); } }
7、ViewGroup的measure流程
  1. ViewGroup没有onMeasure方法,只定义了measureChildren方法(onMeasure依照差异结构难以统风流倜傥卡塔尔国
  2. measureChildren中遍历全数子成分并调用measureChild方法
  3. measureChild方法中会获取子View的MeasureSpec,然后调用子成分View的measure方法开展度量

版权声明:本文由龙竞技官网发布于龙竞技官网,转载请注明出处:4.1初识ViewRoot和DecorView