手势功能
屏幕驱动程序生成鼠标左键移动
势可包括许多不同的动作。它可以是计算机屏幕上的手指移动,而且就像挥动手臂或与别人握手一样,摇头甚至也算得上是一种手势(姿势)。依我看来,只将屏幕输入视为唯一手势来源似乎有点狭隘。当今的许多设备都有多种传感器,其中包括触摸屏、加速感应器、罗盘、GPS 装置及照相机等。摇晃、翻转、旋转设备,甚至对着镜头微笑都可以解释为需要软件进行响应的手势(姿势),而这些仅仅是我们所知传感器能够识别的手势。
功能概述
自 2002 年第一批 Windows Mobile 设备问世以来,触摸屏已经成为该设备的代名词;不过,据称 Windows Mobile 6.5 才是第一个向开发人员公开所有形式手势支持的版本。那么什么是手势,它又为何如此风靡?
Windows Mobile Professional 设备的传统触摸屏提供的是鼠标模拟图面,它通过屏幕驱动程序接口生成鼠标左键和鼠标移动消息。在处理和传送这些消息时,屏幕和触笔就像实际的鼠标一样,它们之间存在一定的相似性:鼠标与屏幕上的触笔一样,以线性方式产生一系列位置坐标,都可用作非常精确的指针设备。
两者之间也有一些区别。比如,鼠标发送的位置信息与按钮信息无关,而触摸屏总是模拟按下的左键,仅在发生屏幕接触时才发送位置信息。只要触摸屏与鼠标保持足够的相似性,此模式就仍然有用武之地。然而,随着现代话机屏幕的不断增大,用户食指已然迅速变成最自然而直观的触笔。在个人消费市场上,精度要求和易失性都比较高的触笔很快就会过时,取而代之的是对粗犷的交互式界面的需求,这种界面会勾起消费者的触摸冲动,促进与用户之间的情感联系。
与触笔笔尖的精确度要求形成鲜明的对比,手指活动提供了一种不同的操作模式,它与鼠标输入截然不同。输入数据不再是精确定位的,线性输入轨迹往往更接近于绕月轨道而非直线。区别不仅在于数据;输入结果也应产生与输入序列相符的平滑动态响应。现在,鼠标模式显然已不再适用,我们需要一种不同的新模式来帮助描述这种输入并了解其响应方式。答案就是手势。
触摸手势
在详细了解触摸手势之前,我们先在更宽泛的层面思考一下一般意义上的手势。手势可包括许多不同的动作。它可以是计算机屏幕上的手指移动,而且就像挥动手臂或与别人握手一样,摇头甚至也算得上是一种手势(姿势)。依我看来,只将屏幕输入视为唯一手势来源似乎有点狭隘。当今的许多设备都有多种传感器,其中包括触摸屏、加速感应器、罗盘、GPS 装置及照相机等。摇晃、翻转、旋转设备,甚至对着镜头微笑都可以解释为需要软件进行响应的手势(姿势),而这些仅仅是我们所知传感器能够识别的手势。
Windows Mobile 6.5 中的体系结构在设计上考虑到了这一点,因此将手势来源及识别过程与该手势的路由、传送和响应分开。虽然我们只有触摸手势识别功能,但一旦具备相应传感器和识别组件,就可以通过系统提供新的手势。新的传感器和识别软件可由硬件制造商添加,并集成到现有手势传送体系结构中, 图 3 五种核心手势
您可能会奇怪为什么要使用其中的一些手势,因为鼠标行为似乎已足以获取相同的信息。比如,选择手势就像单击按钮一样,而平移手势就像鼠标移动一样。这五种手势的重要性体现在两个方面。
一致性:系统通过两个消息接收鼠标点击操作:鼠标按钮按下和弹起。识别点击操作的具体行为是识别该操作的控件所特有的功能。比如,当鼠标按钮按下和弹起的位置都位于窗口边界内时,按钮控件会将这两个动作识别为一次点击。相比之下,ListView 控件对其列表中的每一项也会识别同样的事件。选择手势通过使用一致的参数,使其识别独立于控件。用于手势识别的距离阈值可识别分辨率(或者更准确地说,可识别到点/英寸),并且是为使用最广泛的指形(手指形状范围异常广泛)而设的。因此,可在大小不同的屏幕上使用相同的物理距离,从而在设备之间提供一致性。
路由:手指不是准确指针设备,当用户在移动或走动中时尤为如此,因此应用程序需要最大化可触摸目标区域,这一点至关重要。手势传送组件借助特定的规则完成此任务,从而提升了这些简单手势的价值。
路由
手势信息通过新的 WM_GESTURE 消息进行传送,而且该消息与所有窗口消息一样,也有包含消息详细信息的关联参数 DWORD wParam 和 LONG lParam。WM_GESTURE 消息参数包含手势 ID 作为 wParam 以指示要传送的手势,并包含完整手势信息的句柄作为 lParam。鼠标消息总是发送到鼠标坐标位置所在的最顶层窗口(不包括鼠标捕获情形),但对于手势来说,这些规则将有所不同。手势消息与之不同,它们总是发送到构成完整手势序列的第一个触摸点相应的最顶层窗口。对于屏幕移动距离容差很小的选择、按住和双选手势来说,这一细微差异的影响不大。但对于平移手势,差别就很大了。在开始平移时,所有平移消息都会发送到开始平移时所在的窗口,即使平移移动使触摸点移出原始窗口也如此。
同样,滚动手势的识别位置也会从其原始触摸点延伸到许多像素的距离。但将滚动消息路由到与上述平移消息相同的窗口中比较合理,因为用户在该原始控件中启动输入序列并打算以其作为目标。鉴于平移手势通常与直接操控有关(即像在桌面上移动纸张一样在屏幕上移动内容),因此此路由非常合理,因为在屏幕上移动内容期间,在最初触摸时指下的控件或屏幕点应始终保持在指下。
未处理的消息路由
手势消息路由的另一个不同寻常的方面是对未处理手势消息所采取的操作。与所有未处理的消息一样,它们最终也被发送到 DefWindowProc 以进行默认处理。当 DefWindowProc 收到手势消息时,它会尝试查找当前窗口的父窗口并将该消息发送到该父窗口。这样做可最大限度地扩大可供用户使用的可触摸区域。
为便于解释,请考虑一个包含一些子标签控件的可滚动窗口。父窗口实现平移和滚动手势响应逻辑,从而在可视图面中上下移动子标签控件。不过,这些标签控件未发生修改,对手势支持一无所知。如果用户手势碰巧始于触摸标签控件而不是父窗口,该用户也可获得相同的结果,也就是窗体将相应地移动以响应输入移动。通过将未处理的手势消息从标签控件转发到父窗口,满足了用户的期望,内容移动的效果就像用户直接触摸窗体一样。手势消息
Windows Mobile 6.5 可识别五种手势,而应用程序可接收七种手势。额外的两种手势是在手势序列开始和结束时发送的 BEGIN 和 END(所有手势类型都带有前缀 GID_ 以指示手势标识符,因此这两种手势的标识符是 GID_BEGIN 和 GID_END)。例如,如果识别选择手势,则应用程序会收到三个手势消息:GID_BEGIN、GID_SELECT 和 GID_END。对于以滚动手势为结果的平移序列,应用程序会收到 GID_BEGIN、GID_PAN、GID_PAN…、GID_SCROLL 及最后的 GID_END。
GID_BEGIN 十分有用,因为它包含原始触摸点的屏幕坐标。GID_END 可方便地指示何时用户输入已结束并且不再为当前序列发送手势。
为了帮助您了解 Windows Mobile 6.5 中的基本手势识别和传送系统,我在附带的示例 SimpleGestureCapture 中包含了一个 Visual Studio 项目。此示例显示一个列表框,并为主窗口收到的每个手势消息添加一个新行,其中包括所有手势的位置信息及滚动手势的角度和速度。您将需要安装 Visual Studio 2005 或 Visual Studio 2008 以及 Windows Mobile 6 Professional SDK 和 Windows Mobile 6.5 Developer Tool Kit。从该示例中,您可以了解如何接收手势消息并提取数据。
物理引擎
在手势支持中,最令人兴奋的部分就是操控屏幕内容时用户体验到的自然响应。此响应的关键之处是设备间的一致、可预测和自然的体验。为了实现此一致性,在操作系统中新增了一种名为“物理引擎”的组件。此模块提供一套获取输入信息(包括滚动手势的角度和速度)的数字处理算法,并使用特定减速系数按时间来减慢速度。此外,当输入速度足以将动画点移到边框以外时,还可使用物理引擎应用边界动画。
若要在 Windows Mobile 6.5 中使用物理引擎,必须首选创建并初始化物理引擎的新实例。这样,系统会以固定的时间间隔定期对其进行轮询以检索当前动画位置,并且调用应用程序会相应地重绘其客户端区域。物理引擎会持续减慢动画速度,直至在低于最小阈值,那时会将动画标记为完成并可将其释放。
作为初始化数据的一部分,应用程序必须指定数据空间的边框以及显示空间的视图框(见图 5)。如果视图框移到边框以外,物理引擎会使用所选边界动画(也是初始化数据的一部分)将视图框重新纳入边框之内。物理引擎初始化十分灵活,可以只允许在一个轴上设置动画,必要时也可为每个轴设置不同的边界动画。
图 5 物理引擎如何处理边框和显示框
默认情况下,物理引擎根据从初始化时刻到每个位置检索调用时刻这一期间所获取的时间差值来减慢速度。调用应用程序可通过指定“user time”值来覆盖此设置,并让物理引擎计算该时间点的位置。这对于查找动画结束屏幕位置十分有用。
项大小是另一个引人注目的物理引擎配置。此信息用于向数据空间强加一个有效停止位置网格,从而强制物理引擎允许视图的最终位置落于其中某一网格坐标。当应用程序要在屏幕上显示项列表,而不希望在屏幕顶部显示不完整的项时,此行为十分有用。该行为适用于任意轴或同时适用于两个轴,并将调整动画减速和停止算法以延长或缩短动画持续时间,从而击中所需的停止点。
综合讲述
若要应用程序完全支持触摸手势,需要对其进行改进,使其识别相应的手势消息并做出相应的响应。必要时,需要创建和查询物理引擎实例以驱动屏幕重绘。此外,应用程序还需要考虑在动画或手势序列被进一步的用户输入或其他事件中断时应采用什么操作,并确保这种情况得到有效处理。虽然所有这一切的实现方式相对而言简单明确,但需要编写大量必须为响应手势的每个窗口而创建的 Boilerplate 代码。因此,Windows Mobile 6.5 中已采取一些步骤来简化此任务。
首先,一些内置控件已更新为支持手势,包括 LISTVIEWLISTBOX、TREEVIEW 和 WEBVIEW 控件(有些模式不支持手势)。如果您已在使用上述任意控件,则表示您的应用程序已支持手势。
对于没有使用这些内置控件的应用程序,有一个名为 Window Auto Gesture (WAG) 的新 API 可显著简化在最常见方案中实现手势支持所需的工作。
自动手势
WAG 逻辑与 DefWindowProc处理紧密结合,提供了可用于任何窗口的默认手势响应。启用后,WAG 会自动响应 GID_PAN 和 GID_SCROLL 手势,创建物理引擎实例并通过通知消息将相关定位数据发送回应用程序。此外,WAG 还可以通过在平移或滚动手势进行时监视输入队列来执行手势中断,从而提供与动画状态之间的适当转换。
WAG 的默认配置是忽略手势消息,因此要使用 WAG 行为的所有窗口都必须先启用手势支持。若要启用手势支持,应用程序必须为需要该支持的每个窗口调用 TKSetWindowAutoGesture,并传递所需的配置设置。如前所述,WAG 旨在简化最常见的手势支持方案。因此,若要使 WAG 驱动窗口,则创建该窗口时须在可由触摸手势操控的轴中设置 WS_VSCROLL 和/或 VS_HSCROLL 样式。此外,应用程序须正确管理滚动条,能够根据需要维护范围、最小化/最大化和页面大小。这样 WAG 才能够计算窗口所显示的数据区域大小。
WAG 有几个选项值得一提:
WAG 可处理 GID_PAN 和 GID_SCROLL 手势,但可根据需要禁用其中任意手势。与物理引擎一样,WAG 也支持设置项的宽度和高度。此信息不仅用于设置捕捉点,而且还会将滚动范围值从项计数扩展到像素计数。例如,假设一个列表包含 10 项,其滚动条范围为 0 到 9,每项在垂直方向需要占用 20 像素来显示其内容,则项的高度应设置为 20。WAG 会将该滚动范围 (10) 与像素高度 (20) 相乘,从而确定数据的完整像素范围(200 像素)。WAG 支持一种特殊模式,该模式通过向应用程序生成 WM_xSCROLL 消息来驱动窗口移动,而不是通过生成较常见的所有者动画消息。如果您使用旧式应用程序,并且希望在尽可能少更改代码的情况下获得触摸手势支持,则此模式十分有用。启用此模式的方法是:将 nOwnerAnimateMessage 值(TKSetWindowAutoGesture初始化数据的一部分)设置为 0 而不是常用的 WM_USER + x 值。在此模式下,有些功能是受限的,如不支持逐像素操控,只能逐项操控控件。此外,在此模式下不能超出滚动范围,因此将忽略扩充值。此选项不适合同时在两个轴上滚动,因为每个轴必须独立移动。扩充描述可将显示区域拖到数据范围之外的距离,表示为显示大小的百分比。启用扩充时须小心谨慎,因为这会允许用户将显示区域拖动到滚动限制之外,并公开许多现有应用程序都无法正常处理的屏幕区域。当空间显示在数据范围顶部或左侧以外时,请确保应用程序能够正确清除屏幕。通常,应用程序会将 WAG 的 nOwnerAnimateMessage 配置为一个 WM_USERWM_APP 范围的值。每次应用程序需要重绘其显示区域时,WAG 就会在发回到应用程序的消息中使用此值。在序列中的首个动画消息之前有一个状态消息,该消息指示控件现在正在响应手势输入。WAG 会自动聚合 GID_PAN 手势消息,并以每秒 24 次的最大频率(使用来自 Windows Mobile 6.5 Developer Tool Kit 的 gesturephysics.h 中的 GESTURE_ANIMATION_FRAME_DELAY_MS 计时器持续时间进行管理)只向应用程序发送动画消息。这同样适用于滚动动画,WAG 使用相同的计时器来查询其物理引擎,最大频率为每秒 24 次。
如果控件支持焦点或者在没有用户交互的情况下发生外观变化(例如通过异步更新),则 WAG 的状态消息选项尤为有用。状态消息通知控件用户正通过触摸界面进行交互。此类消息可用作触发器,用于暂停执行一些更新,这些更新可能更改控件或其内容的外观或者因屏幕动画而不必要地占用资源。生成全屏动画效果可能占用大量资源,因此应暂停所有不必要的后台处理,并集中资源为用户提供流畅及时的响应,这是非常重要的。完成触摸交互之后,可根据需要使用状态消息触发数据刷新和更新。
提示技巧
使用手势 API 接受和处理手势信息的思路十分简单。不过,要生成响应手势的流畅动画可能要花一些心思。下面的提示可能对您有所帮助。
首帧时间至关重要。人眼对用户界面延迟的敏感程度令人吃惊。例如,如果屏幕触摸与图形响应之间的延迟超过 100 毫秒,那么即使应用程序随后保持稳定的每秒 24 帧的速率 (fps),用户也会感觉到反映迟缓。应设法确保首帧响应速度足够快,最好低于 50 毫秒。值得一提的是,手势识别器和手势传送功能的开销已经过精心优化,从触摸到起效的时间仅为 1 毫秒或 2 毫秒。
首选一致的帧速率。在我们的测试中,用户宁愿选择稍慢但更一致的帧速率,也不是很快但变化较大的速率。利用此信息,我们创建了一个计时器来调控帧更新频率,并调整了计时器以确保在每个帧中有一些空闲的 CPU 时间可以处理其他任务。
消除动画期间的不必要开销。显而易见,每帧中的工作量越少,每秒绘制的帧数就越多。不过,有时很难确定具体可以留出哪些工作。在触摸操控期间,尤其是在滚动动画期间,用户不太注意细节,而是关注更醒目的内容。例如,在滚动电子邮件列表期间,用户可能不太注意每封邮件的预览,而更关注邮件在列表中的位置及其标题。因此,可以停止更新或检索预览文本,以腾出额外的时间保持顺畅的动画。
明智地使用屏外缓冲区。双缓冲可能是提高绘制性能以及减少屏幕零碎绘制的极佳方法。不过,使用该方法时必须谨慎,因为屏外缓冲区会消耗大量资源。应确保该缓冲区保留尽可能短的时间并保持最小大小。使用 ScrollWindowEx API 通常可以实现类似的结果,而不会产生屏外缓冲区的内存开销。
先度量,再应用适当的改进。标准性能分析做法可确保您修复已发生的问题。因此在更改任何代码之前,您务必先通过度量了解动画循环中的开销在哪里,然后在对应用程序最有益的方面开展工作。
参考资料
最新修订时间:2023-12-08 10:50
目录
概述
功能概述
参考资料