[AIR] 在 Adobe AIR 中为不同屏幕尺寸的多种设备提供支持
转自:http://www.adobe.com/cn/devnet/air/articles/multiple-screen-sizes.html
无论是改编原本在浏览器 Flash Player 中运行的游戏使其在 iOS(使用 Adobe AIR)上运行,还是编写面向 Android 平板电脑的 Adobe AIR 应用程序,都需要至少支持几种不同的屏幕分辨率。我的末日策略游戏重建家园(有关更多背景资料,请阅读重建家园僵尸游戏)设计用于在浏览器中通过 800 x 600 像素的 Flash Player 畅玩,因此可以采用几项技巧进行改编,以便在这里的多种移动设备上运行。
在本文中,我会分享一些执行下列操作的技巧(基于 Adobe AIR 3.2)和代码示例:
- 检测屏幕大小
- 强制定向
- 计算物理维度
- 缩放和集中界面
- 将矢量转换为缩放位图
- 检测和限制 Apple iOS 设备
- 限制 Android 设备
检测屏幕大小
有四种不同的维度检查方法。
stage.width
和 stage.height
属性分别表示屏幕内容的宽度和高度(单位:像素)。如果 SWF 文件是 500 x 500 像素并包含一个 100 x 100 像素的正方形,则这些属性将返回 100 x 100。
您可以使用 stage.stageWidth 和 stage.stageHeight 获取 Flash Player 或 Adobe AIR 中的 SWF 文件的当前宽度和高度(单位:像素)。在上述情况下,它们均将返回 500
和 500
。在最初的几帧,它们的设置可能不正确。在 StageScaleMode.NO_SCALE
中,当这些设置发生变化时将会触发 resize 事件。
当进入全屏模式时,stage.fullScreenWidth 和 stage.fullScreenHeight 属性将会返回屏幕的宽度和高度,包括工具栏所占的空间。当且仅当由于方向发生更改导致宽度和高度对调时,这些属性才会发生改变。
最后,Capabilities.screenResolutionX 和 Capabilities.screenResolutionY 属性将返回屏幕水平方向和垂直方向的最大分辨率。换句话说,它们将向您展示设备的整个屏幕的大小。如果您的游戏在窗口(例如,在计算机上的模拟器上)运行,这些属性仍将返回整个显示器的大小。
理想情况下,您的应用程序应检查 stage.stageWidth
和 stage.stageHeight
,然后侦听 resize 事件(包括方向更改)。视设备不同,这些值起初可能完全错误,随后几秒钟之后,当应用程序变为全屏、工具栏消失及方向自我纠正时,您将会看到若干 resize 事件。下面是动态检查分辨率的方法:

- private static function handleResize(...ig) :void {
- var deviceSize:Rectangle = new Rectangle(0, 0, stage.stageWidth,
- stage.stageHeight);
- // adjust the gui to fit the new device resolution
- }
- stage.addEventListener(Event.RESIZE, handleResize);
- // call handleResize to initialize the first time
- handleResize();

对于游戏《重建家园》,我希望在游戏启动后立即获取维度,以便我可以一次性初始化 GUI。在这种情况下,Stage.fullScreenWidth
和 Stage.fullScreenHeight
属性可提供更加精确的估计值,虽然它们并不包含您应用程序上显示的任何系统工具栏(例如 Kindle Fire 上的某些工具栏)。方向同样可能发生错误,但由于游戏强制采用横向,我可以假设较宽的一侧始终是宽度,如下所示:
- var deviceSize:Rectangle = new Rectangle(0, 0,
- Math.max(stage.fullScreenWidth, stage.fullScreenHeight),
- Math.min(stage.fullScreenWidth, stage.fullScreenHeight));
强制定向
在 application.xml 文件中,您可以将 aspectRatio
默认值设置为 landscape
或 portrait
。对于 iOS,这还将决定您需要哪种初始屏幕图形,即 Default-Landscape.jpg
还是 Default-Portrait.jpg
。如果您要同时支持两种模式,请将 autoOrients
设置为 true
,在这种情况下,只要用户倾斜设备改变更像即会触发resize
事件。甚至在 autoOrients
设置为 false
的情况下,您的应用程序仍可能会以错误的方向启动。
我采用以下代码强制设置横向:

