View框架浅析

来源:互联网 发布:百度云盘mac历史版本 编辑:程序博客网 时间:2024/05/16 17:42

参考文章如下,这几篇文章很好,图文并茂,我这里只是取了一些原文的概念放到这里方便复习:
http://www.jianshu.com/p/a3014f8442b0

整体View结构

控件主要分为两类,一类是View,一类是ViewGroup
如下是View的一些原理

  • 所有的View都是矩形的
  • View是不能添加子View的,ViewGroup可以
  • Activity之所以能加载并且控制View,是因为它包含了一个Window,所有的图形化界面都是由View显示的而Service之所以称之为没有界面的activity是因为它不包含有Window,不能够加载View;
  • 一个View有且只能有一个父View;
  • 在Android中Window对象通常由PhoneWindow来实现的,PhoneWindow将一个DecorView设置为整个应用窗口的根View,即DecorView为整个Window界面的最顶层View。
  • DecorView是FrameLayout的子类,它继承了FrameLayout,即顶层的FrameLayout的实现类是Decorview,它是在phoneWindow里面创建的;
  • 顶层的FrameLayout的父view是Handler,Handler的作用除了线程之间的通讯以外,还可以跟WindowManagerService进行通讯;windowManagerService是后台的一个服务,它控制并且管理者屏幕;
  • 一个应用可以有很多个window,其由windowManager来管理,而windowManager又由windowManagerService来管理;

Measure测量一个View的大小

  • 1、MeasureSpe描述了父View对子View大小的期望。里面包含了测量模式和大小。
  • 2、MeasureSpe类把测量模式和大小组合到一个32位的int型的数值中,其中高2位表示模式,低30位表示大小而在计算中使用位运算的原因是为了提高并优化效率。
  • 3、Android中提供了三种测量模式,EXACTLY 精确模式,AT_MOST最大值模式,UNSPECIFIED不确定模式。默认的是去定的模式。
  • 4、系统最终会调用setMeasureDimension(int measuredWidth, int measuredHeight)方法将测量后的宽高设置进去,从而完成测量工作。
  • 5、当我们需要自定义的时候,需要自定义的measureWidth()方法和measureHeight()方法对宽高进行了重新定义。从MeasureSpec对象中获取到测量模式和测量大小值,通过判断测量模式,返回不同的测量值。

Layout摆放一个View的位置

  • 1、首先是layout函数, Layout方法中接受四个参数,是由父View提供,指定了子View在父View中的左、上、右、下的位置。父View在指定子View的位置时通常会根据子View在measure中测量的大小来决定。
  • 2、子View的位置通常还受有其他属性左右,例如父View的orientation,gravity,自身的margin等等,特别是RelativeLayout,影响布局的因素非常多。
  • 3、layout和onLayout之间有一个setFrame方法。setFrame方法是一个隐藏方法,所以作为应用层程序员来说,无法重写该方法。该方法体内部通过比对本次的l、t、r、b四个值与上次是否相同来判断自身的位置和大小是否发生了改变。如果发生了改变,将会调用invalidate请求重绘。
  • 4、onLayout是ViewGroup用来决定子View摆放位置的,各种布局的差异都在该方法中得到了体现。

Draw画出View的显示内容

View的绘制也是遵循一定的顺序:

  • 1、画背景
  • 2、画边缘
  • 3、画自身: ondraw方法
  • 4、画子View: dispatchDraw方法
  • 5、画滚动条

draw()->onDraw()->dispatchDraw()

  • 1、draw是由ViewRoot的performTraversals方法发起,它将调用DecorView的draw方法,并把成员变量canvas传给给draw方法。而在后面draw遍历中,传递的都是同一个canvas。所以android的绘制是同一个window中的所有View都绘制在同一个画布上。
  • 2、onDraw()方法的使用,因为我们的目的就是自定义View,所以当我们测量好了一个View之后,我们就可以间的重写onDraw()这个方法,并在Canvas对象上来绘制所需要的图形。在onDraw()中就有一个参数,该参数就是Canvas canvas对象,使用这个对象即可进行绘图操作。
  • 3、之所以要传入一个bitmap,是因为传进来的bitmap与通过这个bitmap创建的Canvas画布是紧紧联系在一起的,这个过程称之为装载画布。
  • 4、dispatchDraw
    先根据自身的padding剪裁画布,所有的子View都将在画布剪裁后的区域绘制。遍历所有子View,调用子View的computeScroll对子View的滚动值进行计算。根据滚动值和子View在父View中的坐标进行画布原点坐标的移动,根据子在父View中的坐标计算出子View的视图大小,然后对画布进行剪裁。

