5c394647a6a37ed519093b540ca92010
重学安卓:不如我们 从零开始设计一套 视图系统

温馨提示:如果这是第一次接触《重学安卓》,可借助 这份在 GitBook 上维护的 “导读” 来快速了解《重学安卓》专栏、获取它的目录、试读内容,以及了解它的最新动态 和 发展状况。

截至目前,专栏已对 体系化文章 做了 810 余次修订,数十位群友告诉我 受专栏的启发 他们也开启了写作之路。群里不定期会有小伙伴讨论适配问题、分享原创的开源库 和 提供内推机会,订阅后可随时进群交流。

前言

很高兴见到你!

上一期,我们以 冲出技术舒适圈 的方式,站在 用户体验交互设计 的视角,来从 根源的根源 将 滑动冲突的由来和普适解决方案 给过了一遍,相信阅读过这篇文章的小伙伴,对 滑动冲突 的印象已跑赢全网 99.9% 的 Android 开发者。😉

与此同时,在上周开源的 Jetpack MVVM 最佳实践中,我在详情页中应用了《滑动冲突》一篇所介绍的详情页连贯滑动的用户体验设计。

尽管许多用户因此而惊叹,“怎么会这么流畅”,我个人还是觉得不够满意:当我单手操作时,在作为曲面屏的 Galaxy S8 Plus 上,很难够得到屏幕左边缘 乃至呼出抽屉,这体验 “太难了”。

无动于衷 有动于衷

于是,通过对这么一个微小的、容易让人熟视无睹的现象的观测,我们确立了这么一条普适的观点:

在用户单手握持且 贪婪地、懒洋洋地 浏览时,用户希望的是,用尽可能小的 大拇指的划动幅度,来实现本不大可能、但心中预期的结果 和 大动作

那么,像图 2 那样 符合用户直觉 和 心理预期 的体验,从技术角度来讲,为什么能实现呢?或者换一种方式提问:到底是怎样的视图系统,使得我们有机会实现 这样的用户体验呢?

下面我们就带着这个问题,来从 根源的根源 开始探寻,为什么会有我们现在这样的视图系统、这个系统中包含的重要成分都有哪些、以及在这样的视图系统下,我们可以通过何种方式 来实现上述我们所预期的效果。

文章目录一览

  • 前言
  • 作为 13 年前才面市的新型用户体验
    • First of all
    • Secondly
    • And then
    • However
  • So
    • 视图系统的坐标系 为何如此设计?
    • 视图坐标 为何如此设计?
    • 为什么能做到 惯性滑动?
    • 惯性滑动的测量值 为何如此设计?
    • 为什么能做到 位移?
    • 事件分发 为何要如此设计?
  • 综上

作为 13 年前才面市的新型用户体验

世界上的第一台 全触屏智能手机,是 2007 年苹果发布的 iPhone。在那之前,人类社会根本没有这样的移动设备。

换言之,第一个设计并使之面市的创造者,重新定义 并引领了 后来的十几年里 人们对移动设备的 用户体验习惯

在上一篇中我们介绍过,由于人们使用 智能手机 的动机 多是 打发无聊 或 查阅信息,因而 最普遍的操作意图就是浏览,而最普遍的操作方式之一 就是 单手握持 并通过大拇指 划动屏幕

当然,创造者们 早人们一步 想象到未来的这个情况。

所以不妨继续发挥我们的想象力,想象一下,当时的创造者们,对这样一种 全触屏设备 有着怎样的规划、以及为了实现这些规划,需要存在哪些 最少必要的成分 呢?

First of all

我们需要确保,这台 全触屏设备,是个如假包换的 冯诺依曼计算机。无论是在 2007 年的当时 移动蜂窝数据网络处于 2G 的情况,还是未来 5 年内处于 5G 的情况。

因为就算是云手机,作为全触屏移动设备 也需要 能在本地 独立处理一些 最少必要的计算 —— 世界上绝不会存在 完全不具备算力的 智能手机。

(云计算并不代表 将 计算的任务 全部外包给云端,而是 将 特定的计算任务 外包给云端。移动设备 必然需要 本地算力,以便至少能够支撑 操作系统对硬件资源的调度 和对上层软件的支持)

图片来自《钢铁苍穹》—— 1945 年的社会人 对 2012 年的社会人的 计算机 的评价。

此外,正因为是 冯诺依曼计算机,所以这台独立的 全触屏设备,务必包含 输入设备 和 输出设备

输出设备 很好理解,例如 屏幕、外放 等等,当然我们今天谈的是 视图系统,所以这里我们只关注 屏幕。

与此同时,与传统台式机不同的是,全触屏设备的 输入设备 也是屏幕,用户 对可视化元素 发起指令,就是靠这块屏幕。

至此,我们可以做个小结:

1.我们构想的 全触屏移动设备,务必是一台 具备独立算力的 冯诺依曼计算机。

2.从而它的背景状况是,通过 屏幕来负责 可视化元素的展示,和 接收用户通过屏幕发起的指令。

而光是确立了这两点,就不难望见 全触屏设备 操控可视化元素 的整个逻辑 —— 分两步:

一步是 接收指令

另一步是 基于指令 实时地 重新渲染屏幕内容

于是,我迫不及待地要进入下一环节,来使这个逻辑完整落地。

Secondly

通过上一小节的追溯和分析,我们知道了 所处的背景环境是,全触屏可移动设备 务必是一台 冯诺依曼计算机、而全触屏 同时兼任 输入设备和输出设备 两大职能,