- <initialWindow>
- <visible>true</visible>
- <fullScreen>true</fullScreen>
- <autoOrients>false</autoOrients>
- <aspectRatio>landscape</aspectRatio>
- <renderMode>gpu</renderMode>
- ...
- </initialWindow>

更改 aspectRatio
可强制设置纵向:

- <initialWindow>
- <visible>true</visible>
- <fullScreen>true</fullScreen>
- <autoOrients>false</autoOrients>
- <aspectRatio>portrait</aspectRatio>
- <renderMode>gpu</renderMode>
- ...
- </initialWindow>

对于《重建家园》这类游戏,通常坚持采用纵横比而非来回切换,但绝大部分应用程序往往同时支持两种纵横比。
计算物理维度
虽然小型手机和大型平板电脑像素数可能相同,但物理尺寸却大相径庭。例如,当触摸 iPhone 4 retina 的 960 x 640 屏幕时,您的指尖所占的像素面积是触摸 iPad 1 的 1024 x 768 屏幕的四倍。这对于字体和图标(特别是按钮大小)易读性非常重要。
看一下每英寸点数(称作 PPI 或每英寸像素更恰当),您可以估算为避免操作错误应当设置多大的界面。Apple 建议 163 dpi 屏幕(例如 iPhone 3GS)的按钮和交互对象的最小尺寸应为 44 x 44 像素。
Android 使用与密度无关的像素 (dp) 概念,这是一个规范化像素单位,用于对照 160 dpi 屏幕定义分辨率。设备 dp 的计算方法:像素 * 160 / DPI。要确定 dpi、dp 和大小(以英寸为单位),您可以使用以下代码:
- var dpi:Number = Capabilities.screenDPI;
- var dpWide:Number = stage.fullScreenWidth * 160 / dpi;
- var inchesWide:Number = stage.fullScreenWidth / dpi;
不幸的是,设备通常会报告错误的 dpi(特别是 iOS 显示器)。Capabilities.screenDPI 实际上是基于其他信息的最佳猜测,但可能是错误的。为进行比较,您可以获取操作系统报告的原始 dpi,如下所示:
- var serverString:String = unescape(Capabilities.serverString);
- var reportedDpi:Number = Number(serverString.split("&DP=", 2)[1]);
《重建家园》的 iOS 版本分为两种静态界面布局:一种适用于 iPad,另一种文本和按钮较大的布局适用于 iPhone 和 iPod Touch。您可以使用当前设备的 dpi 和物理尺寸挑选要使用的布局。
缩放和集中界面
Adobe AIR 包含多个内置缩放模式,但我发现 SHOW_ALL 和 EXACT_FIT 在设备中的行为有些古怪。为完全掌控设备缩放(当调整位图时尤其有用),我发现最好使用 NO_SCALE 自行进行缩放处理。
Android 版的《重建家园》使用 1024 x 600 GUI,以便相应地缩放,然后沿水平方向在屏幕顶部集中。此操作在首次加载游戏时执行,如下所示:

- stage.scaleMode = StageScaleMode.NO_SCALE;
- stage.align = StageAlign.TOP_LEFT;
- var guiSize:Rectangle = new Rectangle(0, 0, 1024, 600);
- var deviceSize:Rectangle = new Rectangle(0, 0,
- Math.max(stage.fullScreenWidth, stage.fullScreenHeight),
- Math.min(stage.fullScreenWidth, stage.fullScreenHeight));
- var appScale:Number = 1;
- var appSize:Rectangle = guiSize.clone();
- var appLeftOffset:Number = 0;
- // if device is wider than GUI's aspect ratio, height determines scale
- if ((deviceSize.width/deviceSize.height) > (guiSize.width/guiSize.height)) {
- appScale = deviceSize.height / guiSize.height;
- appSize.width = deviceSize.width / appScale;
- appLeftOffset = Math.round((appSize.width - guiSize.width) / 2);
- }
- // if device is taller than GUI's aspect ratio, width determines scale
- else {
- appScale = deviceSize.width / guiSize.width;
- appSize.height = deviceSize.height / appScale;
- appLeftOffset = 0;
- }
- // scale the entire interface
- base.scale = appScale;
- // map stays at the top left and fills the whole screen
- base.map.x = 0;
- // menus are centered horizontally
- base.menus.x = appLeftOffset;
- // crop some menus which are designed to run off the sides of the screen
- base.scrollRect = appSize;

