在这个讨论创建 Flex 移动 skin 的系列的 第 1 部分 中,我讨论了 Flex 团队在 Mobile 主题中所做的性能优化的原理,提供了一个以性能为主要关注点的简单 Button skin 示例。移动 skin 领域的下一个主要主题是,针对当今可用的各种移动屏幕调整应用程序的外观和感觉。
Flex 4.5 增加了一些新特性以支持像素密度不同的移动设备;像素密度也称为每英寸像素数 (PPI) 或每英寸点数 (DPI)。这些特性包括应用程序缩放、多 DPI 位图、基于 CSS @media 查询的样式规则和与 DPI 相关的 Mobile 主题。
为什么要关注屏幕像素密度?
如果您原来从事桌面 Flex 开发,可能想知道为什么应该关注屏幕像素密度。
当今的移动设备具有不同的大小和屏幕 DPI(见表 1)。在桌面环境中,屏幕大小也不一致,但是 DPI 值大体上是相似的。因此,通常根据像素尺寸设计(本机或基于浏览器的)桌面应用程序。
表 1. 移动设备分辨率、大小和 DPI 值的示例
生产商 |
设备 |
分辨率 (px) |
屏幕对角线长度 (in) |
DPI |
Apple |
iPhone 4, iPod 4 |
960 x 640 |
3.5 |
326 |
Apple |
iPad 1, iPad 2 |
1024 x 768 |
9.7 |
132 |
BlackBerry |
PlayBook |
1024 x 600 |
7 |
170 |
HTC |
Evo |
800 x 480 |
4.3 |
217 |
Motorola |
Atrix |
960 x 540 |
4 |
275 |
Motorola |
Xoom |
1280 x 800 |
10.1 |
150 |
Samsung |
Galaxy Tab |
1024 x 600 |
7 |
170 |
关键是一些设备在相同的物理空间中容纳的像素多得多。如果设计一个大小为 80 x 80 像素的按钮,这个按钮在 160 DPI 的设备(比如 BlackBerry PlayBook 或 Samsung Galaxy Tab)上显示为边长大约 0.5 英寸的正方形。如果在 DPI 值更高的设备(比如 iPhone 4)上运行相同的应用程序,这个按钮的边长现在是 0.25 英寸,很难通过触摸屏操作它。
同样的原理也适用于字体大小选择、基于像素的布局、位图图像等方面。屏幕上任何元素的位置和大小都需要根据屏幕的 DPI 来设置。如果不解决屏幕像素密度问题,应用程序在许多移动设备上会太大或太小。
Flex 4.5 引入了几个新特性以解决屏幕像素密度问题。开发人员可以使用方便的自动应用程序缩放,也可以使用与密度相关的图像和逻辑人工地管理设计保真度和控制能力。
自动应用程序缩放
Flex 4.5 支持 DPI 分类。设备的像素密度范围很宽,为了进行布局,有必要对它们分类;例如,238 DPI 或 249 DPI 的屏幕基本上与 240 DPI 的设备相当。Flex 支持三个 DPI 类别:160、240 和 320。
Application 类及其子类中增加了新的 applicationDPI 属性。当把这个属性设置为以上 DPI 类别之一时,应该针对这个 DPI 设计布局。如果应用程序在 DPI 类别不同的设备上运行,整个应用程序会自动地缩放,让 UI 在此设备上以大体相同的物理大小出现。
本文的示例文件包含 TwitterTrendsFinal.fxp。在这个示例项目中,applicationDPI 设置为 160,针对 160 DPI 屏幕适当地确定了各个布局值的大小(比如 TweetsView 列表中的 iconWidth 和 iconHeight 以 及 UserInfoView 中的 padding/gaps)。在 160 DPI 设备上(比如 Motorola Droid Pro),应用程序采用指定的大小。在 240 DPI 设备上(比如 Droid X 或 Droid 2),应用程序统一地放大 50%。图 1 按绝对像素数对比 160 DPI 屏幕与 240 DPI 屏幕的大小。图 2 显示相同的屏幕截图,但是 240 DPI 图像已经变小了,显示相近的物理大小。但是,在 MXML 和 ActionScript 代码中,所有的值还是原来针对 160 DPI 的值;在 stage 级上应用缩放。
注意,由于矢量绘制会产生痕迹,一般来说最好是放大而不是缩小。因此,最好针对低的密度(比如 160 DPI)进行设计,让应用程序在高密度设备上放大。
与密度相关的位图
在使用 applicationDPI
根据不同的密度自动地缩放应用程序时,矢量缩放并不包括文本;而是缩放字体大小,从而以不同的大小显示保真度很高的文本。但是,位图会被缩放。这并不好, 因为放大位图通常会产生不可接受的痕迹。为了缓解这个问题,Flex 4.5 引入了新的 MultiDPIBitmapSource 类。这个类有三个属性(source160dpi、source240dpi 和 source320dpi
)。它根据实际的 DPI 为每个设备使用最合适的位图。例如,假设一个按钮图标在 160 DPI 设备上应该是 24 x 24。并不把一个图像直接指定为 Button 组件的 icon 属性,而是把 icon 属性设置为一个 MultiDPIBitmapSource
实例,这个实例还引用在 240 DPI 上使用的 36 x 36 图标和在 320 DPI 上使用的 48 x 48 图标。代码如下:
<s:Button>
<s:icon>
<s:MultiDPIBitmapSource
source160dpi="@Embed('/assets/refresh160.png')"
source240dpi="@Embed('/assets/refresh240.png')"
source320dpi="@Embed('/assets/refresh320.png')"/>
</s:icon>
</s:Button>
继续以上场景,假设已经把 applicationDPI
设置为 160,应用程序在 320 DPI 设备上运行。尽管矢量会放大 100%,但是如果指定了多 DPI 位图,Flex 不会把 160 DPI 位图放大 100%;相反,会使用 320 DPI 位图并对此位图进行适当的转换,让它以适当的大小出现,它的所有像素都不缩放。
在能够使用嵌入的或动态装载的图像引用的几乎任何地方,都可以指定 MultiDPIBitmapSource 对象——例如,作为 Image 的 source
属性或作为 Button 的 icon
属性。一个例外是,不能使用 MultiDPIBitmapSource 对象作为应用程序的快闪屏;在这种情况下,需要指定高分辨率位图,让 Flex 对于低密度设备缩小它。
应用程序缩放总结和考虑因素
如果希望使用应用程序缩放,就需要:
- 针对您指定的
applicationDPI
值,创建一套 skin 和视图/组件布局。Adobe 建议使用 160 值,通过乘以 1.5 和 2 分别放大到 240 DPI 和 320 DPI。 - 对于在 skin 中或应用程序中其他地方使用的任何位图资产,创建多个版本并使用 MultiDPIBitmapSource 指定它们。如果使用应用程序缩放,skin 中的矢量资产和文本不需要与密度相关联。
- 忽略
@media
规则,因为应用程序只需要考虑一种目标 DPI。 - 在不同密度的设备上测试应用程序,确认缩放后应用程序的外观在每个设备上都是可接受的。尤其是要检查需要非整数的缩放因子的设备。例如,如果
applicationDPI
是 160,应该在 240 DPI 设备上测试应用程序。
在使用缩放时可能出现的缺陷是,可能有可以看出来的痕迹。当缩放因子不是整数时(例如 160 乘以 1.5 到 240),在某些情况下矢量资产上会出现可以看出来的痕迹。尤其是,根据线条宽度、线条对齐和 Flash Player 执行的像素 snapping,线条可能出现模糊或略微偏移。
文本和位图一般没问题。文本应该不会显示出任何痕迹,因为文本是通过缩放字体大小值缩放的,而不是通过缩放实际的矢量。如果使用 MultiDPIBitmapSource 为每个 DPI 指定适当的位图,位图应该不会显示出缩放痕迹。
在实践中,Flex 团队对比了使用放大到 240 DPI 的 160 DPI skin 与使用未缩放的 240 DPI skin 的效果,只发现了非常小的可见差异。对于大多数应用程序,这种方法的效果应该很好,不需要改进了。但是,如果发现缩放产生了不可接受的痕迹,或者由于其他 原因需要更细致地控制应用程序在不同密度的设备上的行为,那么可以按下一节中的说明人工地管理密度。
与密度相关的 skin 和样式
如果没有显式地设置 applicationDPI
,它的值会设置为设备的 DPI 类别。Flex 4.5 附带的 Mobile 主题根据 applicationDPI
值选择适当的图像类,以及确定组件 skin 的位置和大小。对于不由 skin 控制的元素,比如布局元素,应用程序应该根据不同的密度自动地管理布局值。尤其是,应该在运行时根据applicationDPI
动态地计算代码中的所有像素值(通过命令式代码或数据绑定),还应该对位图使用 MultiDPIBitmapSource。
通过 CSS @media 查询实现与密度相关的样式
Flex 4.5 引入了一套有限的 CSS 媒体查询功能,可以使用它们声明只应用于特定密度的 CSS 规则。例如,Mobile 主题使用与 DPI 相关的规则指定 ActionBar 标题的默认字体大小:
ActionBar #titleDisplay { fontSize: 24pt; }
@media (application-dpi: 160)
{
ActionBar #titleDisplay { fontSize: 18pt; }
}
@media (application-dpi: 320)
{
ActionBar #titleDisplay { fontSize: 36pt; }
}
第一个规则适用于 240 DPI;对于 160 DPI 和 320 DPI,另两个规则覆盖第一个规则。
用来匹配这些 @media 规则的 DPI 类别是应用程序当前的有效 DPI。如果在 Application
标记中设置了applicationDPI
,就使用它的值;否则,使用与设备的 runtimeDPI 对应的 DPI 类别。例如,如果applicationDPI
是 160,而应用程序在 320 DPI 设备上运行,就会选择 application-dpi
:160 规则,这导致 Flex 把文本大小设置为 18pt。然后,文本大小自动地乘以 2,所以最终显示的文本大小实际上是 36pt。在这里,实际上根本不会用到 320 DPI @media 规则。但是,如果没有指定 applicationDPI
,应用程序在 320 DPI 设备上运行,就会使用 320 DPI @media
规则。
与密度相关的 skin 和样式考虑因素
如果通过不设置 Application applicationDPI
选择不使用自动缩放,应用程序就需要在运行时在 skin 和布局中读取 applicationDPI
值,从而判断设备的实际 DPI 类别。然后,需要使用以下技术实现与密度相关的 skin 和样式:
针对每个运行时 DPI 类别,分别设计一套 skin 和视图/组件布局,或者设计一套根据不同密度动态调整的 skin 和布局。Mobile 主题 skin 采用后一种方法——每个 skin 类检查 applicationDPI
并适当地配置本身。
使用 @media 根据设备的 DPI 类别选择 CSS 规则。通常,为每个 DPI 定制字体大小和 padding 值。
对于在 skin 中或应用程序中其他地方使用的任何位图资产,创建多个版本并使用 MultiDPIBitmapSource 指定它们。如果使用应用程序缩放,skin 中的矢量资产和文本不需要与密度相关联。
在不同密度的设备上测试应用程序,确认 skin 和布局会适当地调整。
Flash Builder 4.5 设备配置
Flash Builder 4.5 在 Design View 中和在启动 Adobe Debug Launcher (ADL) 实用程序时支持与密度相关的 Flex 4.5 特性。尽管在 Design View 中预览或用 ADL 模拟既快又简便,但是在设备上启动应用程序会最精确地反映应用程序的外观。
在 Flash Builder 中创建新的移动项目
当在 Flash Builder 中创建 Flex Mobile 项目时,可以在项目向导中启用自动缩放。创建项目之后,随时可以通过编辑应用程序文件添加(或删除) applicationDPI
。
在 Flash Builder 中编写应用程序
Flash Builder 4.5 还包含一个新的 Device Configurations 首选项页面,可以使用它为任意设备指定像素密度和分辨率。内置的配置已经为相应的设备指定了适当的像素密度和分辨率。这意味着,在 Design View 中选择要预览的设备或者使用 ADL 在桌面上启动应用程序时,会适当地模拟在此设备上运行应用程序的情况。
例如,如果 applicationDPI
是 160 并在 Design View 中选择 Google Nexus S 作为预览设备,那么 Design View 会把应用程序放大 50%,按 Nexus S 的尺寸显示它。这意味着,Flex 代码中的 1 像素现在对应于 Design View 中的 1.5 像素(见图 3)。
自动缩放并不影响在 Properties View 中做的编辑,因为这些编辑实际上修改底层代码。例如,在上面的场景中,如果在 Properties View 中把一个组件的宽度设置为 100,它在 Design View 中显示的宽度实际上是 150 像素,这符合在 240 DPI 设备上的情况。
同样,如果使用模拟 Nexus S 的 ADL 在桌面上运行应用程序,也会把应用程序放大 50%,按它在 Nexus S 屏幕上的正确尺寸显示它。Design View 和 ADL 还会正确地显示 @media 查询和 MultiDPIBitmapSource 的效果。
可以使用 Properties View 在支持的任何地方设置多分辨率位图。为此,选择一个组件(比如 Button)。在 Properties View 中 icon 设置的旁边,浏览文件夹图标现在是一个下拉列表,可以通过它选择使用单一位图还是多分辨率位图。
选择后者就会打开一个对话框,可以使用它为每个 DPI 类别指定位图(见图 4)。
测试
如果您有像素密度不同的设备,可以自己试试这些密度独立特性。例如,如果您有 Motorola Droid Pro 或 Samsung Galaxy Tab (DPIClassification 160) 和更先进的 Android 手机 (DPIClassification 240),可以编写一个应用程序并把 applicationDPI 设置为 160,在两个手机上运行它,对比运行效果,应该会看到 UI 组件的大小在两个设备上很接近。(当然,Galaxy Tab 的屏幕更大,所以在这种情况下整个 UI 的大小比在手机上大。)
如果没有像素密度不同的手机,通过使用 Flash Builder 在桌面上运行应用程序,仍然可以试验这些特性。例如,Flash Builder 4.5 现在有针对 Droid Pro 的内置设备配置。只需在 Run Configurations(见图 5)或 Debug Configurations 对话框中选择这个设备配置。ADL 会把设备配置中指定的屏幕像素密度传递给 Flex 应用程序,所以会模拟在此设备上应该出现的缩放行为。
与密度相关的 Button skin 教程
因为自动缩放需要做的工作很少,本文的教程主要关注编写与密度相关的 skin 所需的步骤。目标是在每个 DPI 上显示保真度很高的图像。因此,我们将使用一个按钮图像的三个版本。在本系列的第 1 部分中创建了一个圆角的药片形状的按钮,但是本教程只使用老式的按钮图像。为了简单,这里省略 Adobe Illustrator 细节。
步骤 1:创建 FXG 图像
对于本教程,可以使用 DensitySpecificButtonTutorial.fxp 示例项目中现成的图像(见图 6)。可以在 skins.assets160 包中找到这些 FXG 文件。
我在第 1 部分中讨论过使用 Illustrator CS5 创建 FXG 文件的基本知识。为与密度相关的 skin 创建图像还需要做一些工作,包括:
- 针对 160、240 和 320 DPI 值,缩放原来的图像。先创建 160 DPI 图像,然后在 Illustrator 中使用缩放因子转换它。
- 为每个 DPI 更新刻度网格信息。
您可能想知道,既然可以在运行时通过程序缩放图像,为什么要在 Illustrator 中缩放。有两个原因:
- 设计保真度——设计者可能会发现简单地缩放 160 DPI 图像是不可接受的。对于更高的 DPI,可能需要改进细的线条、角半径参数、像素 snapping 或形状详细信息。
- 复杂性——同时应用缩放(对于 DPI)和大小调整转换(使用刻度网格数据缩小/放大)会增加复杂性。可以使用更简单、更可预测的方式实现用刻度网格调整大小的与 DPI 相关的图像。
在 Illustrator 中缩放图像非常简单(见图 7):
- 在工具栏中找到 Transform Panel。
- 按缩放因子调整宽度和高度值。如果以 160 DPI 图像为基础,对于 240 和 320 DPI 分别乘以 1.5 和 2。
- 检查图像并做必要的修改。
在 Mobile 主题库中,图像资产根据 DPI 值组织在包中。在 mobiletheme.swc 中的 spark.skins.assets160、spark.skins.assets240 和 spark.skins.assets320 包中可以找到图像资产。在实践中,为了通过设计检查,在缩放后需要做一些小的设计调整,尤其是对细线条的宽度。在缩放后可能不需要任何修改,但是最好检查 一下。缩放看似很简单,但是也有要处理的问题。在缩放示例项目中的 160 DPI 图像时,对于 240 和 320 DPI 值,Illustrator 会分别把外线条的 x 和 y 位置从半像素对齐 (0.5,0.5) 移动到 (0.75,0.75) 和 (1,1)。如果希望细线条对于所有三个 DPI 值都保持像素 snapping,必须把 x 和 y 位置调整回 (0.5,0.5) 并调整宽度和高度以便补偿。
在开始处理 ActionScript skin 代码之前,还需要重复第 1 部分中解释过的两个步骤:
- (可选)清理 FXG 以提高可读性
- 删除组并更新刻度网格值
步骤 2:添加 applicationDPI 逻辑
有了与 DPI 相关的 FXG 图像和包结构之后,可以根据 applicationDPI 的值调整 skin 中的图像。MobileSkin 基类作为属性公开 applicationDPI。在构造函数中初始化图像类,如下所示
public class TransparentRoundedButtonSkin extends ButtonSkin
{
public function TransparentRoundedButtonSkin()
{
super();
switch (applicationDPI)
{
case DPIClassification.DPI_320:
{
upBorderSkin = skins.assets320.TransparentRoundedButton_up;
downBorderSkin = skins.assets320.TransparentRoundedButton_down;
break;
}
case DPIClassification.DPI_240:
{
upBorderSkin = skins.assets240.TransparentRoundedButton_up;
downBorderSkin = skins.assets240.TransparentRoundedButton_down;
break;
}
default:
{
// default DPI_160
upBorderSkin = skins.assets160.TransparentRoundedButton_up;
downBorderSkin = skins.assets160.TransparentRoundedButton_down;
break;
}
}
}
}
步骤 3:为每个 DPI 类别添加 CSS @media 规则
到目前为止,过程与第 1 部分中的 skin 实现过程差异不大。这是好消息!实现 Button skin 的最后一步是根据 DPI 类别选择适当的字体大小。mobiletheme.swc defaults.css 文件已经指定了一个全局样式,它设置默认的全局字体大小,如下所示:
- 160 DPI – 16
- 240 DPI – 24
- 320 DPI – 32
要想覆盖默认的字体大小,应该在 CSS 文件或 fx:Style 块中创建新的样式规则,例如:
@media (application-dpi: 160)
{
Button
{
fontSize: 20;
}
}
@media (application-dpi: 240)
{
Button
{
fontSize: 30;
}
}
@media (application-dpi: 320)
{
Button
{
fontSize: 40;
}
}
与密度相关的快闪屏教程
Flex 4.5 框架引入了在移动应用程序启动时显示快闪屏的功能。这个特性让开发人员可以定制应用程序的启动体验,比如在 Flex 应用程序初始化时显示徽标等品牌信息。
显示快闪屏很简单而且适用于所有 Flex Application 类(Application、ViewNavigatorApplication 和 TabbedViewNavigatorApplication)。要想启用这个特性,只需在主 Application 文件中把splashScreenImage
属性设置为一个嵌入的图像。
<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
splashScreenImage="@Embed('assets/SplashScreen.png’)"
splashScreenMinimumDisplayTime=”2000”
splashScreenScaleMode=”zoom”>
</s:Application>
上面的示例让应用程序在启动时显示 SplashScreen.png 图像。应用程序完全装载之后,图像消失,用户可以开始使用应用程序。应用程序可以使用 splashScreenMinimumDisplayTime
属性让快闪屏至少持续显示指定的时间。这样可以确保即使是在很快的设备上,图像也会显示一定的时间。
正如本文中指出的,移动开发的主要难题之一是,开发出的应用程序要能够适应不同的设备。因为每种设备的特征不一样,应用程序可能需要调整用户界面, 才能产生理想的体验。设备的主要差异之一是屏幕分辨率。因为每种设备支持不同的屏幕分辨率,很难创建在所有设备上都恰当地显示的单一图像。为了帮助解决这 个问题,快闪屏支持根据设备的分辨率调整本身大小的功能。开发人员可以使用splashScreenScaleMode
属性选择快闪屏的缩放行为。有效值是:
- none(默认):快闪屏不缩放,但是会居中。
- letterbox:快闪屏在保持高宽比的情况下缩放到设备的可用屏幕大小。
- stretch:快闪屏缩放到填满屏幕。不保持高宽比。
- zoom:快闪屏在保持高宽比的情况下缩放到完全填满可用的屏幕大小。因此,图像的一部分可能会超出屏幕的边界。
定制快闪屏以支持 DPI
缩放快闪屏有助于在某些设备上产生很好的体验,但是如果屏幕之间像素密度不一样,仅仅改变图像大小不一定能够产生最好的效果。在某些设备上,缩放会 导致图像太大、太小或扭曲。另外,缩放有时候会产生可见的痕迹和 pixilation。如果需要进一步控制,应用程序可能希望根据设备的像素密度显示不同的快闪图像。这会提供更精细的体验,产生更好的效果。通过编写定 制的快闪屏预装载器,可以轻松地扩展框架以支持这个需求。预装载器可以决定在应用程序启动时要显示哪个图像。下面是实现过程:
步骤 1:创建一个扩展 spark.preloaders.SplashScreen 组件的定制对象并嵌入图像资产
package
{
import spark.preloaders.SplashScreen;
public class MultiDPISplashScreen extends SplashScreen
{
[Embed(source="assets/splash160.png")]
private var SplashImage160:Class;
[Embed(source="assets/splash240.png")]
private var SplashImage240:Class;
[Embed(source="assets/splash320.png")]
private var SplashImage320:Class;
public function MultiDPISplashScreen()
{
super();
}
}
}
步骤 2:覆盖 getImageClass 方法并根据设备的 DPI 返回适当的嵌入资产
package
{
import spark.preloaders.SplashScreen;
import mx.core.DPIClassification;
import mx.core.mx_internal;
use namespace mx_internal;
public class MultiDPISplashScreen extends SplashScreen
{
[Embed(source="assets/splash160.png")]
private var SplashImage160:Class;
[Embed(source="assets/splash240.png")]
private var SplashImage240:Class;
[Embed(source="assets/splash320.png")]
private var SplashImage320:Class;
public function MultiDPISplashScreen()
{
super();
}
override mx_internal function getImageClass(dpi:Number, aspectRatio:String):Class
{
if (dpi == DPIClassification.DPI_160)
return SplashImage160;
else if (dpi == DPIClassification.DPI_240)
return SplashImage240;
else if (dpi == DPIClassification.DPI_320)
return SplashImage320;
return null;
}
}
}
步骤 3:指定自己的定制快闪屏预装载器作为应用程序的预装载器
<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
preloader=”MultiDPISplashScreen”>
</s:Application>
当定制的预装载器运行时,它根据设备的 DPI 显示不同的嵌入资产。注意,在 getImageClass 方法中还可以实现各种其他逻辑。例如,可以扩展上面的示例,使用 aspectRatio 参数和 stage 尺寸根据设备的方向和应用程序是否在平板电脑上运行显示不同的图像。
后续内容
在用 Flex 4.5 设计多屏幕应用程序时,可以在方便与完美的像素控制之间做出选择。在构建应用程序之前,考虑屏幕像素密度对设计的影响,尽可能使用 Flex 提供的工具。
本系列的第 3部分讨论主题的编写和与平台相关的 skin。