辅助功能*

Charles Ward

Charles Ward

Adobe

出版日期:
2008 年 2 月 28 日
用户级别:
中/高级
产品:
Adobe AIR

测量虚拟桌面

放置或移动窗口时, 经常需要了解桌面屏幕的尺寸和相对位置。AIR 通过 Screen 类提供对此信息的访问。图 1 中显示的 Screens 范例应用程序演示如何使用 Screen API。

Screens 范例应用程序

图 1。Screens 范例应用程序利用了 AIR 中的 Screen API。

注意: 本范例应用程序按原样提供, 用于教学目的。

要求

若要充分利用本篇文章, 您需要以下软件和文件:

Adobe AIR

Adobe Flash CS3 Professional

为 Flash CS3 Professional 提供的 Adobe AIR 更新

范例文件:

本范例应用程序包括以下文件:

  • Screens.as: 应用程序主文件, 包括本文所讨论的代码
  • Screens.fla: 与 Adobe Flash CS3 Professional 配合使用的 Flash 文档
  • Screens-app.xml: AIR 应用程序描述符文件
  • AIR 图标文件范例

必备知识

应具备使用 Flash CS3 构建应用程序的一般经验。有关使用此快速入门指南的详细信息, 请参阅用 Flash 构建快速入门范例应用程序

关于虚拟桌面

虚拟桌面由一个或多个屏幕组成。 (由操作系统) 指定主屏幕, 并将虚拟桌面的原点放置在此屏幕的左上角。向右和向下为正值, 就像您对计算机显示器所料想的那样。主屏幕上方和左侧的屏幕中的坐标为负值。

屏幕的可用区域要排除任何任务栏、菜单栏、停靠栏、边栏或其它始终绘制于任何窗口之前的栏。Screen 类报告整个屏幕和可用区域的尺寸。

Screens 范例应用程序

Screens 应用程序在虚拟桌面上绘制屏幕的示意图, 其中包括该应用程序自身窗口的缩小版本。每个屏幕的可用区域以蓝色显示。在屏幕内、但在可用区域之外的区域以灰色显示。应用程序窗口所在的屏幕上以浅蓝色加亮显示。

若要测试 Screens, 请启动应用程序文件 (Screens.air)。可以四处移动应用程序窗口, 更改显示器的分辨率和颜色深度, 以及更改任务栏的大小和位置, 从而观察对 AIR Screen API 报告的屏幕信息所产生的效果。使用方向键可以使窗口在不同屏幕之间跳转。

了解代码

Screen 应用程序使用了若干非 AIR 所特有的图形函数。有关这些函数的详细信息, 请参阅《ActionScript 3.0 语言参考》*

计算虚拟边界

矩形边界描述了位置和尺寸。虚拟桌面的矩形边界是包含所有屏幕的边界框。为了计算此边界框, Screens 应用程序遍历由静态 Screen.screens 属性提供的 Screen 对象数组中的每个屏幕, 并将屏幕的每个边与边界框的当前边相比较。如果屏幕的值远远超过当前边界框的值, 则加大边界框的值, 使其与屏幕相匹配。结果为包含所有屏幕的矩形边界。

private function computeVirtualBounds(screens:Array):Rectangle{
    var bounds:Rectangle = new Rectangle();
    for each (var screen:Screen in screens){
        if(bounds.left > screen.bounds.left){bounds.left = screen.bounds.left;}
        if(bounds.right < screen.bounds.right){bounds.right = screen.bounds.right;}
        if(bounds.top > screen.bounds.top){bounds.top = screen.bounds.top;}
        if(bounds.bottom < screen.bounds.bottom){bounds.bottom = screen.bounds.bottom;}
    }
    return bounds;
}

随屏幕成比例地调整应用程序窗口大小

计算出虚拟桌面的大小后, 即可确定应用程序窗口的大小。将窗口的宽度设置为主屏幕宽度的 60% (再为边框增加少许)。随虚拟桌面成比例地调整窗口的高度, 以使窗口的高度足以完整地显示桌面的缩略图:

var scale:Number = (Screen.mainScreen.bounds.width * .6)/virtualScreenBounds.width;
stage.stageWidth = (Screen.mainScreen.bounds.width * .6) + (border * 2);
stage.stageHeight = (virtualScreenBounds.height * scale) + (border * 2);

可以使用 Screen 类的静态 mainScreen 属性访问操作系统所指定主屏幕的 Screen 对象。

绘制桌面的示意图

由于 Screens 应用程序类扩展 Sprite 类, 因此使用子画面的图形函数绘制桌面的示意图。使用屏幕坐标以完整大小绘制桌面中的屏幕。作用于主子画面的转换矩阵平移并缩放桌面示意图, 使其适合窗口的显示区域。

用 Matrix 类的 createBox() 方法创建转换矩阵。此方法采用水平和垂直缩放系数作为参数。对这两个参数使用相同的缩放比例, 该比例的计算方法为窗口的显示区域除以虚拟桌面的总宽度。createBox() 方法对于示意图要移动 (或平移) 的幅度也采用了参数, 以使示意图在窗口上恰当地排列整齐。