无论怎样执行缩放,在库中,对象的相对大小和位置均保持不变。例如,滚动库存清单中的项目间隔始终为 50 像素 (px)。但每当您使用 MouseEvent.stageX
、MouseEvent.stageY
或 DisplayObject.localToGlobal
时,都需要进行调整缩放。
将矢量转换为缩放位图
《重建家园》的浏览器版本使用建筑物、图标、按钮和背景等矢量对象。它们在缩放时看起来很棒并可保证文件较小,但这种缩放过于复杂,不适合在设备上高效地呈现。它们还可以使用 Adobe AIR GPU 呈现模式不支持的过滤器,并且具有锯齿状边缘,以便在 stage.quality
降低时提高性能。我的解决方案是利用前面计算得出的比例创建的位图替换它们。仅在首次加载游戏时执行一次此操作。
如果您不使用矢量,还可以使用下面的代码将大位图缩小至所需的尺寸,这样它们看起来将更加平整,占用的内存也会更小。但是,如果缩放过度,包含文本或图标的位图可能会变得难以辨认。如果有两个或两个以上的预呈现位图集合,并根据屏幕 dpi 进行选择,那么效果可能会更好。

- // the original object's size (won't include glow effects!)
- var objectBounds:Rectangle = object.getBounds(object);
- objectBounds.x *= object.scaleX;
- objectBounds.y *= object.scaleY;
- objectBounds.width *= object.scaleX;
- objectBounds.height *= object.scaleY;
- // the target bitmap size
- var scaledBounds:Rectangle = objectBounds.clone();
- scaledBounds.x *= appScale;
- scaledBounds.y *= appScale;
- scaledBounds.width *= appScale;
- scaledBounds.height *= appScale;
- // scale and translate up-left to fit the entire object
- var matrix:Matrix = new Matrix();
- matrix.scale(object.scaleX, object.scaleY);
- matrix.scale(appScale, appScale);
- matrix.translate(-scaledBounds.x, -scaledBounds.y);
- // briefly increase stage quality while creating bitmapData
- stage.quality = StageQuality.HIGH;
- var bitmapData:BitmapData = new BitmapData(scaledBounds.width,
- scaledBounds.height, true);
- bitmapData.draw(object, matrix);
- stage.quality = StageQuality.LOW;
- // line up bitmap with the original object and replace it
- var bitmap:Bitmap = new Bitmap(bitmapData);
- bitmap.x = objectBounds.x + object.x;
- bitmap.y = objectBounds.y + object.y;
- object.parent.addChildAt(bitmap, object.parent.getChildIndex(object));
- object.parent.removeChild(object);
- // invert the scale of the bitmap so it fits within the original gui
- // this will be reversed when the entire application base is scaled
- bitmap.scaleX = bitmap.scaleY = (1 / appScale);

如果您需要两个或多个相同的 Bitmap 对象,则应保存并重用单一 BitmapData 对象,因为这样只需占用极少的内存即可以此创建另一个 Bitmap 对象。切记,在完成此操作后,调用 Bitmap.bitmapData.dispose()
立即从内存中将其删除,而不是等待它进行垃圾收集。
检测和限制 Apple iOS 设备
iPhone、iPad 和 iPod touch 会在 Capabilities.os
中报告各自的名称,这样就能精确地检测出游戏在哪种设备上运行并做出相应的调整。例如,在运行速度较慢的 iPad 1 和 iPhone 3GS 上,《重建家园》的动画和地图较少。下面的代码展示了如何检测 iOS 设备并做出相应的调整:

