现在的位置: 首页 > 综合 > 正文

Android界面绘制流程——–How Android Draws Views

2017年09月19日 ⁄ 综合 ⁄ 共 1973字 ⁄ 字号 评论关闭

在学习自定义组件的时候,偶然发现官网的这篇文章,觉得不错,于是试着翻译出来。一是为了和大家分享,二是为了加深自己的印象。水平有限,翻译过程中有不正确的地方,欢迎指正。

原文地址为: How Android Draws Views

当一个Activity呈现在用户面前时,其布局将被绘制出来。android系统将处理绘制的过程,但是,前提是Activity需要提供其布局的根节点。

绘制过程从布局的根节点开始,然后对整个布局树型结构(layout tree)进行测量并绘制,绘制过程沿着布局树型结构(layout tree)进行,依次渲染其中的各个View。在这个遍历过程中,遇到ViewGroup时,每个ViewGroup(注意,ViewGroup也属于View的一种)将依次绘制包含于其中的View;遇到View时,View将绘制自身。由于绘制的过程是沿着布局树型结构(layout tree)遍历的,所以,布局树型结构(layout tree)中的父节点将先于孩子结点被绘制,而兄弟结点则按照它们出现的顺序进行绘制。

绘制布局需要经过两个pass process(传递过程?),分别是measure pass和layout pass(可以翻译为测量的传递过程,计算布局的传递过程?)。measure pass是在方法measure(int,int) 中进行的,measure pass自上而下地递归遍历布局结构。在递归过程中,每个View都沿着布局树传递dimension specifications,在measure pass结束时,每个View都将获得自身的测量结果。第二个pass process是layout pass,这个过程在在layout(int,int,int,int) 中进行,并且和measure
pass一样是自上而下的(top-down),在layout pass过程中,每个父结点都将利用measure pass的测量结果,计算出各个子结点的位置。

当一个View对象的measure()方法执行完之后,该View以及其子结点的getMeasureWidth()和getMeasureHeight()的返回值必须已经被赋值,(其实这两个方法分别返回View对象中的变量mMeasureWidth和mMeasureHeigth,这两个变量的注释是:Height/Width as measured during measure pass)。各个View的测量宽度和测量高度必须遵从其父结点传递过来的限制条件(其实就是上文提到的dimension specifications)。这样才能保证在measure
pass结束时,每个父节点能接受其各个子结点的测量结果。一个parent View可能不止触发其子结点的measure()方法依次,比如,父结点可能第一次首先测量每个没指定大小的子结点,测量出它们需要占用多大的空间,而如果经过第一轮测量,各个子结点的计算结果相互之间有所冲突,则父结点将再次调用各个子结点的onMeasure()方法测量各个子结点。

measure pass使用两个类去传递关于测量大小的值。这两个类分别是ViewGroup.LayoutParams和MeasureSpec。View对象可以借助ViewGroup.LayoutParams对象去告诉它的父结点,它需要怎样的大小和放置在什么位置。最基础的ViewGroup.LayoutParams类只能描述View的高和宽。它可以指定的高和宽有以下三类

  • 某个确切的数值
  • MATH_PARENT,代表该View希望和它的父结点一样大(减去padding)
  • WRAP_CONTENT,代表该View希望大小足够包围住它的内容(加上padding)

ViewGroup.LayoutParams有几个子类,它们分别为ViewGroup几个对应的子类而存在。比如,RelativeLayout有自己的一个ViewGroup.LayoutParams的子类,它有一个特殊的功能,可以讲子结点设置在水平方向上的中点,或者垂直方向上的中点。

MeasureSpec对象是由父结点传递给子节点的一个对象,父结点对子结点的限制信息保存在该对象中。MeasureSpec对象可以有以下三种模式:

  • UNSPECIFIED:父结点对子结点的大小没有任何要求。
  • EXACTLY:  父结点要求其子节点的大小指定为某个确切的值。其子节点以及其他子孙结点都需要适应该大小。     
  • AT MOST:父结点要求其子节点的大小不能超过某个最大值,其子节点以及其他子孙结点的大小都需要小于这个值     

该文章中还提到两个小tips:

  • invalidate():调用该方法,可以强制重新绘制界面
  • requestLayout():当前布局大小改变时,可以调用该方法,刷新布局。

抱歉!评论已关闭.