因而,为了 在这样的背景下,完成 符合用户直觉的、所见即所得 的 操控体验,我们的视图系统 会分别准备两套组件,来基于这两种设备的表现 对用户体验负责,

其中,触摸事件组件 对应的是 输入设备,而 排版渲染组件 对应的是 输出设备

于是,对输入设备来说,我们需要一个或多个坐标系,来为我们的触控操作 提供 实时坐标反馈,从而能够 基于实时坐标 和 某些预设好的指标 来辨别我们在触屏上的操作,以便输送 正确的、符合预期的指令,去通知 排版渲染组件 完成相应的工作。

同时,对于输出设备来说,我们同样需要一个或多个坐标系,来 根据 触控指令或布局 实时排版和渲染 可视化元素

换言之,如果一个 可视化元素跟随你的手指移动了,这个过程必然是分两步走的:先是根据触控获取了坐标轨迹和指令,然后是根据坐标、指令、横纵差等计算出加速度等数值,并根据这些数值 通过某些组件,来实现 元素内容的惯性滑动,或元素本身的位移。

而之所以分两步走 却能符合用户直觉,又是因为这些触控信号被设计为 “实时发送”,例如每隔 16ms 截取一次(当前的坐标),也即 每一段从直觉上 看似轻松连贯的 划动操作,实际都包含无数次 “输入指令” 和 “输出渲染” 的交替执行。

那么至此,我们可以做个小结:

1.为了在以 冯诺依曼计算机 为背景 的条件下 实现 符合用户直觉的 所见即所得 操作,我们需要分别为 输入设备和输出设备 准备 触摸事件组件 和 排版渲染组件,分别用于 实时输送指令 和 实时展示结果

2.触摸事件组件 和 排版渲染组件 分别需要一个或多个坐标系,来为触控和排版提供坐标依据,以及基于 坐标轨迹 和 预设的指标 来辨别输入的指令,以期 通知对应的渲染工作的执行。

在确立了这两点后,我们终于可以开始 对视图系统做一个 具体的构思!

And then

作为创造者们,我们首先从 输入系统开始构思。

—— 请对着手中 四角圆润、黑乎乎的 “板砖” 发呆一下:通常我们的触控操作 都会有哪些呢?

1.点击(Click),点击按钮,即按下(Down)、松开(Up),

2.划动(Fling),划动内容,即按下(Down)、移动(Move)、松开(Up),并且让内容 保持惯性 滑动一小段距离。

3.拖拽(Drag),拖拽元素,即按下(Down)、移动(Move)、松开(Up)。

由此可见,划动和拖拽在使用过程中,需要一些指标来做出区分:

对于内容要能惯性滑动,而对于元素要能拖拽 —— 要是反其道而行的话:

内容划动松开后 立刻停止滑动,就会与直觉不符、令人感到十分突然;

而元素拖拽松开后 还在漂移,就会与预期不符、令人感到十分不安。

而假如所处场景既是(列表)内容,又要能拖拽(列表)item 的话,就需进一步提供一些指标来区分,例如 长按(LongClick)或其他方式,让场景默认处于划动模式、而被动处于拖拽模式。

至此,我们可以做个小结:

1.常见的触控操作包括 点击、划动、拖拽,而它们又可被分解为 按下、移动、松开 等 原子操作

2.在不同的场景下 需要恰当地处理好 后续的滑动渲染,以免造成 不符合直觉的 用户体验。

于是,从每一次的 完整的触控过程中,我们可以最终得到一条坐标轨迹、在期间被识别和定性的原子操作( Down、Move or Up),以及基于原子操作而被定性的宏观操作(Click、Fling or Drag),而这些宏观操作、原子操作 以及 坐标轨迹,都会被反馈到 排版渲染组件,从而 排版渲染组件:

1.根据坐标差,来判断出滑动的方向,

2.根据坐标差,结合系统预设的坐标截取周期(例如 16ms),来判断滑动的加速度,

3.根据被辨别的宏观操作模式,来决定是继续惯性滑动,还是戛然而止。

从而,人们所期待的 触控事件组件 的 背景基石,就此落地

然而,触控事件 并不是 输入设备 “一个人的自嗨”,它的存在也要兼顾 排版渲染系统。
因而,在 排版渲染系统的背景下,也就有了 Android 的事件分发系统。具体是这样的:

However

为了塑造符合用户直觉的、所见即所得的 用户体验,视图排版的方式被设计为,父容器 嵌套 子控件,且越是作为子的控件,就越是置于前景、能被人们第一个所接触。

所以,考虑到这一点,触控事件组件 的坐标系,不仅包含 屏幕绝对坐标系,也包含 相对坐标系

相对坐标系的存在,主要是为了 方便对 视图区域内 与触摸坐标相关的计算

例如当实现 滑动拼图验证码 的时候,使用相对坐标系获取 相对坐标差,要比使用 屏幕绝对坐标系获取 相对坐标差 来得 更简便,因为后者还需手动将 当前触控视图与存在于外部的其他间距 排除。

(注意 “相对坐标系” 和 “相对坐标差” 在概念上的区别,相对坐标系是指 坐标系本身是建立在随时可能发生位移的视图原点,而非方位绝不变化的屏幕原点)

那么,既然提到了 坐标和坐标系,我们该怎样设计 坐标系呢?

当时的设计者们,一定是直接照抄 PC 系统中的坐标系,因为这套坐标系 自可视化操作系统 被施乐发明以来,已经得到几十年的演化 并最终成熟定型了。

然而尽管如此,也不妨碍我们进一步去思考,可视化坐标系 为何要如此设计:

top Created with Sketch.