- public static function getDevice():String {
- var info:Array = Capabilities.os.split(" ");
- if (info[0] + " " + info[1] != "iPhone OS") {
- return UNKNOWN;
- }
- // ordered from specific (iPhone1,1) to general (iPhone)
- for each (var device:String in IOS_DEVICES) {
- if (info[3].indexOf(device) != -1) {
- return device;
- }
- }
- return UNKNOWN;
- }
- public static const IPHONE_1G:String = "iPhone1,1"; // first gen is 1,1
- public static const IPHONE_3G:String = "iPhone1"; // second gen is 1,2
- public static const IPHONE_3GS:String = "iPhone2"; // third gen is 2,1
- public static const IPHONE_4:String = "iPhone3"; // normal:3,1 verizon:3,3
- public static const IPHONE_4S:String = "iPhone4"; // 4S is 4,1
- public static const IPHONE_5PLUS:String = "iPhone";
- public static const TOUCH_1G:String = "iPod1,1";
- public static const TOUCH_2G:String = "iPod2,1";
- public static const TOUCH_3G:String = "iPod3,1";
- public static const TOUCH_4G:String = "iPod4,1";
- public static const TOUCH_5PLUS:String = "iPod";
- public static const IPAD_1:String = "iPad1"; // iPad1 is 1,1
- public static const IPAD_2:String = "iPad2"; // wifi:2,1 gsm:2,2 cdma:2,3
- public static const IPAD_3:String = "iPad3"; // (guessing)
- public static const IPAD_4PLUS:String = "iPad";
- public static const UNKNOWN:String = "unknown";
- private static const IOS_DEVICES:Array = [IPHONE_1G, IPHONE_3G, IPHONE_3GS,
- IPHONE_4, IPHONE_4S, IPHONE_5PLUS, IPAD_1, IPAD_2, IPAD_3, IPAD_4PLUS,
- TOUCH_1G, TOUCH_2G, TOUCH_3G, TOUCH_4G, TOUCH_5PLUS];

虽然我在上面列出了所有 Apple 移动设备,但您无需支持第一代或第二代 iPhone 和 iPod Touch,因为它们缺少运行 AIR 3.2 应用程序所需的架构 (armv7) 和图形库 (opengles-2)。
有几种方法可进一步将您的应用程序限制为仅在 iOS 兼容设备子集上运行。
您可以通过 UIDeviceFamily
(位于 application.xml 中)将应用程序设置为仅在 iPad 上运行,或者仅在目标 iPhone/iPod Touch 上运行;例如:

- <iPhone>
- <InfoAdditions><![CDATA[
- <key>UIDeviceFamily</key>
- <array>
- <!-- iPhone support -->
- <string>1</string>
- <!-- uncomment for iPad support
- <string>2</string>
- -->
- </array>
- ...
- ]]></InfoAdditions>
- </iPhone>

您可以通过在同一 application.xml 中设置 UIRequiredDeviceCapabilities 进行更加具体的限定。例如,需要静态照相机意味着设备必须具有照相机。有了陀螺仪可方便地将您的应用程序限定为在最新的 iPhone 4+、iPod Touch 4+ 和 iPad 2+。Wikipedia 提供了 iOS 设备及其功能的最终名单。
为在 iPhone 4S+(AIR 3.2 目前还不支持 iPad 3 retina)中启用双密度,请将requestedDisplayResolution
设置为 high
。这会将 iPhone 的报告屏幕分辨率从 480 x 320 (163 dpi) 更改为 960 x 640 (326 dpi)。确保您的应用程序已针对 iPhone 3GS 和第三代 iPod Touch 进行了相应的缩小,它们不支持视网膜显示。
- <iPhone>
- ...
- <!-- requestedDisplayResolution standard (the default), or high. -->
- <requestedDisplayResolution>high</requestedDisplayResolution>
- </iPhone>
限制 Android 设备
目前市场上共有近 1000 种不同的 Android 设备。它们不通过 Capabilities.os
报告自身名称,因而没有办法确定 CPU 容量或设备内存。但是,您可以通过在 application.xml 中获取某种屏幕大小和密度,在 Google Play 及其他 Android 应用程序商店中查找特定的 Android 设备(例如平板电脑)。
screenSize
属性是指较短的屏幕宽度(英寸数):
small
(约 2 到 3 英寸)normal
(约 3 到 5 英寸)large
(约 5 到 7 英寸)xlarge
(约 7 英寸或更高)
screenDensity
属性反映计算的每英寸像素:
ldpi
(100 到 120 dpi)mdpi
(120到180 dpi)tvdpi
(约 213 dpi)hdpi
(180到260 dpi)xhdpi
(大于 260 dpi)
以下代码(application.xml 除外)表示《重建家园》不支持小屏幕和低密度屏幕:

- <android>
- <manifestAdditions><![CDATA[
- <manifest android:installLocation="preferExternal">
- <supports-screens
- android:smallScreens="false"
- android:normalScreens="true"
- android:largeScreens="true"
- android:xlargeScreens="true"/>
- <compatible-screens>
- <!-- list the screens you support here -->
- <screen android:screenSize="normal" android:screenDensity="hdpi" />
- <screen android:screenSize="normal" android:screenDensity="xhdpi" />
- <screen android:screenSize="large" android:screenDensity="ldpi" />
- <screen android:screenSize="large" android:screenDensity="mdpi" />
- <screen android:screenSize="large" android:screenDensity="hdpi" />
- <screen android:screenSize="large" android:screenDensity="xhdpi" />
- <screen android:screenSize="xlarge" android:screenDensity="ldpi" />
- <screen android:screenSize="xlarge" android:screenDensity="mdpi" />
- <screen android:screenSize="xlarge" android:screenDensity="hdpi" />
- <screen android:screenSize="xlarge" android:screenDensity="xhdpi" />
- </compatible-screens>
- ...
- </manifest>
- ]]></manifestAdditions>
- </android>

截至 2012 年初,最受欢迎的 Android 设备有 Samsung Galaxy 系列手机 (800 x 480)、Motorola Droids (960 x 540)、HTC Evo 和 Desire (800 x 480)、Samsung Galaxy Tab (1280 x 800) 和 Kindle Fire (1024 x 600)。但是,每个国家流行的款式不同,Android 设备领域并不存在绝对的领导者,因此并不建议针对特定的设备。
我通过 Nexus One(800x480,2010 年 1 月发布)进行了本地测试,这是一款我想要支持的低端手机,而 Kindle Fire 则是一款中端平板电脑。由于慢速设备可能会让人感到挫败,因此最好使用旧硬件进行开发,以便对自己的最低性能做出良好的判断。TestDroid 等服务有助于您在大量设备上进行远程测试。
下一步阅读方向
由于担心处理过多设备,我已将《重建家园》入住 Android 的进程推迟了数月之久,但最终让缩放功能正常工作非常简单,只用了几天就实现了正常运行。我还将这款游戏移植到 BlackBerry PlayBook,事实证明这点努力非常值得。
Adobe AIR 能够轻松地在上千种不同的移动设备上运行同样的代码。Adobe 负责完成大部分艰苦工作,因此如果您计划采用某种设备,同样也可以支持所有不同规格和尺寸的同类设备。
[AIR] 在 Adobe AIR 中为不同屏幕尺寸的多种设备提供支持的更多相关文章
- Android 中4种屏幕尺寸
具体信息,请参考 Android 官方文档 Supporting Multiple Screens small(屏幕尺寸小于3英寸左右的布局), normal(屏幕尺寸小于4.5英寸左右), lar ...
- ios中获取当前屏幕尺寸的方法
//获取当前屏幕尺寸 CGRect screenFrame = [UIScreen mainScreen].bounds; int screenWidth = screenFrame.size.wid ...
- Adobe AIR 中为不同尺寸和分辨率屏幕适配
在 Adobe AIR 中为不同屏幕尺寸的多种设备提供支持 http://www.adobe.com/cn/devnet/air/articles/multiple-screen-sizes.html ...
- 【Adobe Air程序开发】用Adobe Flex3开发AIR应用程序–入门指南
1 安装Adobe AIR 运行时,和java的JVM类似.Adobe AIR 运行时允许在桌面运行AIR应用程序,脱离游览器的束缚.下载安装文件http://labs.adobe.com/downl ...
- Adobe AIR中使用Flex连接Sqlite数据库(1)(创建数据库和表,以及同步和异步执行模式)
系列文章导航 Adobe AIR中使用Flex连接Sqlite数据库(1)(创建数据库和表) Adobe AIR中使用Flex连接Sqlite数据库(2)(添加,删除,修改以及语句参数) Adobe ...
- 开发Adobe AIR移动应用程序的考虑事项
http://www.adobe.com/cn/devnet/air/articles/considerations-air-apps-mobile.html Adobe AIR 经过发展演进,已经超 ...
- adobe air 通用设置
某些应用程序描述符设置对所有移动设备应用程序都很重要. 所需的 AIR 运行时版本 使用应用程序描述符文件的命名空间指定应用程序所需的 AIR 运行时版本. 在 application 元素中分配的命 ...
- 解决adobe air sdk打包 apk后自动在包名前面加上air. (有个点)前缀的问题
早就找到了这个方法,但是一直忙没心思写博客. 默认情况下,所有 AIR Android 应用程序的包名称都带 air 前缀.若不想使用此默认行为,可将计算机环境变量 AIR_NOANDROIDFLAI ...
- adobe air类app 接入腾讯开放平台移动游戏使用带tencent包名前缀的问题
作者:Panda Fang 出处:http://www.cnblogs.com/lonkiss/p/4209159.html 原创文章,转载请注明作者和出处,未经允许不可用于商业营利活动 ------ ...
随机推荐
- Nuget 摘录
1 , Creating and Publishing a Package https://docs.nuget.org/create/creating-and-publishing-a-pa ...
- wikioi 1203 判断浮点数是否相等
/*======================================================================== 1203 判断浮点数是否相等 题目描述 Descr ...
- 【转】windows7的桌面右键菜单的“新建”子菜单,在注册表哪个位置,如何在“新建"里面添加一个新项
点击桌面,就会弹出菜单,然后在“新建”中就又弹出可以新建的子菜单栏.office与txt 的新建都是在这里面的.我想做的事情是:在右键菜单的“新建” 中添加一个“TQ文本”的新建项,然后点击它之后,桌 ...
- linux系统下搭建php环境之-Discuz论坛
1.安装搭建论坛必要的软件 apache php mysql CentOS系统我们可以直接使用 yum install 的方式进行软件安装,腾讯云有提供软件安装源,是同步CentOS官方的安装源,包涵 ...
- (zt)Lua的多任务机制——协程(coroutine)
原帖:http://blog.csdn.net/soloist/article/details/329381 并发是现实世界的本质特征,而聪明的计算机科学家用来模拟并发的技术手段便是多任务机制.大致上 ...
- EnterpriseLibrary之Caching应用
http://blog.csdn.net/linux7985/article/details/6239433 http://cache.baiducontent.com/c?m=9f65cb4a8c8 ...
- LintCode "Backpack"
A simple variation to 0-1 Knapsack. class Solution { public: /** * @param m: An integer m denotes th ...
- php访问mysql工具类
本文转载自:http://www.cnblogs.com/lida/archive/2011/02/18/1958211.html <?php class mysql { private $db ...
- golang的连接池例子
github.com/jolestar/go-commons-pool 测试代码 package main import ( "github.com/jolestar/go-commons- ...
- html中div定位练习
html中div定位练习,实现简单的计划列表: 记录div定位时主要的属性:float.position等,以及对应的relative和absolute等,同时使用到angular js中的数据绑定, ...