View框架的measure机制

1.mesure干了什么

Android中View有自使用的机制,把各种尺寸值,经过计算,得到具体的像素值。measure过程会遍历整棵View树,然后依次测量每个View真实的尺寸。具体是每个ViewGroup会向它内部的每个子View发送measure命令,然后由具体子View的onMeasure()来测量自己的尺寸。最后测量的结果保存在View的mMeasuredWidth和mMeasuredHeight中,保存的数据单位是像素。

2.如何合理的测量一颗View树

一个View需要把它内部的match_parent或者wrap_content转换成具体的像素值。
在measure过程中,ViewGroup会根据自己当前的状况,结合子View的尺寸数据,进行一个综合评定,然后把相关信息告诉子View,然后子View在onMeasure自己的时候,一边需要考虑到自己的content大小,一边还要考虑的父布局的限制信息,然后综合评定,测量出一个最优的结果。

3.ViewGroup是如何向子View传递限制信息

谈到传递限制信息,那就是MeasureSpec类了,该类贯穿于整个measure过程,用来传递父布局对子View尺寸测量的约束信息。简单来说,该类就保存两类数据。
1、子View当前所在父布局的具体尺寸。
2、父布局对子View的限制类型。
还是包括那三种类型精确、最大和适应

4.源代码的分析

我们知道,整棵View树的根节点是DecorView,它是一个FrameLayout,所以它是一个ViewGroup,所以整棵View树的测量是从一个ViewGroup对象的measure方法开始的。

View的测量过程

measure->onMeasure->setMeasuredDimension

(1)measure
该方法会调用onMeasure()方法,所以只有onMeasure能被也必须要被override。public final void measure(int widthMeasureSpec, int heightMeasureSpec);
父布局会在自己的onMeasure方法中,调用child.measure ,这就把measure过程转移到了子View中。

(2)onMeasure
具体测量过程,测量view和它的内容,来决定测量的宽高(mMeasuredWidth mMeasuredHeight )。该方法中必须要调用setMeasuredDimension(int, int)来保存该view测量的宽高。

(3)setMeasuredDimension
protected final void setMeasuredDimension(int measuredWidth, int measuredHeight);
当View测量结束后,把测量结果保存起来,具体保存在mMeasuredWidth和mMeasuredHeight中。

ViewGroup的测量过程

measureChildren->measureChild->measureChildWithMargins->getChildMeasureSpec

(1)measureChildren
让所有子view测量自己的尺寸,需要考虑当前ViewGroup的MeasureSpec和Padding。跳过状态为gone的子view

(2)measureChild
测量单个View,需要考虑当前ViewGroup的MeasureSpec和Padding。

(3)measureChildWithMargins
测量单个View,需要考虑当前ViewGroup的MeasureSpec和Padding、margins。

(4)getChildMeasureSpec
measureChildren过程中最困难的一部分,为child计算MeasureSpec。该方法为每个child的每个维度(宽、高)计算正确的MeasureSpec。目标就是把当前viewgroup的MeasureSpec和child的LayoutParams结合起来,生成最合理的结果。

getChildMeasureSpec的过程分析

根据当前自身的状况,以及特定子View的尺寸参数,为特定子View计算一个合理的限制信息

  • 首先判断限定信息的模式
  • 如果是精确的模式,如果子容器申请的是固定尺寸,就用这个固定尺寸,如果子容器希望和父容器一样大,就使用父容器的尺寸,还有就杀包裹内容啦。
  • 最大尺寸模式
  • 当前容器尺寸不限定模式

自定义View控件

需要覆写onMeasure来正确测量自己。最后都需要调用setMeasuredDimension来保存测量结果
一般来说,自定义View的measure过程伪代码为:

  • 首先根据measureSpc来获得mode和size
  • 根据不同的模式来设置不同的值,如果当前是精确模式直接设置大小即可,如果是最大值模式,就设置为内容尺寸和父布局尺寸的最小值。
    如果是不限定模式,那么当前值有多大就设置为多大。
  • 然后通过setMeasureDimension(viewSize)来设置

自定义ViewGroup控件

不但需要覆写onMeasure来正确测量自己,可能还要覆写一系列measureChild方法,来正确的测量子view,比如ScrollView。或者干脆放弃父类实现的measureChild规则,自己重新实现一套测量子view的规则,比如RelativeLayout。最后都需要调用setMeasuredDimension来保存测量结果。

  • ViewGroup开始测量自己的尺寸
  • ViewGroup为每个Child计算限定信息
  • 将上一步的限定信息传递给子View,然后子View需要measure自己的尺寸
  • 子View测量完成后,ViewGroup就可以获取每个子View测量后的尺寸
  • ViewGroup会根据自己的状况计算自己的尺寸
  • ViewGroup保存自己的尺寸

