适用于 Windows Mobile 的自适应应用程序
Michael Saffitz
目录
自适应应用程序
设计和体系结构
显示区别
适用于本机应用程序的分辨率感知
适用于托管应用程序的分辨率感知
基本设备区别
其他设备区别
测试和进一步探索
当 Microsoft 首度发行 Pocket PC 2000 时以及在接下来的几年中,开发人员可以相对轻松地编写在所有设备上都运行良好的应用程序。通常,所有设备都具有相同的功能和特征:触摸支持、屏幕分辨率、纵向方向、API 集等等。因此,那时您可以完全将精力投放到自己应用程序的功能上,无需担心设备的这些其他元素,也绝不用担心它们可能会更改。
但更改是不可避免的。随着市场和技术的不断发展,涌现出越来越多的设备,这些设备开始采用不同的设计。不细说这当中的发展,让我们径直看下 2008 年的情况,现在已有 140 多种支持各种不同设计选择的 Windows Mobile
® 电话,这些选择包括 12 键键盘和 QWERTY 键盘、直板式和折叠式、是否支持 GPS、正方形、纵向和横向屏幕、Wi-Fi、蓝牙和红外通信以及依设备而不同的其他功能。与 Pocket PC 2000 不同,现在客户比较容易就能找到符合其需要、品位和预算的设备。
自适应应用程序
不过,由于 Windows Mobile 设备的不断多样化,开发人员必须要了解这些设备间的区别,并在应用时考虑哪种最适合。开发人员现在需要构建自适应应用程序,即在所有 Windows Mobile 设备上正常运行且外观精美的应用程序。
幸运的是,作为操作系统,Windows Mobile 可在所有设备上提供最一致的任何移动平台的核心 API 集之一。无论设备的移动运营商或硬件制造商是谁,Windows Mobile 开发人员看到的核心 API 集都是相同的(在多个位置均有设计,以与存在于桌面上的那些 API 集匹配)。
因此,构建自适应应用程序的多数开发人员会发现,处理不同的显示分辨率、每英寸点数 (DPI) 以及方向将成为需要重点关注的方面。对于少数开发人员,设备功能(如电话的存在、GPS 等等)的区别、Windows Mobile 主要版本之间的 API 更改以及设计面向触摸与非触摸设备也是需要考虑的方面。
我将向您介绍一些开发人员可用于构建自适应应用程序的工具和技术。我会介绍一些常规设计和体系结构原理,然后深入介绍有关适应屏幕和功能的区别的详情。
尽管选择构建自适应应用程序时一定会有一些必须加以注意的其他事项,但是预先投资的优势经常远胜于以下方式:针对每个当前(和未来)设备开发和测试应用程序版本,或选择不支持某种设备从而限制可能的用户群。
设计和体系结构
构建自适应应用程序时,遵循经过时间考验的实践和良好的体系结构模式是很重要的。从实践的角度来看,通过认真并明确地确定要求,您可以开始了解自己的应用程序所需的最小设备配置文件。您还可以识别应用程序可以动态使用的可选功能(如果存在)。采取此方法,您可以确保自己的应用程序可以在最多数的设备上运行,而不仅仅限于最常用的几种设备。
举一个简短的示例,假设有人要求我构建一个简单的包裹递送跟踪应用程序。我研究了我的要求,确定需要收集签名以表明包裹已成功传递。实际应用时应将包裹状态上传到服务器以用于联机跟踪,这可作为一项可有可无的功能。由于需要收集签名,我的应用程序将仅限于触摸设备(Windows Mobile Professional 和 Classic)。认识到这一点,我现在可以随意使用要求触摸的其他功能(如按钮控件)。
同时,我应注意架构连接组件。例如,如果假设应用程序有权访问蜂窝数据连接,则我无需清除不包含电话但可以包含 Wi-Fi 的 Classic 设备。
最后,您应该注意到,如果我的客户改变主意并删除签名需求,考虑到我随后将使用按钮控件,所以我不必支持非触摸设备 (Window Mobile Standard)。
显示区别
如前所述,屏幕分辨率、DPI 和方向上的差别通常是构建自适应应用程序时最棘手的问题。针对这些差别做打算并仔细考虑体系结构在实现解决方案时也将产生重要影响。
当前支持的 Windows Mobile 6 分辨率、DPI、方向和图标维度显示在
图 1 中。除此之外,Microsoft 偶尔会添加新配置、原始设备制造商 (OEM) 和移动运营商。有些设备也支持动态屏幕方向旋转。例如,纵向显示为 240×320 的设备可以在键盘滑出时旋转为 320×240 横向显示。
图 1 Windows Mobile 6 显示选项
Windows Mobile Professional 和 Classic(有触摸屏) |
分辨率 |
DPI |
方向 |
小图标 |
大图标 |
240×240 |
96 |
正方形 |
16×16 |
32×32 |
240×320 |
96 |
纵向和横向 |
16×16 |
32×32 |
240×400 |
96 |
纵向和横向 |
16×16 |
32×32 |
320×320 |
128 |
正方形 |
21×21 |
43×43 |
480×480 |
192 |
正方形 |
32×32 |
64×64 |
240×240 |
192 |
纵向和横向 |
32×32 |
64×64 |
240×240 |
192 |
纵向和横向 |
32×32 |
64×64 |
Windows Mobile Standard(没有触摸屏) |
分辨率 |
DPI |
方向 |
小图标 |
大图标 |
176×220 |
96 |
纵向 |
16×16 |
32×32 |
240×320 |
131 |
纵向和横向 |
22×22 |
44×44 |
320×320 |
131 |
正方形 |
22×22 |
44×44 |
240×400 |
131 |
纵向和横向 |
22×22 |
44×44 |
440×240 |
131 |
横向 |
22×22 |
44×44 |
相比从前,以下要求更显重要:应用程序不采用一个或多个屏幕特征,而是被编写为能动态适应并支持所有分辨率、方向以及至少 96 DPI 和 131 DPI。(192 DPI 也很重要,但往往限于极少数设备。128 DPI 与 131 DPI 非常接近,不会产生重大问题。)
有多种同时适用于本机和托管应用程序开发的资源和工具,可使此任务更加简单。我将介绍其中一些资源和工具,并分析一些可在构建分辨率感知应用程序过程中使用的常规方法和最佳实践。
适用于本机应用程序的分辨率感知
Windows Mobile 6 SDK 为使用本机代码编写分辨率感知应用程序提供了两种主要资源:UILayout 示例中的可重用 ScreenLib 类和 DeviceResolutionAware.h 标头。UILayout 示例可在 Windows Mobile SDK 的 /Samples/Common/CPP/Win32 目录中找到。DeviceResolutionAware.h 安装在 Microsoft
® Visual Studio
® 安装目录的 /VC/ce/atlmfc/include 目录下。
ScreenLib 提供了一组帮助函数,用于对齐屏幕上的元素。例如,您可以使用 DockControl 函数将给定控件停靠到屏幕某个边缘或所有四个边缘来填充客户端区域。OptimizeWidth 和 OptimizeHeight 函数将某个控件(或多个控件 — 对于 OptimizeWidth 函数)与显示器对齐并调整其大小,左右或上下分别留出一小块边距。提供的其他函数可用于对齐控件和将一组控件调整为相同大小。在这些函数中,当大量使用基于窗体的应用程序时,ScreenLib 可能最有用。
DeviceResolutionAware.h 弥补了 ScreenLib 的不足,并提供了有助于构建更复杂的自适应应用程序的 20 多个函数和宏,首先引出的是可为 GetDisplayMode(可提供显示特征和功能)之类的自适应用户界面提供构造块的基本函数和宏。
您会看到 SCALEX、SCALEY、SCALERECT 和 SCALEPT,它们为当前分辨率适当缩放值。接下来,是可帮助针对当前显示特征缩放图像的函数(如 StretchIcon 和 ImageList_StretchBitmap),以及可用于在方向发生更改时自动调整对话框布局的函数,如 RelayoutDialog。
ScreenLib 和 DeviceResolutionAware.h 为构建本机分辨率感知应用程序奠定了坚实的基础。但同时,一定要注意,它们不是您的环境可能独有的设计和解决方案的替代品,尤其是直接与显示缓冲区进行交互或构建复杂 UI 时。
适用于托管应用程序的分辨率感知
对于托管代码应用程序,Microsoft .NET Compact Framework 提供了可帮助创建分辨率感知应用程序的一组显示属性。与 ScreenLib 中的功能类似,.NET Compact Framework 提供了将指定控件绑定到其父项边缘的 Control.Dock 属性。Control.Anchor 通过将控件绑定到距其父项边缘固定距离的位置提供了类似的功能。如果显示器无法容纳窗体中的控件,则使用 Control.AutoScroll 属性将自动添加滚动条。此外,Control.AutoScale 跟踪窗体的设计分辨率,并在 DPI 或分辨率更改时动态缩放窗体。正如您所料,对于较简单的窗体,Control.AutoScale 属性很好用,但是,随着窗体变得越来越复杂,它能发挥的作用会变得越来越有限。
对于较复杂的窗体,这些属性提供了一个起点,但它们不一定是完整的解决方案,尤其是在开始动态调整方向更改时。一种方法是使用 Orientation Aware Control (OAC),Microsoft 模式和实施方案小组已将其作为移动客户端软件工厂 (
msdn2.microsoft.com/library/aa480471) 的一部分发布。OAC 提供了一种面向多个方向的简单方法,尤其适用于基于窗体的应用程序。
安装 OAC 后,您可以使用 Visual Studio 中的托管窗体设计器先以纵向模式布置 UI,然后使用 OAC 旋转为横向模式。采用此新方向后,您可以根据需要横向调整 UI。OAC 将跟踪更改,以便当最终用户处于纵向或横向模式下在运行时使用适当的布局。
但是,使用 OAC 也存在一些弊端。首先,从本质上而言,OAC 允许您设计特定的方向和分辨率组合;因为支持的方向和分辨率将来可能发生更改,所以不鼓励使用此方法。其次,它不允许您明确设计不同的 DPI 或方形屏幕,方形屏幕最近在基于 Windows Mobile Standard 的 smartphone 设备上很流行。最后,由于 OAC 会为每个屏幕创建和管理多个布局,在您添加更多的窗体和增加复杂性时,性能下降会相当显著(在极端的情况下,这会显著增加您的应用程序启动时间)。
由于存在这些弊端,Windows Mobile 团队一直鼓励开发人员避免使用 OAC。该小组即将发布 Windows Mobile Line of Business Accelerator (
go.microsoft.com/fwlink/?LinkId=115317) 的新版本,该版本包括一个可帮助构建分辨率感知应用程序的组件,其中的分辨率感知应用程序包含单个 UI 并使用停靠、定位和其他技术。
基本设备区别
尽管有各种工具和新的加速器,但对于编写自适应应用程序,哪一个也不是万能的。相对于您的需求和环境而言,每种方法都有其优点和缺点。但无论您采用哪种方法,这里都会为您提供了一些或许有用的常规指导。
从三个基本方向(方形、纵向和横向)开始,考虑您如何为每个方向布置 UI。完成之后,您可以侧重于确保 UI 在各种分辨率和 DPI 之间适当缩放。对于复杂的 UI,发生方向更改时,调整设计并使其在多个窗体中分布比将其保留在一个窗体中并进行重新安排可能更容易。
针对视觉适应实现应用程序时,使 UI 和应用程序的逻辑清楚分开将变得更重要。通过将 UI 视为交互点并避开基于窗体的思路,您可以封装管理类中的 UI 元素,这些类集中并提取如何将这些元素映射到各个窗体的详细信息。
专门针对方形屏幕设计可能比较诱人 — 将控件停靠到左上角,通过有效减少纵向和横向方向长度使形状呈方形提供适应性。但是,如果这样做,您可能会使应用程序和用户浪费三分之一到三分之二的潜在屏幕空间。此外,起初的看似可节省大量时间的开发工作最后可能会花费更多时间 — 甚至还会使能利用得上的屏幕空间更少,您被迫更严格地设计 UI 或添加更多屏幕。
其他设备区别
尽管处理显示特征方面的区别通常是编写自适应应用程序的最重要部分,但还有其他方面需要注意。因为 API 这些年来历经引入、修改和弃用阶段,所以 Windows Mobile 版本很重要。您可能希望在可用时使用某些特定于设备的功能。
对于这些情况及其他情况,仍然可以以允许它们在所有设备上运行的方式编写应用程序。您可以考虑使用一项技术 — 工厂模式,尤其是与定义基本功能和包含特定于设备的实现的接口结合使用。
让我们看一下包括前面讨论的某些设计和收集实践要求的简单示例。有人要求我创建一个包含餐馆评论的城市指南。核心要求是要包含餐馆目录和评论,并能够选择和查看特定餐馆并阅读其评论。我的应用程序必须在 Windows Mobile 2003 和更高版本上运行。最后一项要求是使用可提供电话服务的设备时,可以从评论屏幕直接打电话到餐馆。
对于此示例,我将使用 C#,但是所呈现出的理念同样适用于 Visual Basic
® 和 C++。Windows Mobile 5.0 首先引入了托管电话服务 API,因此,我的应用程序需要了解它是在哪个 Windows Mobile 版本上运行并使用相应的实现。
不考虑设备和任何特定的实现,我首先创建定义所需的常用功能的电话服务界面:
interface ITelephony {
bool HasPhone {
get;
}
void MakeCall(string phoneNumber);
}
接下来,
图 2 显示了我的两种实现,一种针对低于 Windows Mobile 5.0 的设备,另一种针对运行 Windows Mobile 5.0 或更高版本的设备。为简单起见,我已省略低于 Windows Mobile 5.0 的设备上所需的 P/Invoke。
图 2 实现 ITelephony
class PreWindowsMobile5 : ITelephony {
public bool supportsTelephony {
get { return File.Exists(@"/Windows/Phone.dll"); }
}
public void MakeCall(string phoneNumber) {
// call Native Phone API
}
}
class PostWindowsMobile5 : ITelephony {
public bool supportsTelephony {
get { return SystemState.PhoneRadioPresent; }
}
public void MakeCall(string phoneNumber) {
Phone thePhone = new Phone();
thePhone.Talk(phoneNumber);
}
}
最后,我将创建 TelephonyFactory 类,用于集中不同的实现并将这些实现从应用程序核心逻辑隔离出来(请参见
图 3)。在此示例中,我选择使 TelephonyFactory 类包含识别设备是否支持电话的功能,如果不支持,则引发异常。现在,当我希望在应用程序内使用电话服务时,我可以直接运行以下内容:
try {
ITelephony myTelephony =
TelephonyFactory.GetTelephony();
myTelephony.MakeCall(phoneNumber);
}
...
图 3 TelephoneFactory
class TelephonyFactory {
static ITelephony _myTelephony = null;
public static ITelephony GetTelephony() {
if (_myTelephony == null) {
if (Environment.OSVersion.Version.Major >= 5)
myTelephony = new PostWindowsMobile5();
else
myTelephony = new PreWindowsMobile5();
}
if ( ! myTelephony.supportsTelephony)
throw(new NotImplementedException());
return _myTelephony;
}
}
通过使用此模式,我隔离了在不同设备间出现的差异,并且使应用程序更加易于更新和提供服务,同时还提供了适用性并确保应用程序可以在所有 Windows Mobile 设备上运行。
测试和进一步探究
编写自适应应用程序需要考虑三个方面,良好的设计和严谨的编码是其中的两个方面。幸运的是,由于仿真器映像的多样性,使得第三个方面 — 测试更加简单,该仿真器映像是 Windows Mobile SDK 的一部分,位于一个独立的软件包中。通过仿真器映像,可以在几乎不影响任何可能的显示特性(包括动态旋转)情况下测试应用程序。
借助 SDK 中的附加工具,可以模拟 GPS 功能、多种类型的蜂窝网络,甚至不同的安全性配置,从而可以测试动态功能检测和使用。最后,可以通过设备仿真器自动化 API 和核心连接框架对大部分仿真器环境进行编程,这样就可以自动测试多个设备配置的方方面面。
当今,Windows Mobile 提供最佳移动开发平台的一种方法即,提供一个平台,在此平台上,单个应用程序可以面向多种多样的设备。不管您是经验丰富的平台高手,还是初识平台者,Windows Mobile 开发人员网站 (
msdn.microsoft.com/windowsmobile) 都会为您提供帮助,其中提供了与丰富的 API 和高级技术结合使用的文档、教程和工具,使开发 Windows Mobile 的前景十分广阔。有关在此专栏中讨论的适应性问题的其他材料和最新信息,请参阅
msdn.microsoft.com/windowsmobile/adaptyourapp。
特别感谢 Jim Wilson (
pluralsight.com/blogs/jimw),他的“调整您的应用程序”演讲不仅为此专栏带来了灵感,同时还提供了相关材料。