I'm attempting to build a window-based application system (a mini GUI, if you will) within an Adobe Air environment (Flex 4). When implementing window dragging, I account for the z-order by updating the now activated window's depth from a global function, which updates a hidden variable every time accessed (ensuring the selected window is always on top). (Note: I chose global functions over a set of events because the goal of this action is to receive the returned value immediately, not to tell some other part of the system to do something, so I believe this to be a valid exception to the normal suggestion to "use events to communicate between objects.") I encountered dozens of possible solutions to create and call this global function, but the only one that has thus far worked is:
event.target.parentApplication.obtainNewHigherDepth()
As you can see, it's not really a global function call at all, but a call to a top-level (or more top-level) parent that happens to contain the function needed. In my test cases, this works just fine even as I add internal content components to the windows. However, when adding a Google Map as the window's content, clicking on the map type buttons results in an error:
ReferenceError: Error #1069: Property parentApplication not found on com.google.maps.wrappers.
WrappableSprite and there is no default value.
at Window/selectWindow()[C:\Users\d04000196\Desktop\Projects\UI Concepting\ErrorExample\src\Window.mxml:18]
So it appears that the Google Map API is attempting to call my code with the wrong context. Being new to Flex/Air, I don't know how to bind the relevant context like one can do with Javascript. But that shouldn't be necessary.
If there is a better way to create and call a truly global function (and use the relevant global variable), this problem of the misaligned contexts wouldn't matter. Or, if there is a known solution for this particular issue with the Google Maps API, that might also work as a solution (but wouldn't be as future proof).
The below files are the minimum code required to illustrate the problem.
To Setup
- Load the below files into the default package of a new Adobe Air project via Flash Builder.
- Download the Google Maps API and add the library to the project.
To Illustrate Error
- Run application
- Click the "Open a Map" button
- Click any of the map type buttons in the top right of the map area
ExampleError.mxml
<?xml version="1.0" encoding="utf-8"?>
<s:WindowedApplication xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
width="800" height="600"
frameRate="60"
applicationComplete="init()">
<fx:Script>
<![CDATA[
// ********************
// THE PART IN QUESTION
// ********************
private var maxDepth:Number = 0;
public function obtainNewHigherDepth():Number {
return (maxDepth++);
}
private function openAMap(event:MouseEvent):void {
var window:Window = new Window();
var map:CustomMap = new CustomMap();
window.x = 160;
window.titleBarText.text = "Map"
window.content.addElement(map);
this.addElement(window);
}
private function init():void {
openMapButton.addEventListener(MouseEvent.CLICK, openAMap);
statusBar.visible = false;
}
]]>
</fx:Script>
<s:VGroup x="10" y="10" width="135" height="580">
<s:Button label="Open a Map" id="openMapButton" />
</s:VGroup>
</s:WindowedApplication>
Window.mxml
<?xml version="1.0" encoding="utf-8"?>
<s:Group xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
x开发者_如何学编程mlns:visuals="com.lsus.udop.visuals.*"
width="400" height="400" depth="1"
initialize="init()">
<fx:Script>
<![CDATA[
import flash.events.MouseEvent;
private var moving:Boolean = false;
private var relativeX:Number;
private var relativeY:Number;
private function selectWindow(event:MouseEvent):void {
// ********************
// THE PART IN QUESTION
// ********************
this.depth = event.target.parentApplication.obtainNewHigherDepth();
}
private function windowDragMouseDown(event:MouseEvent):void {
relativeX = event.stageX - this.x;
relativeY = event.stageY - this.y;
moving = true;
this.systemManager.addEventListener(MouseEvent.MOUSE_MOVE, windowDragMouseMove, true);
this.systemManager.addEventListener(MouseEvent.MOUSE_UP, windowDragMouseUp, true);
}
private function windowDragMouseMove(event:MouseEvent):void {
event.stopImmediatePropagation();
this.x = event.stageX - relativeX;
this.y = event.stageY - relativeY;
}
private function windowDragMouseUp(event:MouseEvent):void {
event.stopImmediatePropagation();
moving = false;
this.systemManager.removeEventListener(MouseEvent.MOUSE_MOVE, windowDragMouseMove, true);
this.systemManager.removeEventListener(MouseEvent.MOUSE_UP, windowDragMouseUp , true);
}
private function init():void {
this.addEventListener(MouseEvent.MOUSE_DOWN, selectWindow);
titleBar.addEventListener(MouseEvent.MOUSE_DOWN, windowDragMouseDown);
this.depth = this.parentApplication.obtainNewHigherDepth();
}
]]>
</fx:Script>
<s:Rect width="100%" height="100%">
<s:fill>
<s:SolidColor color="0x000000" />
</s:fill>
</s:Rect>
<s:Group id="titleBar" top="0" left="0" right="0" height="20">
<s:Label text="Title Bar Text" id="titleBarText" top="5" bottom="0" left="5" color="0xffffff" />
</s:Group>
<s:Group id="content" top="21" left="1" right="1" bottom="1" />
</s:Group>
CustomMap.mxml
<?xml version="1.0" encoding="utf-8"?>
<s:Group xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
width="100%" height="100%"
initialize="init()">
<fx:Script>
<![CDATA[
import com.google.maps.Map;
import com.google.maps.MapEvent;
import com.google.maps.MapType;
import com.google.maps.controls.MapTypeControl;
private function onMapReady(event:MapEvent):void {
map.enableScrollWheelZoom();
map.addControl(new MapTypeControl());
}
private function init():void {
map.addEventListener(MapEvent.MAP_READY, onMapReady);
}
]]>
</fx:Script>
<maps:Map xmlns:maps="com.google.maps.*"
url="http://code.google.com/apis/maps/"
key="ABQIAAAAiVMBdjzjhpqA-qSxuuT0cRT2yXp_ZAY8_ufC3CFXhHIE1NvwkxQ4igt7gOyr4x11lp3ETKR2fOH5Hw"
sensor="false"
width="100%" height="100%" id="map" />
</s:Group>
Update
Found on another question just now (It never fails, does it?), this:
FlexGlobals.topLevelApplication
appears to give me what I'm looking for. It has the disadvantage of being insecure if my window component is ever used outside of this particular application, but since that is not planned, it should suffice. Still, if there is a better solution, I'd love to hear it.
What I think you should do is:
- Create a custom Event called DepthRequestEvent
- Add listener to main app (ExampleError.mxml):
addEventListener(DepthRequestEvent.REQUEST_DEPTH, obtainNewHigherDepth)
. - Add listener to Window.mxml:
addEventListener(DepthRequestEvent.REQUEST_RESULT, handleDepthRequestResult)
. - Dispatch a
DepthRequestEvent.REQUEST_DEPTH
from Window.mxml - In main app's obtainNewHigherDepth() function, dispatch a
DepthRequestEvent.REQUEST_RESULT
event after adding thedepth
value to the event. - The
handleDepthRequestResult()
function in Window.mxml will capture this, and you can get the event.depthValue variable from the event parameter
Update: In Window.mxml add the event listener using: FlexGlobals.topLevelApplication.addEventListener(.....
Bingo, loosely coupled classes! (calling parents of class is a kind of no no)
Hey I just had a really quick read though it, maybe I am wrong, but I think you should have a look at the PopUpManager. It takes care of the depth sorting
精彩评论