I have a UIComponent that sits on top of a grid visually, which is also a UIComponent. The grid and the UIComponent that sits on top are on different branches. The gird has children which are Sprites. When the MousEvent.CLICK is dispatched it goes to the UIComponent on top. What I want it to do is go the element of the grid underneath. Is this possible?
Yes you can,
if your element id is "ui_component"
then in ActionScript you should add two properties
ui_component.mouseEnabled = true;
ui_component.mouseChildren = true;
This will tell your component to pass click event on MovieClip behind it.
I hope this helps.
What I had to do was just make the UIComponent on top top.mousEnabled = false
Another option, if your implementation does not allow for you to disable the mouse events for the top UIComponent, is to manually redispatch the event.
Here's a rather generic example. Attach the following handler for a MouseEvent.CLICK to either the container that holds the other UIComponents or the one on the UIComponent on top:
private function propagateClickEvent(event:MouseEvent):void
{
// find all objects under the click location
var uicOver:Array = getObjectsUnderPoint(new Point(event.stageX, event.stageY));
// filter out what you don't want; in this case, I'm keeping only UIComponents
uicOver = uicOver.filter(isUIComponent);
// resolve skins to parent components
uicOver = uicOver.map(resolveSkinsToParents);
// remove the current item (otherwise this function will exec twice)
if (event.currentTarget != event.target) { // otherwise let the following line do the removal
uicOver.splice(uicOver.indexOf(event.currentTarget),1);
}
// remove the dispatching item (don't want to click it twice)
uicOver.splice(uicOver.indexOf(event.target),1);
// dispatch a click for everything else
for each (var uic:UIComponent in uicOver) {
uic.dispatchEvent(new MouseEvent(MouseEvent.CLICK, false)); // no bubbling!
}
}
// test if array item is a UIComponent
private function isUIComponent(item:*, index:int, arr:Array):Boolean {
return item is UIComponent;
}
// resolve Skins to parent components
private function resolveSkinsToParents(item:*, index:int, arr:Array):* {
if (item is Skin) {
return item.parent;
}
return item;
}
This code would work as follows in MXML:
<s:Group click="propagateClickEvent(event)">
<s:Button click="trace('button1')"/>
<s:Button click="trace('button2')"/>
<s:Button click="trace('button3')"/>
</s:Group>
<!-- trace output on click: "button3", "button1", "button2" -->
This is very generic. I'd recommend using something more specific than UIComponent, otherwise it would probably dispatch more clicks than are necessary. That all depends on what you're actually doing.
Also, the MXML example is rather poor, since, if you knew all your components beforehand you could definitely handle it differently. However, if you had a bunch of components whose positions were determined at runtime, this would approach would make more sense.
Related
I have an app which is using a vector object to create and display multiple dynamic items on the stage.
When I quit the game and then return to the inGame screen the objects are still visible. I have tried a number of ways to remove but nothing has worked.
I have null'ed the vectors but the objects still remain on the stage.
Basically I just need to clear the stage when the game is quit.
I am using:
var item = new Vector.<Item>();
to create.
The user quits the game by hitting the pause button and then a 'back to menu' button.
Any guidance is appreciated.
Once you perform an addChild() on something, that something receives another link to it, namely from display list. So you have to manually call removeChild() on each of your dynamic objects you store in your Vector, before actually dropping the vector.
var item:Vector.<Item>;
for (var i:int=item.length-1;i>=0;i--)
item[i].parent.removeChild(item[i]);
item=null;
If all of your DisplayObject's are in the item Vector and Item is an ancestor of DisplayObject then following loop executed when you are going to the main screen will remove them from it's container.
var child:Item;
while(item.length)
{
child = item.shift();
if(child)
{
//perform some cleaning functionality e.g. child.destroy();//if there is destroy method for disposing Item class
//
if(child.parent)
{
child.parent.removeChild(child);
}
child = null;
}
}
//
item = null;
I have a image in renderer class and if i click image i need to dispatch a event to the main mxml page in flex.
In Renderer Class:-
public function clickOnMoreImage(event:Event):void {
var moreImageEvent:Event = new Event("clickOnMoreImage");
var parent:EventDispatcher = parentDocument.parentDocument as EventDispatcher;
if (parent != null) {
parent.dispatchEvent(moreImageEvent);
}
}
<mx:Image id="imgStatus" buttonMode="true" useHandCursor="true" click="clickOnMoreImage(event)" />
parent class:-
this.addEventListener("clickOnMoreImage", showMoreImagePopUpData);
public function showMoreImagePopUpData(event:Event):void {
Alert.show("clicked on More Image");
}
This is not working. can i know any work around for this?
Don't use parent or parentDocument in this case. You cannot be sure of nesting level of your renderer against top application.
The "Flex" way is to use event bubbling.
Dispatch event by renderer itself (with bubbles set to true). It will "bubble" (in case the renderer is not a child of a popup) up to stage, so you can listen to it in any parent of the renderer (e.g. -> DataGroup -> List -> Application -> SystemManager -> Stage).
var moreImageEvent:Event = new Event("clickOnMoreImage", true); //2nd parameter "true"
dispatchEvent(moreImageEvent);
The direct children of a ViewStack receive a FlexEvent.SHOW once the respective child view is shown. Unfortunately, the event information is lost at deeper child levels so that the grandchildren of the ViewStack do not receive the SHOW event.
Which approach would be advisable to tap into the SHOW events received by a ViewStack's direct children from a grandchild or grand-grand-child?
Using parent.parent.[...].addEventListener(FlexEvent.SHOW, [...]) would be possible, of course, but is kind of ugly as it will break as soon as the number of hierarchy levels between the two components changes.
Are there other events that are better suited?
The background of my question is that I would like to reload the grandchildren's content when it becomes visible again.
You could listen for the ADDED_TO_STAGE event in the grandchild view and find out if you're part of a view stack. If yes, then just listen to the view stack child's show/hide events.
... addedToStage="setMeUpForViewStack()" ...
Which is:
private var vsView:UIComponent = null;
private function setMeUpForViewStack():void
{
if (vsView) {
vsView.removeEventListener("show", vsViewShowHideHandler);
vsView.removeEventListener("hide", vsViewShowHideHandler);
vsView = null;
}
var obj:DisplayObject = this;
while (obj.parent != obj) {
if (obj.parent is ViewStack) {
vsView = obj;
break;
}
obj = obj.parent;
}
if (vsView) {
vsView.addEventListener("show", vsViewShowHideHandler, false, 0, true);
vsView.addEventListener("hide", vsViewShowHideHandler, false, 0, true);
}
}
And in your vsViewShowHideHandler you would reload the content (if the view is visible).
Basically this frees your from worrying about the level of nesting. It doesn't work with multiple nested view stacks though.
In the REMOVED_FROM_STAGE event handler you would forget vsView.
Although burying down into the viewstack would work I agree this is ugly and potentially can lead to headaches as soon as something changes.
A different approach could be to implement a global event dispatcher.
By having a static event dispatcher in a class, the grandchildren could subscribe to events from the static dispatcher from anwywhere within the application.
When the parent hears the FlexEvent.Show the handler could dispatch a custom event using the global dispatcher?
I'm writing a Flex application using Flash Builder 4 and I'm having a bit of trouble with an AS3 object. Essentially, it is a BorderContainer, with a few buttons and images, and programming logic that determines how these interact with eachother and a database.
What I want to be able to do is configure the layout/style of the inner components using MXML and CSS. I can configure the inherited objects, but not ones that I have defined...
For example, in my MXML. I can modify the (inherited) borderstroke variable of myContainer like so;
<IE:MyContainer>
<IE:borderStroke>
<s:LinearGradientStroke weight="10" rotation="270">
<s:GradientEntry color="0xF655E5"/>
<s:GradientEntry color="0x6600CC"/>
</s:LinearGradientStroke>
</IE:borderStroke>
</IE:MyContainer>
However, I can't edit the nextButton variable (which is of type Button) like this;
<IE:MyContainer>
<IE:nextButton width="100" height="30" left="10%" bottom="10%"/>
</IE:MyContainer>
If I try, I get the compile error "Could not resolve to a component implementation".
What do I need to do to make this work?!
Thanks in advance,
Aidan
EDIT:
Here's the main method of MyContainer (actually named InvestigativeEnvironment).
The call to defineTestInvestigativeEnvironment() is what takes care of setting up the objects and action listeners and such. What I want to do is change the layout and appearance of these visual components in MXML (nextButton, prevButton, toolbox, displayArea). I want to be able to set their height, width, background, x, y, horizontalCenter, etc like I can to a button that I add to a container via MXML.
public class InvestigativeEnvironment extends BorderContainer
{
private var toolbox:Toolbox;
private var bodySystem:BodySystem;
public var nextButton:Button;
public var prevButton:Button;
private var displayArea:Group;
private var image:Image;
private var toolDisplayArea:Group;
public function InvestigativeEnvironment()
{
super();
//create 'Next' button and event listener
nextButton = new Button();
nextButton.addEventListener(MouseEvent.CLICK, nextViewAngle);
nextButton.label = "Next";
this.addElement(nextButton);
//create 'Prev' button and event listener
prevButton = new Button();
prevButton.addEventListener(MouseEvent.CLICK, prevViewAngle);
prevButton.label = "Prev";
this.addElement(prevButton);
//define investigative environment by creating models.
defineTestInvestigativeEnvironment();
//Instantiate the Group that contains the model image and tool overlays
displayArea=new Group();
//Instantiate the image that is used to display the model
image = new Image();
image.source=bodySystem.getImage();
image.horizontalCenter=0;
image.verticalCenter=0;
displayArea.addElement(image);
//add toolOverlayContainer to the display area ABOVE the model image
toolDisplayArea = new Group();
toolDisplayArea.verticalCenter=0;
toolDisplayArea.horizontalCenter=0;
displayArea.addElement(toolDisplayArea);
this.addElement(displayArea);
//add toolbox to display
toolbox = new Toolbox(toolDisplayArea);
toolbox.replaceTools(bodySystem.getToolGroup());
this.addElement(toolbox);
}
I can't understand what is your problem with editing button in particular, sorry for that. But I have a lot of notices about your InvestigativeEnvironment which code you've attached.
First, you haven't follow Flex components livecycle (see here or here). So in your code you should add and configure children in createChildren() method.
But anyway you can't use your container to add children both with MXML and from code. If your adding custom components code will be executed first MXML (in your implementation with adding them in constructor it is so) all the MXML tags just remove all your added content (anyway result will be unpredictable). In other case it will be very hard to control instance's order.
I can suggest you to declare your nextButton etc as skin parts and perform their positioning in skin. This way these internal controls will be a part of border and you can add MXML children without any problem. And you can configure them within partAdded() method.
It turns out that I wasn't quite asking the right question. I wanted to edit the components, but specifically the layout and color type attributes of them.
What I need to do is set the id of the components, and then target them using CSS.
For my nextButton, I add the ID like this;
nextButton.id="nextButton";
Then I can lay it out in the MXML file (or external stylesheet) like this;
<fx:Style>
#namespace s "library://ns.adobe.com/flex/spark";
#namespace IE "InvestigativeEnvironment.*";
IE|InvestigativeEnvironment s|Button {
chromeColor: #336666;
}
#nextButton {
bottom: 100;
right: 5;
}
</fx:Style>
I have a project that involves polling a hardware switch each update and converting its states to a custom events:
ToggleSwitchEvent.BackWardButtonDown
ToggleSwitchEvent.BackWardButtonUp
ToggleSwitchEvent.Click
etc ....
The hardware switch acts similar to a two button mouse. So I would like to be able to "inject" the custom ToggleSwitchEvent events into the Display List and have it act just as a MouseEvent does, bubbling up from the InteractionObject that the click intersects with. This would allow me to addEventListener to the display list just as I do with MouseEvents.
The trouble, as far as I understand it, is that I would need a reference to the leaf nodes of the DisplayList to insert my ToggleSwitchEvent manually. I would like to say that the switch was clicked at a certain position on screen, and have the DisplayList figure out what InteractionObject the click intersects and dispatch my ToggleSwitchEvent instead of a MouseEvent.
Is there a method to emulate the MouseEvent in AS3 but using a custom event?
i do not quite understand ... do you want a custom event to bubble, or to fire MouseEvents on your own?
any event will bubble as expected, if its bubbles property is set to true ...
if you dispatch mouse events manually, they will also bubble, and even their stageX and stageY is calculated appropriately ...
so good luck then ... ;)
edit:
ok, well the display list will not really take care of this for you ... actually, you have two options ... start at the stage, and make your way through it, always keeping under the mouse ... or, start at the object under the mouse, get its path in the tree, and find the right InteractiveObject in its parent chain ... the latter can be done using DisplayObjectContainer::getObjectsUnderPoint ... here's the code ...
public static function dispatchEventUnderMouse(event:Event, stage:Stage, point:Point):Boolean {
//return if stage is disabled
if (!stage.mouseEnabled) return false;
//find the topmost object
var cur:DisplayObject = stage.getObjectsUnderPoint(point).pop();
if (cur == null) cur = stage;
//build the path
var path:Array = [];
while (cur != null) {
path.push(cur);
cur = cur.parent;
}
//traverse the path from the root to find the right InteractiveObject
while (path.length > 0) {
cur = path.pop();
if (cur is InteractiveObject) {
if (!(cur as InteractiveObject).mouseEnabled) {
cur = cur.parent;
break;
}
if (cur is DisplayObjectContainer) {
if (!(cur as DisplayObjectContainer).mouseChildren) break;
}
}
else {
cur = cur.parent;
break;
}
}
return cur.dispatchEvent(event);
}
you might actually want to inject the right coordinates into the event, but i guess you'll figure that out ... ;)