View框架的layout机制

什么是layout过程

就是给View找到合适的位置
该位置是View相对于父布局坐标系的相对位置,而不是以屏幕坐标系为准的绝对位置。这样更容易保持树型结构的递归性和内部自治性。而View的位置,可以无限大,超出当前ViewGroup的可视范围,这也是通过改变View位置而实现滑动效果的原理。

layout过程干了什么

  • 由于View是以树结构进行存储,所以典型的数据操作就是递归操作,所以,View框架中,采用了内部自治的layout过程。

  • 每个叶子节点根据父节点传递过来的位置信息,设置自己的位置数据,每个非叶子节点,除了负责根据父节点传递过来的位置信息,设置自己的位置数据外(如果有父节点的话),还需要根据自己内部的layout规则(比如垂直排布等),计算出每一个子节点的位置信息,然后向子节点传递layout过程。

  • 对于ViewGroup,除了根据自己的parent传递的位置信息,来设置自己的位置之外,还需要根据自己的layout规则,为每一个子View计算出准确的位置(相对于子View的父布局的位置)。

  • View对象的位置信息,在内部是以4个成员变量的保存的,分别是mLeft、mRight、mTop、mBottom。他们的含义如图所示。

源代码

protected void onLayout(boolean changed, int left, int top, int right, int bottom);

ViewGroup中,只需要覆写onLayout方法,来计算出每一个子View的位置,并且把layout流程传递给子View。

  • 在onLayout中首先遍历子View
  • 然后在遍历的for循环中计算每一个子View的位置信息
  • 计算的规则包括当前布局规则/子View 的测量尺寸/子View所在的位置索引
  • 通过child.layout来设置上面的位置信息

结论

一般来说,自定义View,如果该View不包含子View,类似于TextView这种的,是不需要覆写onLayout方法的。而含有子View的,比如LinearLayout这种,就需要根据自己的布局规则,来计算每一个子View的位置。


View框架的draw

什么是layout过程

View框架中,draw过程主要是绘制View的外观。ViewGroup除了负责绘制自己之外,还需要负责绘制所有的子View。而不含子View的View对象,就负责绘制自己就可以了。

draw过程的主要流程

  • 绘制 backgroud(drawBackground)
  • 如果需要的话,保存canvas的layer,来准备fading(不是必要的步骤)
  • 绘制view的content(onDraw方法)
  • 绘制children(dispatchDraw方法)
  • 如果需要的话,绘制fading edges,然后还原layer(不是必要的步骤)
  • 绘制装饰器、比如scrollBar(onDrawForeground)

源代码

我们知道,整棵View树的根节点是DecorView,它是一个FrameLayout,所以它是一个ViewGroup,所以整棵View树的测量是从一个ViewGroup对象的draw方法开始的。

下面是draw方法的流程

  • 绘制background
  • 绘制View的content
  • 绘制children(dispatchView)
  • 绘制装饰器
1 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 梅花浇水浇多了怎么办 深水井底下花管进水量小怎么办 盆景对节白腊树叶尖干枯怎么办 四季海棠茎软了怎么办 玫瑰海棠烂根了怎么办 格丽海棠花腌了怎么办 养殖场不能雨污分流怎么办 药店买的药贵了怎么办 神经损伤小便少尿不出来怎么办 手指夹伤出血了怎么办 手指夹破流血了怎么办 喂了宝宝熊胆粉怎么办 不小心擦伤了皮怎么办 吃了减肥药拉肚子怎么办 遇到他心通的人怎么办 被茅山术害了怎么办 鱼缸鱼身上烂了怎么办 鳄鱼龟皮肤烂了怎么办 墨水渗透进皮肤里了怎么办 中药渗透到皮肤里怎么办 甘露醇渗透到皮肤下怎么办 水银弄到眼睛里怎么办 水银粘到皮肤上怎么办 榴莲和虾同吃了怎么办 吃榴莲和虾中毒怎么办 榴莲和虾一起吃怎么办 狗被别人下毒了怎么办 大掌门2没存元宝怎么办 大掌门2转换阵容怎么办 率土之滨s2绝版怎么办 异界气息的装备怎么办 vivo电板没电了怎么办 门套拼接有缝隙怎么办 公司如果一直没有上税收入怎么办 赛车输了俩百万怎么办 交pk金员工不交怎么办 员工不想交pk金怎么办 心悦光环领错角色怎么办 心悦光环领错了怎么办 扑克牌1到13洗后怎么办 南通长牌没钱了怎么办