Event listeners in SpriteVisualElement not responding - apache-flex

I have embedded Flashpunk game library to my Flex/Air project where some Flex components are rendered on top of the FP. Things show correctly and Flex components respond to mouse input. Flashpunk however doesn't. I embedded it like this:
<?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"
resize="windowedapplication1_resizeHandler(event)"
initialize="init();"
applicationComplete="complete();"
showStatusBar="false"
mouseEnabled="false"
xmlns:local="*">
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>
<s:SpriteVisualElement id="flashpunk" depth="-2" mouseEnabled="true" mouseChildren="true">
</s:SpriteVisualElement>
<mx:MenuBar id="myMenubar" width="832" itemClick="menuHandler(event);" labelField="#label" depth="0">
<fx:XMLList xmlns="">
<item label="File">
<item label="New" id="new" />
<item label="Open" id="open"/>
<item label="Save" id="save"/>
<item label="Save As" id="saveas"/>
<item label="Quit" id="quit"/>
</item>
<item label="Edit">
<item label="Undo" id="undo"/>
<item label="Redo" id="redo"/>
<item label="Preferences" id="preferences"/>
</item>
<item label="Level">
<item label="New Scene" id="newroom"/>
<item label="Properties" id="properties"/>
</item>
<item label="Objects">
<item label="Clickable" id="clickable"/>
<item label="Character" id="character"/>
<item label="Door" id="door"/>
<item label="Treasure" id="treasure"/>
</item>
</fx:XMLList>
</mx:MenuBar>
<fx:Script>
<![CDATA[
import basicui.Global;
import basicui.NewUI;
import flash.display.NativeWindow;
import flash.display.StageScaleMode;
import flash.filesystem.File;
import mx.collections.*;
import mx.controls.Alert;
import mx.core.IUIComponent;
import mx.core.InteractionMode;
import mx.core.mx_internal;
import mx.effects.effectClasses.FadeInstance;
import mx.events.FlexNativeWindowBoundsEvent;
import mx.events.MenuEvent;
import mx.events.ResizeEvent;
import mx.utils.object_proxy;
import net.flashpunk.FP;
import net.flashpunk.Screen;
import org.osmf.elements.F4MElement;
import spark.components.Application;
import spark.components.supportClasses.InteractionState;
public var Maini:Main = new Main;
public static var windowsize:Point = new Point(640, 480);
private var started:Boolean = false;
private function init():void
{
flashpunk.addChild(Maini);
Global.main = this;
}
private function complete():void
{
stage.scaleMode = StageScaleMode.NO_SCALE;
stage.align = StageAlign.TOP_LEFT;
//calculate system chrome
//nativeWindow.width = 640;
//nativeWindow.height = 480;
Global.chromew=nativeWindow.width-nativeWindow.stage.stageWidth;
Global.chromeh=nativeWindow.height-nativeWindow.stage.stageHeight;
Global.uibarh = 50;
//default windowsize
resizewindow(windowsize.x, windowsize.y);
started = true;
}
public function resizewindow(w:int, h:int):void
{
if (w < 640)
w = 640;
if (h < 480)
h = 480;
windowsize.x = w+Global.chromew;
windowsize.y = h+Global.chromeh+Global.uibarh;
nativeWindow.width = w+Global.chromew;
nativeWindow.height = h +Global.chromeh+Global.uibarh;
myMenubar.width = nativeWindow.width;
//resize flashpunk
FP.width = nativeWindow.width;
FP.height = nativeWindow.height;
FP.screen = new net.flashpunk.Screen;
FP.bounds.width = Number(FP.width);
FP.bounds.height = Number(FP.height);
}
private function menuHandler(evt:MenuEvent):void
{
// Don't open the Alert for a menu bar item that
// opens a popup submenu.
if (evt.item.#id == "new")
{
var a:NewUI = new NewUI;
a.begin(this);
addElement(a);
}
if (evt.item.#id == "open")
{
var filter:FileFilter = new FileFilter("Levels", "*.ael");
var file:File = new File;
file.browseForOpen("",[filter]);
file.addEventListener(Event.SELECT, fileselected);
}
if (evt.item.#id == "quit")
{
//close the program
//could ask if there is unsaved data...?
close();
}
/*Alert.show("Label: " + evt.item.#label + "\n" +
"Data: " + evt.item.#data, "Clicked menu item");
*/
}
private function fileselected(event:Event):void
{
//user selected a file:
File(event.currentTarget).addEventListener(Event.COMPLETE, fileloaded)
File(event.currentTarget).load();
}
private function fileloaded(event:Event):void
{
//user selected file is loaded:
Global.levelfile = File(event.currentTarget);
Global.levelXML = XML(File(event.currentTarget).data.toString());
//initialize level project
Global.editor.initlevel();
}
protected function windowedapplication1_resizeHandler(event:ResizeEvent):void
{
// TODO Auto-generated method stub
if (started)
{
windowsize.x = nativeWindow.width;
windowsize.y = nativeWindow.height
myMenubar.width = nativeWindow.width;
}
}
]]>
</fx:Script>
</s:WindowedApplication>
Flashpunk library has its own event listeners for input "Input.as". For some reason they seem to not run. I suppose that the flex components somehow hijacks these listeners or that the SpriteVisualElement doesn't allow or recognize them. How I can enable SpriteVisualElement event listeners? Is there setting I need to set or something? Note that neither mouse or keyboard input doesn't work.
EDIT: Added my main mxml code. Is there way to create a "catch all" listener which would then feed events to the FP's Input class?

Related

flex line chart with variable colored line

Ive got a rather simple need to create a line chart. The data that I would like to chart is based on a single daily datapoint. xml example of data:
<?xml version="1.0"?>
<dataset>
<data>
<date>01/14/2013</date>
<number>80.6</number>
<indication>G</indication>
</data>
<data>
<date>01/15/2013</date>
<number>74.6</number>
<indication>A</indication>
</data>
<data>
<date>01/21/2013</date>
<number>79.4</number>
<indication>G</indication>
</data>
<data>
<date>01/22/2013</date>
<number>67.7</number>
<indication>A</indication>
</data>
</dataset>
The trick is that I want to alter the plotted line color based on the value in indication.
In other words if my first point is on 01/14/2013 I want the color of the line between that point and the next to be based on the indication so with the example data above it would be amber. Then from the second point to the 3rd green and from the thirs to the fourth amber again.
I really like the amstock charts but they seem to be lacking this functionality.
Has anyone seen any components capable of this or have ideas how I could do it with default flex 4.6 components?
I have an idea, i hope it will help you.
You can process your dataset and form a new one from it, so that each two points represent a single datasource for one line chart segment.
Then you go through all your segments and add them separate to the chart.
You need two classes to save informations about "points" and "parts"
//Part.as
public class Part
{
public var col:Number;
public var punkts:ArrayCollection;
}
//Punkt.as
public class Punkt
{
public var date:String;
public var number:Number;
public function Punkt(date:String, number:Number)
{
this.date = date;
this.number = number;
}
}
//here is your application
<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
minWidth="955" minHeight="600"
creationComplete="init()">
<fx:Declarations>
<fx:Model id="myData">
<dataset>
<data>
<date>01/14/2013</date>
<number>80.6</number>
<indication>G</indication>
</data>
<data>
<date>01/15/2013</date>
<number>74.6</number>
<indication>A</indication>
</data>
<data>
<date>01/21/2013</date>
<number>79.4</number>
<indication>G</indication>
</data>
<data>
<date>01/22/2013</date>
<number>67.7</number>
<indication>G</indication>
</data>
<data>
<date>01/24/2013</date>
<number>47.7</number>
<indication>A</indication>
</data>
<data>
<date>01/25/2013</date>
<number>87.7</number>
<indication>G</indication>
</data>
</dataset>
</fx:Model>
</fx:Declarations>
<fx:Script>
<![CDATA[
import com.Part;
import com.Punkt;
import mx.charts.series.LineSeries;
import mx.collections.ArrayCollection;
import mx.graphics.SolidColorStroke;
import mx.graphics.Stroke;
import mx.utils.ObjectProxy;
[Bindable]private var xAxis:ArrayCollection = new ArrayCollection();
[Bindable]private var dp:ArrayCollection = new ArrayCollection();
private function init():void
{
var prevCol:Number = 0x000000;
var len:int = myData.data.length;
var item:ObjectProxy;
var i:int;
for (i = 0; i < len; i++)
{
item = myData.data[i];
xAxis.addItem(item.date);
}
for (i = 0; i < len - 1; i++)
{
item = myData.data[i];
var part:Part = new Part();
switch (item.indication)
{
case "A":
part.col = 0xe48701;
break;
case "G":
part.col = 0xa5bc4e;
break;
}
part.punkts = new ArrayCollection();
part.punkts.addItem(new Punkt(item.date, item.number));
item = myData.data[i + 1];
part.punkts.addItem(new Punkt(item.date, item.number));
dp.addItem(part);
}
var mySeries:Array=new Array();
for each (var part:Part in dp)
{
var lineSeries:LineSeries = new LineSeries();
lineSeries.dataProvider = part.punkts;
lineSeries.xField = "date";
lineSeries.yField = "number";
lineSeries.setStyle('lineStroke', new SolidColorStroke(part.col, 3, 1));
mySeries.push(lineSeries);
}
lc.series = mySeries;
}
]]>
</fx:Script>
<mx:LineChart id="lc" x="184" y="55">
<mx:horizontalAxis>
<mx:CategoryAxis dataProvider="{xAxis}"/>
</mx:horizontalAxis>
</mx:LineChart>
</s:Application>

could not be able to work with the SplitViewNavigator container

I want to make an App using SplitViewNavigator container which contains List of cities in left view and Detail about the city in right view, In right view there is a text input through I get Name of city and store in a SQLite Database, and that name should be added to list in left view from SQLite Database I got started with flowing code in Main.mxml:
<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
applicationDPI="160"
initialize="application1_initializeHandler(event)">
<fx:Script>
<![CDATA[
import model.DataModel;
import mx.events.FlexEvent;
import valueobject.CityValueObject;
import utillities.CityUtils;
public var sqlConnection:SQLConnection;
protected var statement:SQLStatement;
protected function application1_initializeHandler(event:FlexEvent):void
{
sqlConnection = new SQLConnection();
sqlConnection.open(File.applicationStorageDirectory.resolvePath("cityDB.db"), SQLMode.CREATE);
statement.sqlConnection = sqlConnection; // Here error occurs saying that Error #1009: Cannot access a property or method of a null object reference.
statement.text = "CREATE TABLE IF NOT EXISTS CITYNAME (" +
"id INTEGER PRIMARY KEY AUTOINCREMENT, " +
"nameofcity TEXT)";
statement.execute();
DataModel.getInstance().connection = sqlConnection;
CityUtils.getAllCities();
}
]]>
</fx:Script>
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>
<s:SplitViewNavigator id="svn" width="100%" height="100%">
<s:ViewNavigator width="30%" height="100%" id="list_of_cities" firstView="views.ListOfCities"/>
<s:ViewNavigator width="70%" height="100%" id="display_contents" firstView="views.DisplayContents"/>
</s:SplitViewNavigator>
My model.DataModel is an action script class:
package model
{
import flash.data.SQLConnection;
import mx.collections.ArrayCollection;
[Bindable]
public class DataModel
{
public var connection:SQLConnection;
public var cityList:ArrayCollection = new ArrayCollection();
public var logs:String="Application Logs........\n";
public static var _instance:DataModel;
public function DataModel()
{
}
public static function getInstance():DataModel
{
if(_instance == null)
{
_instance = new DataModel();
}
return _instance;
}
}
}
My valueobject.CityValueObject class is:
package valueobject
{
[Bindable]
public class CityValueObject
{
public var id:uint;
public var nameofcity:String;
}}
And My uttillities.CityUtils class is ::
package utillities
{
import flash.data.SQLResult;
import flash.data.SQLStatement;
import flash.display.Loader;
import flash.display.LoaderInfo;
import flash.events.Event;
import flash.net.URLRequest;
import flash.utils.ByteArray;
import model.DataModel;
import mx.collections.Sort;
import mx.collections.SortField;
import valueobject.CityValueObject;
public class CityUtils
{
public static function getAllCities():void
{
var contactListStatement:SQLStatement = new SQLStatement();
contactListStatement.sqlConnection = DataModel.getInstance().connection;
contactListStatement.text = "SELECT * FROM CITYNAME";
contactListStatement.execute();
var result:SQLResult = contactListStatement.getResult();
if( result.data!=null)
{
DataModel.getInstance().cityList.removeAll();
for(var count:uint=0;count<result.data.length;count++)
{
var cityVO:CityValueObject = new CityValueObject();
cityVO.id = result.data[count].id;
cityVO.nameofcity = result.data[count].city;
DataModel.getInstance().cityList.addItem(cityVO);
}
}
sortData();
}
public static function sortData():void
{
var dataSortField:SortField = new SortField();
dataSortField.name = "cityName";
dataSortField.numeric = false;
/* Create the Sort object and add the SortField object created earlier to the array of fields to sort on. */
var numericDataSort:Sort = new Sort();
numericDataSort.fields = [dataSortField];
/* Set the ArrayCollection object's sort property to our custom sort, and refresh the ArrayCollection. */
DataModel.getInstance().cityList.sort = numericDataSort;
DataModel.getInstance().cityList.refresh();
}
public static function updateLog(newLog:String):void
{
DataModel.getInstance().logs += new Date().time+" :-> "+newLog+"\n";
}
}
}
My left containing list of cities is :
<?xml version="1.0" encoding="utf-8"?>
<s:View xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark" title="Cities"
>
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>
<fx:Script>
<![CDATA[
import model.DataModel;
import mx.collections.ArrayCollection;
import mx.events.FlexEvent;
import mx.events.IndexChangedEvent;
import spark.components.SplitViewNavigator;
import spark.components.ViewNavigator;
import spark.transitions.ViewTransitionBase;
protected function myList_changeHandler():void {
// Create a reference to the SplitViewNavigator.
var splitNavigator:SplitViewNavigator = navigator.parentNavigator as SplitViewNavigator;
// Create a reference to the ViewNavigator for the Detail frame.
var detailNavigator:ViewNavigator = splitNavigator.getViewNavigatorAt(1) as ViewNavigator;
detailNavigator.transitionsEnabled = false;
// Change the view of the Detail frame based on the selected List item.
detailNavigator.pushView(DisplayContents, list_of_cities.selectedItem);
}
]]>
</fx:Script>
<s:VGroup width="100%" height="100%">
<s:List id="list_of_cities" height="100%" width="100%" change="myList_changeHandler();"
dataProvider="{DataModel.getInstance().cityList}" labelField="nameofcity">
</s:List>
</s:VGroup>
and in last my Display Detail about city is simply like this :
<?xml version="1.0" encoding="utf-8"?>
<s:View xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark" title="Detail About City"
>
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>
<s:actionContent>
<s:CalloutButton id="add_call_out_button" label="Add City" verticalPosition="after"
icon="#Embed('assets/add.png')" calloutDestructionPolicy="never">
<!-- layout the callout content here -->
<s:calloutLayout>
<s:VerticalLayout paddingTop="10" paddingBottom="10" paddingLeft="10" paddingRight="10" horizontalAlign="center" gap="5"/>
</s:calloutLayout>
<s:calloutContent>
<s:TextInput id="city_name_input" prompt="Enter City Name" text="Sydney"/>
<s:HGroup gap="40">
<s:Button id="add_city_name" label="Add City" width="150" height="40" click="add_city_name_clickHandler()"/>
<s:CheckBox id="preferred_cbox" label="Preferred" height="40" />
</s:HGroup>
</s:calloutContent>
</s:CalloutButton>
<s:Button id="remove_city_name" label="Remove" width="120" height="40"
click="remove_city_name_clickHandler()" icon="#Embed('assets/delete.png')"/>
</s:actionContent>
<s:Label id="nameSomeThing" text="{data.Description}"/>
<fx:Script>
<![CDATA[
import model.DataModel;
import spark.components.SplitViewNavigator;
import spark.components.ViewNavigator;
protected function add_city_name_clickHandler():void
{
var sqlStatement:SQLStatement = new SQLStatement();
sqlStatement.sqlConnection = DataModel.getInstance().connection;
sqlStatement.text = "INSERT INTO CITYNAME (nameofcity)" +
"VALUES(:nameofcity)";
sqlStatement.parameters[":nameofcity"] = city_name_input.text;
sqlStatement.execute();
var splitNavigator:SplitViewNavigator = navigator.parentNavigator as SplitViewNavigator;
// Create a reference to the ViewNavigator for the Detail frame.
var detailNavigator:ViewNavigator = splitNavigator.getViewNavigatorAt(1) as ViewNavigator;
detailNavigator.transitionsEnabled = false;
// Change the view of the Detail frame based on the selected List item.
detailNavigator.popToFirstView();
}
protected function remove_city_name_clickHandler():void
{
// TODO Auto-generated method stub
}
]]>
</fx:Script>
the above view(Display Detail) is still in development but at this stage I was Trying to add City Name to list of cities by getting name from city name input text input but at:
statement.sqlConnection = sqlConnection; // Here error occurs saying that Error #1009: Cannot access a property or method of a null object reference.
Iget that error and not be able to go ahead.
Can any one please give me the way to solve my this problem by my code givien above or suggest me an other way to meet my needs by this App Thanks in Advance...
As the error message says, statement is null.
I don't see any code that would initialize it.
You need:
statement = new SQLStament();
(And I don't see any reason why this variable would need to be outside the application1_initializeHandler function.)

Display an alert which cannot be closed by user, closes automatically on event

I have a Flex application which sends a query to a database when an user clicks a button. Since the query might be heavy, and can take up to a minute, I want to display an alert, which will close only after an event comes back from the database (user won't be able to close it himself). Is it possible in Flex? How do I do that?
I have functions sendQuery() and dataEventHandler(). I think I need to put code in sendQuery() to display the alert and in dataEventHandler() to close it after data comes from the DB, but how do I make the alert "unclosable" by the user?
The built in Flex Alert class will always have some type of close button.
However, there is no reason you can't create your own component; and then open and close it using the PopUpManager.
Following code may help you… (One of the solution...)
You can find I have made Solution 1 and Solution 2… You can use any one of it and third solution is to create your own Custom Component.
Please find below code…. You can use below logic to solve your problem..
Use Timer to check if data received or you can dispatch custom event and call updateAlertPosition function.
Hope it may help: -
<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx" minWidth="955" minHeight="600"
>
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>
<fx:Script>
<![CDATA[
import mx.controls.Alert;
import mx.events.CloseEvent;
import mx.managers.PopUpManager;
private var minuteTimer:Timer;
private var alert:Alert;
private var displayInitialText:String = "Data Not Received, Please wait....";
private var displayDataReveivedText:String = "Data Received...";
private function timerInit():void
{
//Logic to check if data Received.
minuteTimer = new Timer(3000);
minuteTimer.addEventListener(TimerEvent.TIMER, updateAlertPosition);
minuteTimer.start();
}
private function updateAlertPosition(event:Event = null):void {
minuteTimer.stop();
//Solution 1
//add your flag here if y you want to check if data is received or not
//if(Data Received)
alert.mx_internal::alertForm.mx_internal::buttons[0].enabled = true;
alert.mx_internal::alertForm.mx_internal::buttons[1].enabled = true;
alert.mx_internal::alertForm.mx_internal::textField.text = displayDataReveivedText;
//Solution 2
//alert.enabled = true;
//If you want to remove it automatically
//closeAutomatically();
}
private function closeAutomatically():void
{
PopUpManager.removePopUp(alert);
}
private function clickHandler():void
{
//Start Timer
timerInit();
//Solution 1
alert = Alert.show(displayInitialText, "Alert", Alert.OK|Alert.CANCEL,this,alertCloseHandler);
alert.mx_internal::alertForm.mx_internal::buttons[0].enabled = false;
alert.mx_internal::alertForm.mx_internal::buttons[1].enabled = false;
//Solution 2
//alert.enabled = false;
}
private function alertCloseHandler(event:CloseEvent):void
{
if(event.detail == Alert.CANCEL)
{
//Some Code on close
}
else
{
//Some Code on OK
}
}
]]>
</fx:Script>
<s:Button label="Show Alert" x="100" y="100" click="clickHandler()"/>
</s:Application>
make a 0-0.2 alpha shape what covers the whole application (probably you'll want to listen for resizeevents), and add a custom panel to the middle of it, with the message.
As an idea you can create a custom alert then:
Show Alert
Disable Application.
Hide Alert.
Enable Application.
An alert example:
<?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="400" height="300"
creationComplete="onCreationComplete(event)">
<s:Rect>
<s:fill>
<s:SolidColor color="0xFFFFFF"/>
</s:fill>
<s:stroke>
<s:SolidColorStroke />
</s:stroke>
</s:Rect>
<s:Label text="Please Wait..."/>
<fx:Script>
<![CDATA[
import mx.core.FlexGlobals;
import mx.events.FlexEvent;
import mx.managers.PopUpManager;
public static function show():void
{
PopUpManager.createPopUp(FlexGlobals.topLevelApplication);
}
public static function hide():void
{
PopUpManager.removePopUp(this);
FlexGlobals.topLevelApplication.enabled = true;
}
protected function onCreationComplete(event:FlexEvent):void
{
PopUpManager.centerPopUp(this);
FlexGlobals.topLevelApplication.enabled = false;
}
]]>
</fx:Script>
</s:Group>
Usage:
YourAlert.show();
YourAlert.hide();
#Alex, I used your code but modify it a bit, because there was some errors:
<?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"
creationComplete="creationCompleteHandler()" width="100%" height="100%">
<fx:Script>
<![CDATA[
import mx.core.FlexGlobals;
import mx.core.UIComponent;
import mx.managers.PopUpManager;
///////////////////////////////////////
//// public functions - my group is ImageViewer.mxml component
public static function show():ImageViewer {
return PopUpManager.createPopUp(FlexGlobals.topLevelApplication as DisplayObject, ImageViewer) as ImageViewer;
}
public function hide():void {
PopUpManager.removePopUp(this);
FlexGlobals.topLevelApplication.enabled = true;
}
////////////////////////////
//// component events
private function creationCompleteHandler():void {
PopUpManager.centerPopUp(this);
FlexGlobals.topLevelApplication.enabled = false;
}
]]>
</fx:Script>
</s:Group>
And call it like:
var imageviewer:ImageViewer = ImageViewer.show();
//imageviewer.imageURL = _value_dto.value;

dropdown following moving calloutbutton

Flex Actionscript question.
I have a calloutbutton that is moving on the screen.
When I open the dropdown (by a click) it stays at the same position on the screen (only the arrow is moving). It does not follow the calloutbutton's position
I would like this dropdown to follow the position of the calloutbutton when it has moved (as it does when one clicks on the button again when it has moved).
I have tried to dispatch a MouseEvent.CLICK. It does not work.
Tried also to open, then close again dropdown with actionscript sing closeDropDown() and openDropDown(). No change.
Thanks
Sample code (call init() from creationComplete) :
creationComplete="init();">
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>
<fx:Script>
<![CDATA[
import flash.events.MouseEvent;
import mx.events.DropdownEvent;
private function init():void {
var minuteTimer:Timer = new Timer(1*1000);
minuteTimer.addEventListener(TimerEvent.TIMER, updateCalloutPosition);
// starts the timer ticking
minuteTimer.start();
}
private function updateCalloutPosition(event:Event):void {
myButton.closeDropDown();
myButton.x = this.width * Math.random();
myButton.y = this.height * Math.random();
// myButton.openDropDown();
myButton.dispatchEvent(new MouseEvent(MouseEvent.CLICK));
}
protected function myButton_clickHandler(event:MouseEvent):void
{
if (myButton.isPopUp) {
myButton.closeDropDown();
}
else {
myButton.openDropDown();
}
}
]]>
</fx:Script>
<s:CalloutButton id="myButton" click="myButton_clickHandler(event)">
</s:CalloutButton>
I tried some workaround but openDuration and closeDuration is not working for DropDownList if it can be achieved than following code may help you. Hope following code may help you.
<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx" minWidth="955" minHeight="600"
creationComplete="init()">
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
<s:ArrayCollection id="ac">
<fx:String>AK</fx:String>
<fx:String>AL</fx:String>
<fx:String>AR</fx:String>
</s:ArrayCollection>
</fx:Declarations>
<fx:Style>
#namespace s "library://ns.adobe.com/flex/spark";
#namespace mx "library://ns.adobe.com/flex/halo";
s|DropDownList {
openDuration: 0;
closeDuration: 0;
}
</fx:Style>
<fx:Script>
<![CDATA[
import flash.events.MouseEvent;
import spark.components.supportClasses.DropDownListBase;
import spark.components.supportClasses.TextBase;
import spark.events.DropDownEvent;
import spark.skins.spark.DropDownListSkin;
import spark.utils.LabelUtil;
private var storeVisible:Boolean = false;
private function init():void {
var minuteTimer:Timer = new Timer(1*1000);
minuteTimer.addEventListener(TimerEvent.TIMER, updateCalloutPosition);
minuteTimer.start();
}
private function updateCalloutPosition(event:Event = null):void {
myButton.x = this.width * Math.random();
myButton.y = this.height * Math.random();
if(myButton.isDropDownOpen)
{
myButton.closeDropDown(false);
storeVisible = true;
}
}
private function updateComp():void
{
if(storeVisible)
{
myButton.openDropDown();
storeVisible = false;
}
}
]]>
</fx:Script>
<s:DropDownList id="myButton" selectedIndex="0" dataProvider="{ac}" updateComplete="updateComp()"/>
</s:Application>

Menu attached to mx.controls.PopUpButton never changes

I have prepared a very simple test case to demo my problem.
Please just place the 2 files below into a Flash Builder 4.6 project and they will run instantly.
My problem is that the menu attached to my (slightly modified) custom PopUpButton never changes - even though the underlying Array data provider is changed when you click one of the 3 buttons on the right side of it:
AuxButtonTest.mxml:
<?xml version="1.0" encoding="utf-8"?>
<s:Application
xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
xmlns:comps="*"
creationComplete="init()">
<fx:Script>
<![CDATA[
import mx.controls.Alert;
import mx.events.FlexEvent;
private const XML1:XML =
<pref>
<aux event="1">One</aux>
<aux event="2">Two</aux>
<aux event="3">Three</aux>
<aux event="4">Four</aux>
<aux event="5">Five</aux>
</pref>;
private const XML2:XML =
<pref>
<aux event="1">One</aux>
<aux event="2">Two</aux>
</pref>;
private const XML3:XML =
<pref>
<aux event="3">Three</aux>
</pref>;
public function init():void {
_auxBtn.update(XML1.aux);
}
//private function handleAuxChosen(event:PrefEvent):void {
//Alert.show(event.toString());
//}
]]>
</fx:Script>
<s:controlBarContent>
<!-- commented: aux_chosen="handleAuxChosen(event)" -->
<comps:AuxButton id="_auxBtn" />
<s:Button id="_btn1" label="XML 1" click="_auxBtn.update(XML1.aux);" />
<s:Button id="_btn2" label="XML 2" click="_auxBtn.update(XML2.aux);" />
<s:Button id="_btn3" label="XML 3" click="_auxBtn.update(XML3.aux);" />
</s:controlBarContent>
</s:Application>
AuxButton.mxml (my custom component based on PopUpButton):
<?xml version="1.0" encoding="utf-8"?>
<mx:PopUpButton
xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
popUp="{_menu}"
creationComplete="init(event)">
<fx:Metadata>
<!-- [Event(name="aux_chosen", type="PrefEvent")] -->
</fx:Metadata>
<fx:Script>
<![CDATA[
import mx.controls.Menu;
import mx.events.MenuEvent;
import mx.events.FlexEvent;
private var _str:String;
[Bindable]
private var _data:Array = new Array();
[Bindable]
private var _menu:Menu = new Menu();
private function init(event:FlexEvent):void {
_menu.dataProvider = _data;
_menu.addEventListener('itemClick', handleMenu);
addEventListener('click', handleClick);
}
public function update(xlist:XMLList):void {
_data.length = 0;
for each (var xml:XML in xlist) {
_data.push({label: xml, event: xml.#event});
}
label = _data[0].label;
_str = _data[0].event;
enabled = true;
}
private function handleMenu(event:MenuEvent):void {
label = event.label;
_str = event.item.event;
}
private function handleClick(event:MouseEvent):void {
enabled = false;
//dispatchEvent(new PrefEvent(PrefEvent.AUX_CHOSEN, _str));
}
]]>
</fx:Script>
</mx:PopUpButton>
I've commented my custom event out - it doesn't matter here.
Please just click at the buttons few times and then look at the menu - it always has 5 items and that is wrong.
UPDATE: Thank you for the answers - the following code works now for me. I still wonder why isn't ArrayList supported, but ArrayCollection works fine.
AuxButtonText.mxml:
<?xml version="1.0" encoding="utf-8"?>
<mx:PopUpButton
xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
popUp="{_menu}"
creationComplete="init(event)">
<fx:Script>
<![CDATA[
import mx.controls.*;
import mx.events.*;
import mx.collections.*;
private var _str:String;
[Bindable]
private var _data:ArrayCollection = new ArrayCollection();
//private var _data:ArrayList = new ArrayList();
[Bindable]
private var _menu:Menu = new Menu();
private function init(event:FlexEvent):void {
_menu.dataProvider = _data;
_menu.addEventListener('itemClick', handleMenu);
addEventListener('click', handleClick);
}
public function update(xlist:XMLList):void {
_data.removeAll();
if (xlist == null || xlist.length() == 0) {
enabled = false;
return;
}
for each (var xml:XML in xlist)
_data.addItem({label: xml, event: xml.#event});
label = _data.getItemAt(0).label;
_str = _data.getItemAt(0).event;
enabled = true;
}
private function handleMenu(event:MenuEvent):void {
label = event.label;
_str = event.item.event;
}
private function handleClick(event:MouseEvent):void {
enabled = false;
//dispatchEvent(new PrefEvent(PrefEvent.AUX_CHOSEN, _str));
}
]]>
</fx:Script>
</mx:PopUpButton>
AuxButton.mxml:
<?xml version="1.0" encoding="utf-8"?>
<s:Application
xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
xmlns:comps="*"
initialize="init()">
<fx:Script>
<![CDATA[
private const XML1:XML =
<pref>
<aux event="1">One</aux>
<aux event="2">Two</aux>
<aux event="3">Three</aux>
<aux event="4">Four</aux>
<aux event="5">Five</aux>
</pref>;
private const XML2:XML =
<pref>
<aux event="1">One</aux>
<aux event="2">Two</aux>
</pref>;
private const XML3:XML =
<pref>
<aux event="3">Three</aux>
</pref>;
private const XML4:XML =
<pref>
</pref>;
public function init():void {
_auxBtn.update(XML1.aux);
}
]]>
</fx:Script>
<s:controlBarContent>
<comps:AuxButton id="_auxBtn" />
<s:Button id="_btn1" label="XML 1" click="_auxBtn.update(XML1.aux);" />
<s:Button id="_btn2" label="XML 2" click="_auxBtn.update(XML2.aux);" />
<s:Button id="_btn3" label="XML 3" click="_auxBtn.update(XML3.aux);" />
<s:Button id="_btn4" label="LEN=0" click="_auxBtn.update(XML4.aux);" />
<s:Button id="_btn5" label="NULL" click="_auxBtn.update(null);" />
</s:controlBarContent>
</s:Application>
Arrays do not dispatch any events when their content changes. As a rule of thumb: Using [Bindable] on an Array and using it as some kind of dataProvider is usually a bad idea since adding/removing doesn't dispatch any events and therefore won't be handled by any component.
Instead of Array use an ArrayCollection which dispatches events of type CollectionEvent.COLLECTION_CHANGE whenever its content changes. Most components in the Flex SDK handle those events and update itself accordingly. So, the following code works since assigning a new source to the ArrayCollection dispatches a CollectionEvent which causes the Menu to update its menu items.
// you don't need [Bindable] on _data
private var _data:ArrayCollection = new ArrayCollection();
public function update(xlist:XMLList):void
{
var items:Array = [];
for each (var xml:XML in xlist)
{
items.push({label: xml, event: xml.#event});
}
_data.source = items;
if (items.length > 0)
{
label = items[0].label;
_str = items[0].event;
}
enabled = true;
}
My problem is that the menu attached to my (slightly modified) custom
PopUpButton never changes - even though the underlying Array data
provider is changed
This herein lies your problem. You have no code to change the menu's dataProvider or sync it with your private _data variable. The _data array does indeed change, but you have not coded any relation between _data and _menu.dataProvider.
The initial value is set in your init method:
private function init(event:FlexEvent):void {
_menu.dataProvider = _data;
_menu.addEventListener('itemClick', handleMenu);
addEventListener('click', handleClick);
}
But, it is never changed as data changes.
I'll do one little side note here to add that your init() method is called on creationComplete. Which means the component goes through it's full lifecycle once; then you change the dataProvider of the _menu, forcing it go through it's lifecycle again to redraw with the new dataProvider. This is usually undesirable. You should read up on the Flex Component Lifecycle to understand what events are fired and when. I would probably recommend setting the dataProvider on initialize, which is caleld after createChildren() is run, but before the children will be sized and measured.
Anyway, a quick fix is to just change the _menu.dataProvider on the fly in your update:
public function update(xlist:XMLList):void {
_data.length = 0;
for each (var xml:XML in xlist) {
_data.push({label: xml, event: xml.#event});
}
label = _data[0].label;
_str = _data[0].event;
enabled = true;
// new code
_menu.dataProvider = _data;
}

Resources