canvas教程

公共技术点( View 绘制流程)

字号+ 作者:H5之家 来源:H5之家 2017-09-07 15:03 我要评论( )

公共技术点( View 绘制流程),转载地址:http://p.codekk.com/blogs/detail/54cfab086c4761e5001b253f本文为Android 开源项目源码解析公共技术点中的 View 绘制

转载地址:

本文为Android 开源项目源码解析公共技术点中的 View 绘制流程 部分
分析者:lightSky

View 绘制机制 1. View 树的绘图流程

当 Activity 接收到焦点的时候,它会被请求绘制布局,该请求由 Android framework 处理.绘制是从根节点开始,对布局树进行 measure 和 draw。整个 View 树的绘图流程在ViewRoot.java类的performTraversals()函数展开,该函数所做 的工作可简单概况为是否需要重新计算视图大小(measure)、是否需要重新安置视图的位置(layout)、以及是否需要重绘(draw),流程图如下:

viewdrawflow img

View 绘制流程函数调用链

view_draw_method_chain img


图片来自https://plus.google.com/+ArpitMathur/posts/cT1EuBbxEgN
需要说明的是,用户主动调用 request,只会出发 measure 和 layout 过程,而不会执行 draw 过程

2. 概念

measure 和 layout

从整体上来看 Measure 和 Layout 两个步骤的执行:

MeasureLayout img


树的遍历是有序的,由父视图到子视图,每一个 ViewGroup 负责测绘它所有的子视图,而最底层的 View 会负责测绘自身。

具体分析
measure 过程由measure(int, int)方法发起,从上到下有序的测量 View,在 measure 过程的最后,每个视图存储了自己的尺寸大小和测量规格。 layout 过程由layout(int, int, int, int)方法发起,也是自上而下进行遍历。在该过程中,每个父视图会根据 measure 过程得到的尺寸来摆放自己的子视图。

measure 过程会为一个 View 及所有子节点的 mMeasuredWidth 和 mMeasuredHeight 变量赋值,该值可以通过getMeasuredWidth()和getMeasuredHeight()方法获得。而且这两个值必须在父视图约束范围之内,这样才可以保证所有的父视图都接收所有子视图的测量。如果子视图对于 Measure 得到的大小不满意的时候,父视图会介入并设置测量规则进行第二次 measure。比如,父视图可以先根据未给定的 dimension 去测量每一个子视图,如果最终子视图的未约束尺寸太大或者太小的时候,父视图就会使用一个确切的大小再次对子视图进行 measure。

measure 过程传递尺寸的两个类

ViewGroup.LayoutParams (View 自身的布局参数) MeasureSpecs 类(父视图对子视图的测量要求)

ViewGroup.LayoutParams
这个类我们很常见,就是用来指定视图的高度和宽度等参数。对于每个视图的 height 和 width,你有以下选择:

具体值 MATCH_PARENT 表示子视图希望和父视图一样大(不包含 padding 值) WRAP_CONTENT 表示视图为正好能包裹其内容大小(包含 padding 值)

ViewGroup 的子类有其对应的 ViewGroup.LayoutParams 的子类。比如 RelativeLayout 拥有的 ViewGroup.LayoutParams 的子类 RelativeLayoutParams。
有时我们需要使用 view.getLayoutParams() 方法获取一个视图 LayoutParams,然后进行强转,但由于不知道其具体类型,可能会导致强转错误。其实该方法得到的就是其所在父视图类型的 LayoutParams,比如 View 的父控件为 RelativeLayout,那么得到的 LayoutParams 类型就为 RelativeLayoutParams。

MeasureSpecs
测量规格,包含测量要求和尺寸的信息,有三种模式:

UNSPECIFIED
父视图不对子视图有任何约束,它可以达到所期望的任意尺寸。比如 ListView、ScrollView,一般自定义 View 中用不到,

EXACTLY
父视图为子视图指定一个确切的尺寸,而且无论子视图期望多大,它都必须在该指定大小的边界内,对应的属性为 match_parent 或具体值,比如 100dp,父控件可以通过MeasureSpec.getSize(measureSpec)直接得到子控件的尺寸。

AT_MOST
父视图为子视图指定一个最大尺寸。子视图必须确保它自己所有子视图可以适应在该尺寸范围内,对应的属性为 wrap_content,这种模式下,父控件无法确定子 View 的尺寸,只能由子控件自己根据需求去计算自己的尺寸,这种模式就是我们自定义视图需要实现测量逻辑的情况。

3. measure 核心方法

measure(int widthMeasureSpec, int heightMeasureSpec)
该方法定义在View.java类中,为 final 类型,不可被复写,但 measure 调用链最终会回调 View/ViewGroup 对象的onMeasure()方法,因此自定义视图时,只需要复写onMeasure()方法即可。

onMeasure(int widthMeasureSpec, int heightMeasureSpec)
该方法就是我们自定义视图中实现测量逻辑的方法,该方法的参数是父视图对子视图的 width 和 height 的测量要求。在我们自身的自定义视图中,要做的就是根据该 widthMeasureSpec 和 heightMeasureSpec 计算视图的 width 和 height,不同的模式处理方式不同。

setMeasuredDimension()
测量阶段终极方法,在onMeasure(int widthMeasureSpec, int heightMeasureSpec)方法中调用,将计算得到的尺寸,传递给该方法,测量阶段即结束。该方法也是必须要调用的方法,否则会报异常。在我们在自定义视图的时候,不需要关心系统复杂的 Measure 过程的,只需调用setMeasuredDimension()设置根据 MeasureSpec 计算得到的尺寸即可,你可以参考ViewPagerIndicator的 onMeasure 方法。

下面我们取 ViewGroup 的measureChildren(int widthMeasureSpec, int heightMeasureSpec)方法对复合 View 的 Measure 流程做一个分析: MeasureChild 的方法调用流程图:

MeasureLayout img

 

1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,请转载时务必注明文章作者和来源,不尊重原创的行为我们将追究责任;3.作者投稿可能会经我们编辑修改或补充。

相关文章
  • 前奏:自定义View(一)onDraw()中一些常用的属性和方法总结

    前奏:自定义View(一)onDraw()中一些常用的属性和方法总结

    2017-08-31 11:08

  • SurfaceView 典型用法

    SurfaceView 典型用法

    2017-08-30 10:03

  • 在IE下动态创建canvas使用excanvas时失效解决方法

    在IE下动态创建canvas使用excanvas时失效解决方法

    2017-08-22 15:04

  • JavaScript在IE中“意外地调用了方法或属性访问”

    JavaScript在IE中“意外地调用了方法或属性访问”

    2017-08-14 15:00

网友点评