View 的存在意义
2023-08-14 20:13:20

注:本文节选自《重学安卓:过目难忘 Android GUI 关系梳理》,本文可免费阅读和全文转载,转载时须注明本文链接出处。

关于 View 体系,若假设读者已有 前置知识 铺垫在先,而 直奔 “What、How” 细枝末节,对 “Why” 绝口不提,这会使文章读起来 “没头没尾”,让人知难而退。

故这期我们 “追根溯源”,铺垫 Android View 系统来龙去脉,相信阅读后,你会对 “为何要学这些技术点、每个技术的「作用边界」到底从哪到哪、技术之间的「关系和顺序」又该如何串起来” 形成清晰理解。

文章目录一览

  • 前言
  • 拆解思路
    • Canvas 是排版的根基
    • View 和 Drawable 是排版的模板
    • Layout 和 Inflater 不过是后来者
    • include,merge,ViewStub 是解药的解药
  • 作为承上启下的小结
    • 上文 “Window” 为何一直加个双引号
    • ViewRootImpl 是怎么帮 Canvas 与窗口对应上的
    • 对症下药 “排版渲染” 性能优化指南
    • PhoneWindow 本质 及 事件分发内幕
  • 综上

Canvas 是排版的根基

所谓排版,即是将一个个图形化内容,以符合预期的方式组合排列,方便用户浏览,

根据《重学安卓:Activity 快乐你不懂》一文的推理,在从零设计的视图系统中,人们最开始可能会将排版工作交由 “Window” 承担,“Window” 不堪重负,于是演变为通过粒度更细的 View/ViewGroup 在视图树中 “递归” 来完成排版,

不过这并不意味着 View 即是排版的基石,

View 只是起到 “承载内容、且限制内容展示范围” 的作用,

例如做过 “添加水印” 的小伙伴应该都接触过 “贴图控件”,该控件是在同一个自定义 View 中绘制多个贴图元素,显而易见,View 并不是排版的最小单位,被绘制的元素才是,

故而背后的画师 Canvas,才是排版的根基:

排版依赖于绘制 —— 在排版这件事上,Canvas 可以没有 View,但 View 不能没有 Canvas。

Paint 和 Path 都是 Canvas 的助手,与 “排版输出” 存在直接关系的 “源头 API”,即 Canvas 自身。

View 和 Drawable 是排版的模板

作为 View 系统,需要提供一套标准化的 “控件开发和运行规则”,

因而在有了 “Window” 和 Canvas 基础上,还需确立 View / ViewGroup 这样规则的 排版标准,使开发者得以基于这套 “标准” 构建 具体的可复用模板(也即人们常说的 “自定义控件”)。

例如 Button、TextView、ImageView 都是 “可复用模板”,开发者日常只需与上层这些 View 打交道、无需接触底层 Canvas,无需做什么都得先手动基于 Canvas 写个数百行排版代码。

且,如对 “现成模板” 不满意,也可自行封装新模板。

与此同时,View 只是模板,如需更改图形细节,还是得接触 Canvas,

为了尽可能避免接触 Canvas,衍生出 Drawable 模板设计,用于负责更具体图形化细节:

例如通过 ShapeDrawable 来描述 View 轮廓和背景、通过 StateListDrawable 来描述 View 点击效果 等,

由此多数情况下,我们都能直接使用 “Drawable 模板” 描述排版细节,避免良莠不齐开发者 “直接与 Canvas 打交道导致不可预知隐患”。

划重点 👆 👆 👆

16、20、32dp 圆角 16、20dp 圆角 16dp 圆角
img img img

图片截自 “小米天气” 客户端:无处不在、各种规格圆角。

Layout 和 Inflater 是后来者

故此 Layout 和 LayoutInflater 是在有 View 和 Drawable 基础上,才有的后来者。

Google 模仿微软 WPF 设计,通过 XML 方式实现 一目了然 “声明式编程”可实时预览 Layout、shape、selector 乃至极大方便 Android 开发者创建和修改图形化内容。

注:Layout 对应视图树;shape、selector 对应 Drawable。

然而,XML 声明式编程 解决上述问题的同时,引入新问题:

1.XML 排版资源复用率极低。例如,为不同场景 Button 改圆角样式,无法像动态代码那样,直接代码中定义参数。哪怕不同样式间仅是 “圆角 dp” 存在细微差异,也得重建一个新 shape Drawable XML 描述文件。

2.XML 解析由 LayoutInflater 负责,LayoutInflater 通过 深度优先遍历 算法解析 XML 构建视图树,从而当 “布局嵌套层次加深” 等原因导致 View 个数过多时,XML 解析会愈加耗时。

include,merge,ViewStub 是解药的解药

故而 XML 布局只是一种 充分非必要 的视图树构建方式。

UI 性能优化涉及的 include,merge,ViewStub 等工具,其实仅用于 XML 布局情况 —— 通过减少层级解决 inflate 耗时问题

Telegram 源码 中 “全 Java 代码” 的布局构建方式,便完全用不上这些个。

划重点 👆 👆 👆

作为承上启下的小结

综上可见,无论 Button、TextView、ImageView,还是 MaterialButton、TextInputLayout 等,无论其表面如何千变万化,都只是 为在特定场景下 “符合用户直觉” 而衍生出的排版模板,本质上都是 基于 Canvas 去绘制 “特定样式” 图形化内容

而 Inflate 也不过是发生在当开发者选择通过 “声明式编程” 而非 “动态编码” 方式去构建视图树。所以若非使用 LayoutInflater,开发者甚至无须知道 include,merge,ViewStub 的存在。

至此,”Window” & Canvas;View & Drawable;Layout & Inflater;merge & ViewStub,来龙去脉一目了然。

img

试读内容完。

相关资料

重学安卓:过目难忘 Android GUI 关系梳理

版权声明

Copyright © 2019-present KunMinX 原创版权所有。

如需 转载本文,或引用、借鉴 本文 “引言、思路、结论、配图” 进行二次创作发行,须注明链接出处,否则我们保留追责权利。