• [译]Android view 测量布局和绘制的流程


    原文链接

    创造优秀的用户体验是我们开发者的主要目标之一.为此, 我们首先要了解系统是如何工作的, 这样我们才可以更好的与系统配合, 从它的优点中获益, 规避它的缺陷.


    之前关于Android渲染过程的文章

    这次我们主要关注Measure/Layout(测量和布局)的阶段, 这些阶段决定了视图的大小和位置, 以便于我们能够绘制它.

    pic

    Step 1: Measure 测量

    目标: 确定是图的大小

    视图的大小包含其子视图的大小, 且必须符合其父视图的要求

    视图的大小由2个方面决定:

    • 测量宽度与测量高度 - 视图在其父视图中想要占据多大空间.这是此阶段我们需要的大小.
    • 宽度与高度(也就是绘制高度与绘制宽度) - 在绘制和布局阶段, 视图在屏幕上视图的大小. 这个大小会在step2中算出.

    How does it work?

    • 自顶向下递归的遍历view树.
    • 每个视图将规格传递给子视图.

    如何实现?父视图通过MeasureSpec类的三种选项之一来决定子视图的宽高:

    1. UNSPECIFIED - 未指定, 子视图可以获得任意大小
    2. EXACTLY - 指定, 子视图应该有指定大小
    3. AT_MOST - 最大, 子视图最大可达到某个值

    每个视图的宽高设置由ViewGroup.LayoutParams的3种选项决定:

    1. 一个明确的数字
    2. MATCH_PARENT 子视图想要和父视图一样大
    3. WRAP_CONTENT 子视图想要和自身的内容一样大
    • 测量过程在onMeasure(int widthMeasureSpec, int heightMeasureSpec)函数中完成

    • 当这个函数返回时, 每个视图都必须有measuredWidthmeasuredHeight (可由调用super()完成), 否则会抛出IllegalStateException.

    Notice that this process sometimes a negotiation between a view and its children, and so measure() may be called more than once. More on that on a later post.

    • 注意这个过程有时需要视图和其子视图互相协商, 因此measure()方法可能会被调用不止一次.

    Since the traversal is top down, and each parent tells its children the requirements, we end up with our goal achieved:
    Each view’s measured size includes its children size, and fit its parent requirements.

    因为遍历是自顶向下的, 并且没个父视图告诉子视图其需求, 我们最终的目标是:
    每个视图的计算大小包含了子视图的大小, 并符合其父视图的要求

    Step 2: Layout 布局

    目标: 为视图及其子视图设定位置和大小(绘制宽度和绘制高度)

    • 与step1类似: 自顶向下递归遍历视图树.
    • 每个父视图通过上一个计算的大小定位其所有子视图的位置.
    • 定位由onLayout(boolean changed, int left, int top, int right, int bottom)方法完成, 其中left, top, right, bottom是相对于其父视图的.
    • 当重写onLayout()方法时, 必须调用每个子视图的layout()方法.

    Step 3: Draw 绘制

    • 当大小和位置都确定之后, 视图可以据此绘制自己.
    • onDraw(Canvas)方法中Canvas对象生成(或者是更新)一系列OpenGl-ES命令(displayList)发送给GPU.

    这就是绘制的过程! 但是当我们改变了视图的属性时发生了什么呢? 由动画, 用户输入, 或者我们决定改变他们的时候.


    When things change… 当发生改变时

    当视图属性改变是, 视图会通知系统. 取决于改变的属性, 视图调用下列之一:

    • invalidate - 此时只会调用视图的onDraw()
    • requestLayout() - 会传递到根视图, 然后调用整个过程(测量->布局->绘制)

    对于需要布局的情况有一个经典的小例子: 我们在一个RelativeLayout中有两个关系相对的视图. 如果其中一个改变了大小 - 一定会导致另一个重新定位, 也可能导致父视图改变大小.所以我们改变了一个视图的属性, 导致了整个布局都过期了.

    requestLayout

    这种情况提醒我们高效的布局是很重要的, 这样布局才能流程的执行并且不会导致跳帧.

  • 相关阅读:
    【USACO2017JAN】 Promotion Counting
    【POJ 3468】 A Simple Problem with Integers
    【HDU 1754】 I Hate It
    【Codeforces 20C】 Dijkstra?
    【POJ 2407】 Relatives
    BZOJ5249 九省联考2018IIIDX(线段树+贪心)
    BZOJ5251 八省联考2018劈配(网络流)
    BZOJ4200 NOI2015小园丁与老司机(动态规划+上下界网络流)
    BZOJ3876 AHOI/JSOI2014支线剧情(上下界网络流)
    LOJ117 有源汇有上下界最小流(上下界网络流)
  • 原文地址:https://www.cnblogs.com/fortitude/p/8632171.html
Copyright © 2020-2023  润新知