Android 开发艺术探索笔记(十一) 之 View 的工作原理(一)

1、ViewRoot 和 DecorView

  • ViewRoot 对应于 ViewRootImpl 类,是连接 WindowManagerDecorView 的纽带,View 的 measure,layout,draw 都是通过 ViewRoot 来完成的。
  • View 的三大流程:measure 过程决定了 View 的宽/高,measure 完成后,可以通过 getMeasuredWidth()/getMeasuredHeight() 获取 View 测量后的宽/高,这个宽高几乎就是 View 最终的宽/高了,除了特殊情况之外;layout 过程决定了 View 的四个顶点的坐标和实际的宽/高,完成后可以通过 getTop()getLeft()getRight()getBottom() 获取四个顶点的坐标,还可以通过 getWidth()getHeight() 获取 View 的最终的宽/高;draw 过程就决定了 View 的显示,只有 draw() 完成才能将 View 的内容呈现在屏幕上。

  • DecorView 其实是一个 FrameLayout,里面包含一个 LinearLayout,这个 LinearLayout 包含两个子View,一个是 Titlebar,另一个是 id 为 android.R.id.content 的 FrameLayout,可以通过 ViewGroup content = (ViewGroup) findViewById(android.R.id.content);来获取这个 contentView,然后通过 content.getChild(0) 来获取我们设置进去的 View。

2、MeasureSpec

MeasureSpec 代表一个 32 位 int 值,高 2 位代表 SpecMode(测量模式),低 30 位代表 SpecSize(某种测量模式下的规格大小)。

MeasureSpec 有 3 类:

  • UNSPECIFIED:父容器不对 View 做任何限制,要多大给多大。这种情况一般用于系统内部,表示一种测量状态。

  • EXACTLY:父容器已经检测出 View 所需要的大小,View 的大小就是 SpecSize 指定的值。这种情况对应于 LayoutParams 的 match_parent 和具体数值两种情况。

  • AT_MOST:父容器指定了一个可用的大小 SpecSize,View 最大不能超过这个 SpecSize 的值。这种情况对应于 LayoutParams 的 wrap_content。

注意:MeasureSpec 不是由该 View 的 LayoutParams 唯一决定的,而是在父容器的约束下(父容器的 MeasureSpec),将该 View 的 LayoutParams 转换成对应的 MeasureSpec,从而决定该 View 的测量后(onMeasure())的宽/高。

普通 View 的 MeasureSpec 的确定方法:

  • 当 View 的 LayoutParam 是固定的宽/高的模式时,不管父容器的 MeasureSpec 是什么,View 的 MeasureSpec 都是 EXACTLY(精确模式)并且其大小遵循 LayoutParams 的大小

  • 当 View 的宽/高是 match_parent 时,如果父容器的 MeasureSpec 是精准模式,View 的 MeasureSpec 也是 EXACTLY(精确模式)并且其大小就是父容器的剩余空间;如果父容器是 AT_MOST(最大模式),那么View 的 MeasureSpec 也是 AT_MOST(最大模式)并且其大小不会超过父容器的剩余空间

  • 当 View 的宽/高是 wrap_content 时,不管父容器的 MeasureSpec 是精准模式还是最大模式,View 的 MeasureSpec 都是 AT_MOST(最大模式)并且其大小不会超过父容器的剩余空间

3、View 的工作原理

  • measure 过程

    measure 过程分两种情况,第一种是只是一个原始的 View ,那么通过 measure 方法就完成了其测量过程;如果是一个 ViewGroup,除了完成自己的测量过程外,还要遍历去调用所有子元素的 measure 方法,各个子元素再递归执行这个流程。

    获取 View 测量后的宽/高的方法:

    • 1.Activity/View # onWindowFocusChanged

    • 2.view.post(runnable)

    • 3.ViewTreeObserver

    • 4.view.measure(int widthMeasureSpec,int heightMeasureSpec)

  • layout 过程

    layout 是 ViewGroup 用来确定子元素的位置的,当 ViewGroup 的位置被确定后,在 onLayout()中会遍历所有子元素并调用其 layout 方法,在 layout 方法中 onLayout() 被调用。

    父容器确定了自己的位置,即四个顶点的坐标之后,确定子元素的位置。

  • draw 过程

    1.绘制背景 background.draw(canvas);
    2.绘制自己 ondraw();
    3.绘制 children dispatchDraw();
    4.绘制装饰 onDrawScrollBars();

你可能感兴趣的:(Android 开发艺术探索笔记(十一) 之 View 的工作原理(一))