转自: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.stageXMouseEvent.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 中为不同屏幕尺寸的多种设备提供支持的更多相关文章

  1. Android 中4种屏幕尺寸

    具体信息,请参考 Android 官方文档 Supporting Multiple Screens small(屏幕尺寸小于3英寸左右的布局),  normal(屏幕尺寸小于4.5英寸左右), lar ...

  2. ios中获取当前屏幕尺寸的方法

    //获取当前屏幕尺寸 CGRect screenFrame = [UIScreen mainScreen].bounds; int screenWidth = screenFrame.size.wid ...

  3. Adobe AIR 中为不同尺寸和分辨率屏幕适配

    在 Adobe AIR 中为不同屏幕尺寸的多种设备提供支持 http://www.adobe.com/cn/devnet/air/articles/multiple-screen-sizes.html ...

  4. 【Adobe Air程序开发】用Adobe Flex3开发AIR应用程序–入门指南

    1 安装Adobe AIR 运行时,和java的JVM类似.Adobe AIR 运行时允许在桌面运行AIR应用程序,脱离游览器的束缚.下载安装文件http://labs.adobe.com/downl ...

  5. Adobe AIR中使用Flex连接Sqlite数据库(1)(创建数据库和表,以及同步和异步执行模式)

    系列文章导航 Adobe AIR中使用Flex连接Sqlite数据库(1)(创建数据库和表) Adobe AIR中使用Flex连接Sqlite数据库(2)(添加,删除,修改以及语句参数) Adobe ...

  6. 开发Adobe AIR移动应用程序的考虑事项

    http://www.adobe.com/cn/devnet/air/articles/considerations-air-apps-mobile.html Adobe AIR 经过发展演进,已经超 ...

  7. adobe air 通用设置

    某些应用程序描述符设置对所有移动设备应用程序都很重要. 所需的 AIR 运行时版本 使用应用程序描述符文件的命名空间指定应用程序所需的 AIR 运行时版本. 在 application 元素中分配的命 ...

  8. 解决adobe air sdk打包 apk后自动在包名前面加上air. (有个点)前缀的问题

    早就找到了这个方法,但是一直忙没心思写博客. 默认情况下,所有 AIR Android 应用程序的包名称都带 air 前缀.若不想使用此默认行为,可将计算机环境变量 AIR_NOANDROIDFLAI ...

  9. adobe air类app 接入腾讯开放平台移动游戏使用带tencent包名前缀的问题

    作者:Panda Fang 出处:http://www.cnblogs.com/lonkiss/p/4209159.html 原创文章,转载请注明作者和出处,未经允许不可用于商业营利活动 ------ ...

随机推荐

  1. Nuget 摘录

    1 , Creating and Publishing a Package     https://docs.nuget.org/create/creating-and-publishing-a-pa ...

  2. 木匠ing[索引]

    古人有云,一个不会写代码的木匠不会是个好厨子. 为了响应这个号召,开始我的木工之路. 首先介绍一个网站,www.zuojiaju.com 木工爱好者 ,里面有大量的关于木匠的帖子,感谢一下. 以前只是 ...

  3. 题目1049:字符串去特定字符——九度OJ

    题目1049:字符串去特定字符 http://ac.jobdu.com/problem.php?pid=1049 时间限制:1 秒 内存限制:32 兆 题目描述: 输入字符串s和字符c,要求去掉s中所 ...

  4. WCF Client is Open Source

    WCF Client is Open Source Wednesday, May 20, 2015 Announcement New Project WCF We’re excited to anno ...

  5. TKinter布局之pack

    pack布局非常简单,不用做过多的设置,直接使用一个 pack 函数就可以了. 1.我们使用 pack 函数的时候,默认先使用的放到上面,然 后 依次向下排,它会给我们的组件一个自认为合适的位置 和大 ...

  6. html之label标签

    label标签为input元素定义标注,label标签与相关元素通过id属性绑定在一起. 相关属性: for:规定label绑定到哪个表单元素 form:规定label字段所属的一个或多个表单 示例代 ...

  7. ason 和 Java 对象转化示例

    1.工程 2.代码: JsonUtil.java package com.my.json; import java.util.ArrayList; import java.util.List; imp ...

  8. 开始学习IOS

    最好的学习方式就是动手. 对于有编程经验的程序员来说,学习另外一门技术最好的方式就是coding,虽然基础知识和IDE都不熟悉,但是在写代码的过程中,不断的解决问题,不断查找资料,不断感悟代码,一切都 ...

  9. zend studio一些常用配置

    zend studio 常用 配置 1.zend中添加注释是ctrl+slash,这个slash在哪里?如何来取消注释 slash是斜杠'/'那个键,就是在,.之后的那个. 进行注释是 ctrl+'/ ...

  10. WinForm窗体拖动代码

    本文转载自:http://www.cnblogs.com/ap0606122/archive/2012/10/23/2734964.html using System; using System.Co ...