redraw() 方法计算出转换矩阵, 将其赋给主子画面 transform 对象的 matrix 属性, 然后调用绘制桌面示意图的元素的函数。响应 enterFrame 事件时将调用 redraw() 方法。

private function redraw(event:Event):void{
    virtualScreenBounds = computeVirtualBounds(Screen.screens);
    var scale:Number = (stage.nativeWindow.width - border * 2)/virtualScreenBounds.width;
    virtualScreenToWindowTransform.createBox(scale,scale,0,
    border - (virtualScreenBounds.x * scale),
    border - (virtualScreenBounds.y * scale));
    transform.matrix = virtualScreenToWindowTransform;
    graphics.clear();
    drawScreenRepresentation();
    highlightScreens();
    drawWindowModel();
}

为了绘制桌面, drawScreenRepresentation() 方法遍历 Screen 类的 screens 数组。对于每个屏幕, 例程都为整个屏幕绘制一个矩形, 再为可用区域绘制另一个矩形:

with(graphics){
    //绘制屏幕矩形
    beginFill(screenColor,.5);
        drawRect(screen.bounds.left,
                 screen.bounds.top,
                 screen.bounds.width,
                 screen.bounds.height);
    endFill();
    //为屏幕的可用区域绘制矩形
    beginFill(usableScreenColor,.5);
        drawRect(screen.visibleBounds.left,
                 screen.visibleBounds.top,
                 screen.visibleBounds.width,
                 screen.visibleBounds.height);
    endFill();
    //...

此方法还绘制若干标签。用 BitmapData.draw() 方法将这些标签绘制到位图中。使用位图填充的方式向主子画面绘制所得的标签位图。

使用位图填充

使用位图填充时的一个意外之处是填充对准子画面的原点, 而非对准要填充区域的左上角。对于重复的纹理, 这可能不是问题, 但用于标签时, 这可能会使文字远离被填充区域之外。解决方案是使用另一个转换矩阵将标签位图移动至被填充区域。下面几行向主屏幕的中心绘制包含“Main Screen”这几个字的位图:

//绘制标签, 用于显示哪个屏幕是“主”屏幕
var mainLabelBitmap:BitmapData = drawLabel("Main Screen");
labelTransform = new Matrix(1, 0, 0, 1,
                            (Screen.mainScreen.visibleBounds.width - mainLabelBitmap.width)/2,
                            (Screen.mainScreen.visibleBounds.height - mainLabelBitmap.height)/2);
with(graphics){
    beginBitmapFill(mainLabelBitmap,labelTransform,false);
        drawRect((Screen.mainScreen.visibleBounds.width - mainLabelBitmap.width)/2,
                 (Screen.mainScreen.visibleBounds.height - mainLabelBitmap.height)/2,
                 mainLabelBitmap.width, mainLabelBitmap.height);
    endFill();
}

在屏幕之间移动窗口

通过 Screens 应用程序, 可以使用方向键使窗口在屏幕之间跳转。为了确定向何处移动窗口, 应用程序必须确定其当前所处的屏幕以及位于所选方向的屏幕 (如果有)。

可以使用静态 Screen.getScreensForRectangle() 方法确定当前屏幕。此方法以数组形式返回与矩形相交的所有屏幕。以下函数将窗口边界传入 getScreensForRectangle() 方法, 并返回所得数组中的第一个屏幕:

private function getCurrentScreen():Screen{
    var current:Screen;
    var screens:Array = Screen.getScreensForRectangle(stage.nativeWindow.bounds);
    (screens.length > 0) ? current = screens[0] : current = Screen.mainScreen;
    return current;
}

如果该窗口由于某种原因不在任何屏幕中, 则返回主屏幕。

为了找到窗口所要移至的正确屏幕, 将对屏幕的数组按垂直或水平方向进行排序, 然后通过遍历排序后的数组来比较相关的坐标。以下方法将窗口移向左侧:

/*Moves the window to the next screen to the left*/
private function moveLeft():void{
    var currentScreen:Screen = getCurrentScreen();
    var left:Array = Screen.screens;
    left.sort(sortHorizontal);
    for(var i:int = 0; i < left.length - 1; i++){
        if(left[i].bounds.left < stage.nativeWindow.bounds.left){
            stage.nativeWindow.x += left[i].bounds.left - currentScreen.bounds.left;
            stage.nativeWindow.y += left[i].bounds.top - currentScreen.bounds.top;
        }
    }
}

上方调用的 sort 方法使用以下排序函数比较屏幕坐标:

private function sortHorizontal(a:Screen, b:Screen):int{
    if (a.bounds.left > b.bounds.left){
        return 1;
    } else if (a.bounds.left < b.bounds.left){
        return -1;
    } else {
        return 0;
    }
}

关于作者

Charles Ward 是一名技术作者, 它喜欢钻研新技术。在之前的 (专业) 职业生涯中, 他曾参与创建开拓性的计算机游戏, 如 Falcon 3.0 和 4.0 以及 Star Trek: A Final Unity。在闲暇时间, Charles 喜欢自由潜水以及和他的孩子们玩耍。