Rather than deal with trying to figure out the passing of parameters to an embedded SWF, I went ahead and made 20 SWF's all compiled with different values. These SWF's are now inside my Flash Builder application.
There is a state for each SWF (using includeIn) so when the user clicks the button to switch states, the appropriate SWF is displayed.
The problem is that when a user views an SWF, it remains loaded and running in the background. I would like to unload the viewed SWF when the user leaves the state and then load it when it is needed again. If this is not possible, then I will settle for simply reloading the SWF when the state is entered, and just leave the other 19 running in the background.
I have the following:
<fx:Script><![CDATA[
private var flashMovie1:MovieClip;
private var flashMovie2:MovieClip;
private function initFirst():void{
flashMovie1 = dmp_first.content as MovieClip;
}
private function initSecond():void{
flashMovie2 = dmp_second.content as MovieClip;
}
protected function btnFirst_clickHandler():void
{
flashMovie2.Stop();
currentState='First';
flashMovie1.Play();
}
protected function btnSecond_clickHandler():void
{
flashMovie1.Stop();
currentState='Second';
flashMovie2.Play();
}
]]></fx:Script>
<mx:SWFLoader id="dmp_first" includeIn="First" source="assets/images/dmp_first.swf" complete="initFirst()"/>
<mx:SWFLoader id="dmp_second" includeIn="Second" source="assets/images/dmp_second.swf" complete="initSecond()"/>
Along with the above code not working at all with the Stop and Play, I still can't figure out how to force an SWF to reload. Any help would be greatly appreciated!
You can have only one swf at a time using a conatiner. Also you donlt have to use states :) for ex:
<?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">
<s:layout>
<s:VerticalLayout/>
</s:layout>
<fx:Script>
<![CDATA[
import mx.controls.SWFLoader;
private function onAppCreationComplete():void
{
showSWF(0);
}
private function onSwfComboChange():void
{
showSWF(cbxSwfData.selectedIndex);
}
private function showSWF(index:int):void
{
grpContainer.removeAllElements();
var swfLoader:SWFLoader = new SWFLoader();
grpContainer.addElement(swfLoader);
swfLoader.load("assets/swfs_files/swf"+index+".swf");
}
]]>
</fx:Script>
<s:ComboBox
id = "cbxSwfData"
dataProvider = "{['swf1','swf2','swf3']}"
change = "onSwfComboChange()"/>
<s:Group
id = "grpContainer"
width = "50%"
height = "50%"/>
</s:Application>
Related
I am having an issue with using the SplitViewNavigator component in flex 4.6 using Adobe Flash Builder.
[UPDATED]* * *
I am building a reddit app for the blackberry playbook tablet, and am making use of reddit's API. I have three mxml views: RedditReaderHomeView.mxml, redditFeed.mxml, and subredditList.mxml. In RedditReaderHomeView.mxml I have a splitViewNavigator. In left side of my SplitViewNavigator resides subredditList.mxml, and on the right side resides redditFeed.mxml. On initialization, redditFeed.mxml pulls in XML data to populate its list with reddit entries, and subredditList.mxml pulls in XML data which populates its list with subreddits(categories) to display. When a user clicks on of the subreddit entries on the left, the redditFeed.mxml on the right should update so that the data it pulls are entries from the subreddit category that was selected on the left. In other words, classic master/detail navigation. Category on the left, which opens entries of that category on the right.
Well, I have a function that passes the url of the selected subreddit over to redditFeed.mxml.
subredditList.mxml - here a subreddit is selected and its url is sent over to a function in redditFeed.mxml
public function list_clickHandler(event:IndexChangeEvent):void {
var RSSItem:Object = redditList.dataProvider.getItemAt(event.newIndex);
var thisItem:Item = RSSItem as Item;
rlink = thisItem.link;
var moddedLink:String = rlink.slice(1, int.MAX_VALUE)
var pushSub:redditFeed = new redditFeed();
pushSub.myList_creationCompleteHandler(moddedLink);
}
redditFeed.mxml - the url is used to grab data from that particular subreddit...
public function myList_creationCompleteHandler(url:String):void
{
getRedditFeedResult.token = redditFeedGrabber.getRedditFeed(url);
}
... though this data never seems to be displayed in the spark List component, even though I can see the call being made through the Network Monitor.
I've spent hours and hours trying to solve this. And I belive it has something to do with views being 'active' vs. 'deactivated', as when i tried placing this same call from within redditFeed.mxml, the call went through and the data in the list updated. I thought perhaps this was because the action was being performed within the same view that contains the list where the data is displayed. So it would be 'active' in this instance as the action is being performed in that view.
I appreciate any help anyone can offer. Also, dont hesitate to request clarification/more details from me. I reeeallly want to solve this.
Thanks!
RedditReaderHomeView.mxml - Here's the code you requested. Lemme know if you need more/something else.
<?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"
actionBarVisible="false" tabBarVisible="true" title="Reddit Reader by Domisy" creationPolicy="all"
actionBarVisible.landscape="true">
<fx:Script>
<![CDATA[
import com.adobe.fiber.core.model_public;
import mx.events.FlexEvent;
import views.redditFeed;
public var defUrl:String = new String("");
public function refreshRSS(event:Event):void
{
//***UPDATED CODE****
var refreshFunction:Object = redditFeedNav.getElementAt(0);
refreshFunction.refreshList();
//var refreshFunction:redditFeed = new redditFeed();
//refreshFunction.refreshList();
}
]]>
</fx:Script>
<fx:Declarations>
</fx:Declarations>
<s:states>
<s:State name="portrait"/>
<s:State name="landscape"/>
</s:states>
<s:actionContent>
<s:Button includeIn="landscape" click="navigator.pushView(profileView)"
icon="#Embed('assets/images/profileIcon.png')"/>
</s:actionContent>
<s:SplitViewNavigator width="100%" height="100%" id="splitViewNavigator" autoHideFirstViewNavigator="true">
<s:ViewNavigator id="redditListNav" firstView="views.subredditList" width="300" height="100%"/>
<s:ViewNavigator id="redditFeedNav" firstView="views.redditFeed" width="100%" height="100%">
<s:actionContent.landscape>
<s:Button id="refreshButtonlLandscape" icon="#Embed('assets/refresh160.png')" click="refreshRSS(event)" />
</s:actionContent.landscape>
<s:actionContent.portrait>
<s:Button id="refreshButton" icon="#Embed('assets/refresh160.png')" click="refreshRSS(event)" />
<s:Button id="navigatorButton" label="Search" click="splitViewNavigator.showFirstViewNavigatorInPopUp(navigatorButton)" />
</s:actionContent.portrait>
</s:ViewNavigator>
</s:SplitViewNavigator>
</s:View>
Okay I see what the problem is I think. You're creating a new instance of the redditFeed object instead of using the one instantiated in MXML. In your RedditReaderHomeView.mxml give the declaration of redditFeed an id, then in the function above use it like:
private var redditFeedId:RedditFeed; //Passed in from RedditReaderHome, bound to the id of the actual one in there
public function list_clickHandler(event:IndexChangeEvent):void {
var RSSItem:Object = redditList.dataProvider.getItemAt(event.newIndex);
var thisItem:Item = RSSItem as Item;
rlink = thisItem.link;
var moddedLink:String = rlink.slice(1, int.MAX_VALUE)
redditFeedId.myList_creationCompleteHandler(moddedLink);
}
Paste in RedditReaderHome if you end up needing any help on that.
Thanks for following up [ADDED]
public function refreshRSS(event : Event) : void
{
trace(redditFeedNav.getElementAt(0));
redditFeedNav.getElementAt(0).refreshList();
}
I have prepared a simplified test case for my question. It will run instantly in your Flash Builder if you put the 2 files below into a project.
I'm trying to display a List of strings and a confirmation checkbox in a popup:
In the real application I dispatch a custom event with the string selected in the list, but in the test code below I just call trace(str);
My problem: if I use click event, then the window closes, even if I click at a scrollbar (the !str check below doesn't help, when an item had been selected in previous use). And if I use change event, then the window doesn't close, when I click on the same item as the last time. And the itemClick event seems not to be present in spark.components.List anymore.
Any suggestions please on how to handle this probably frequent problem?
Writing a custom item renderer and having a click event handler for each item seems to be overkill for this case, because I have strings in the list.
Test.mxml: (please click myBtn few times - to see my problems with click and change)
<?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="400" minHeight="300">
<fx:Script>
<![CDATA[
import mx.managers.PopUpManager;
private var _popup:Popup = new Popup();
private function showPopup(event:MouseEvent):void {
PopUpManager.addPopUp(_popup, this, true);
PopUpManager.centerPopUp(_popup);
}
]]>
</fx:Script>
<s:Button id="myBtn" right="5" bottom="5"
label="Open window" click="showPopup(event)" />
</s:Application>
Popup.mxml:
<?xml version="1.0" encoding="utf-8"?>
<s:TitleWindow
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="240" height="240"
creationComplete="init(event)"
close="close()">
<fx:Script>
<![CDATA[
import mx.collections.ArrayList;
import mx.controls.Alert;
import mx.events.FlexEvent;
import mx.events.CloseEvent;
import mx.events.ItemClickEvent;
import mx.managers.PopUpManager;
private var myData:ArrayList = new ArrayList();
private function init(event:FlexEvent):void {
// XXX in the real app data is updated from server
myData.removeAll();
for (var i:uint = 1; i <= 10; i++)
myData.addItem('Item #' + i);
}
public function close(event:TimerEvent=null):void {
PopUpManager.removePopUp(this);
}
private function handleClick(event:MouseEvent):void {
var str:String = myList.selectedItem as String;
if (!str)
return;
if (myBox.selected) {
Alert.show(
'Select ' + str + '?',
null,
mx.controls.Alert.YES | mx.controls.Alert.NO,
null,
handleConfirm,
null,
mx.controls.Alert.NO
);
} else {
sendEvent();
}
}
private function handleConfirm(event:CloseEvent):void {
if (event.detail == mx.controls.Alert.YES)
sendEvent();
}
private function sendEvent():void {
close();
// XXX in the real app dispatchEvent() is called
trace('selected: ' + (myList.selectedItem as String));
}
]]>
</fx:Script>
<s:VGroup paddingLeft="20" paddingTop="20"
paddingRight="20" paddingBottom="20" gap="20"
width="100%" height="100%">
<s:List id="myList" dataProvider="{myData}"
click="handleClick(event)"
width="100%" height="100%" fontSize="24" />
<s:CheckBox id="myBox" label="Confirm" />
</s:VGroup>
</s:TitleWindow>
Also I wonder, why do I get the warning above:
Data binding will not be able to detect assignments to "myData".
The Spark List dispatches an 'IndexChangeEvent.CHANGE'. You can listen for this event to know when the selection in the List has changed.
<s:List id="myList" dataProvider="{myData}"
change="handleIndexChange()"
width="100%" height="100%" fontSize="24" />
That event is only dispatched whenever the selected index actually changes, which means that when you reopen the window a second time an item might still be selected and when you click on that one, no CHANGE event will be fired. To fix this just deselect the selection before you close the window:
public function close():void {
myList.selectedIndex = -1;
PopUpManager.removePopUp(this);
}
Also make sure to dispatch your event with the selected item before you close the window (and deselect it).
As for your question about the binding warning: you get that message because you didn't mark 'myData' to be bindable. To fix this just use the [Bindable] tag:
[Bindable]
private var myData:ArrayList = new ArrayList();
or skip the binding altogether if you don't need it and just assign the dataprovider to the list in ActionScript:
myList.dataProvider = myData;
I'd recommend two solutions if you absolutely wnat to display what item was selected. Otherwise, the solution provided by RIAStar would do the trick.
Listen to rendererAdd and rendererRemove events within your PopUp
As explained here, you can easily access to your list's renderers without interfering with its virtualLayout business.
Use a custom renderer
I know. But as long as you keep your code clean, itemRenderers won't blow up your application's memory. They're made to render huge amount of items without memory leaks.
In your Test.mxml, modify the codes like:
<fx:Script>
<![CDATA[
import mx.managers.PopUpManager;
private var _popup:Popup;
private function showPopup(event:MouseEvent):void {
_popup = new Popup();
PopUpManager.addPopUp(_popup, this, true);
PopUpManager.centerPopUp(_popup);
}
]]>
</fx:Script>
And in your Popup.mxml, I am not sure why you have the TimerEvent in the close function.
Also the trace won't be shown, as you are calling the close() function immediately after the alert's YES button has been clicked..
When I test my deployed app in a browser the popup window continues to be displayed even after it should be closed. Everything works as expected when debugged in Flash Builder 4.
Following is currently what's happening: the request is sent to my restful web service, which processes the request, (seemingly) the ResultEvent is called which in turn dispatches the profileEvt dynamic event that changes the view state. However, the popup window does not get closed and the applet gets 'stuck.'
Anyone know what could be the problem? Below are the flex applet web service event listeners/handlers:
webService.addEventListener(ResultEvent.RESULT, function(event:ResultEvent):void
{
var rawData:String = String(event.result);
var profileEvt:DynamicEvent = new DynamicEvent("profileSaved", true);
profileEvt.data = JSON.decode(rawData).profile;
dispatchEvent(profileEvt); // Dispatch profile saved event
_progressPopUp.closePopUp();
dispatchEvent(event); // Dispatch submit profile button clicked
});
webService.addEventListener(FaultEvent.FAULT, function(event:FaultEvent):void
{
Alert.show("Could not create profile; please try again later.\n" + event.message, "Status");
_progressPopUp.closePopUp();
});
var params:Object = {"profile" : profile};
try
{
_progressPopUp = PopUpManager.createPopUp(this, com.profs.ui.components.ProgressPopUp, true);
_progressPopUp.eventSource = webService; // Set source of progress events
webService.send(JSON.encode(params));
}
NOTE:
com.profs.ui.components.ProgressPopUp is a custom component; the code for it is below:
<?xml version="1.0" encoding="utf-8"?>
<mx:TitleWindow xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx" layout="absolute" width="300" height="200" showCloseButton="false" title="Status" creationComplete="init()">
<fx:Declarations></fx:Declarations>
<fx:Script>
<![CDATA[
import mx.managers.PopUpManager;
[Bindable] public var eventSource:Object;
private function init():void
{
PopUpManager.centerPopUp(this);
}
public function closePopUp():void
{
PopUpManager.removePopUp(this);
}
public function completionHandler(event:Event):void
{
closePopUp();
}
]]>
</fx:Script>
<mx:ProgressBar id="progressBar" indeterminate="true" mode="event" source="{eventSource}" complete="completionHandler(event)" verticalCenter="0" horizontalCenter="0"/>
</mx:TitleWindow>
I am not familiar with the com.profs.ui.components.progressPopUp component, but it is possible that the closePopUp() method has a bug in it. You could try to remove the ProgressPopUp directly using the PopUpManager method. For example instead of:
_progressPopUp.closePopUp();
try
PopUpManager.removePopUp(_progressPopUp);
I also don't know off the top of my head what the rules for closures are (i.e. at which point is the _progressPopUp variable copied into the ResultEvent.RESULT event handler. You could try moving that particular event handler below the line where you actually created the _progressPopUp instance.
The concept of this seems easy, but I'm having trouble getting it right and can't find anything to help me on this.
I have a panel I need to perform a drag and drop operation on, but I only want to perform that if the user mouses down on a particular area of the panel. I can add an Icon to the panel by doing this:
[Embed("/img/icon.png")]
[Bindable]
public var dragIcon:Class;
newPanel.titleIcon = dragIcon;
But what I really want to add is a box, which I can then add my listeners to for the drag and mouse down like I do on some canvases created in actionscript like so
var tempBox:Box = new Box;
tempBox.x=0;
tempBox.y=0;
tempBox.width = 20;
tempBox.height = 44;
tempBox.setStyle("horizontalAlign","center");
tempBox.setStyle("verticalAlign","middle");
tempBox.addEventListener(MouseEvent.ROLL_OVER,over);
tempBox.addEventListener(MouseEvent.ROLL_OUT,out);
tempBox.addEventListener(MouseEvent.MOUSE_DOWN,mouseDownAnswer);
var tempImg:Image = new Image();
tempImg.source = grabbableItem;
tempBox.addChild(tempImg);
myCanvas.addChild(tempBox);
So what do I need to do to use that tempBox and turn it into a class to be used as my panels titleIcon?
Edit 12/29/09:
So I came up with something where I'm extending the panel class (shown below) but all this is really doing is covering up the icon with something I can access publicly. I'm sure there's a better way out there right?
package custClass
{
import mx.containers.Box;
import mx.containers.Panel;
import mx.controls.Image;
public class DragPanel extends Panel
{
[Bindable] public var iconBox:Box = new Box();
[Embed("../img/doc_page.png")] [Bindable] public var grabbableItem:Class;
public function DragPanel()
{
super();
}
override protected function createChildren():void{
super.createChildren();
iconBox.x = 10
iconBox.y = 4
iconBox.width = 20;
iconBox.height = 20;
iconBox.setStyle("horizontalAlign","center");
iconBox.setStyle("verticalAlign","middle");
iconBox.setStyle("borderStyle","solid");
iconBox.setStyle("backgroundColor",0x000000);
var tempImg:Image = new Image();
tempImg.source = grabbableItem;
iconBox.addChild(tempImg);
this.rawChildren.addChild(iconBox);
}
}
}
EDIT 1/7/10 (or 16 according to my windows mobile phones text messages):
Using Chaims help from below here is my new answer.
Create a box mxml component like Chaim says but also add the following script block to it.
<mx:Script>
<![CDATA[
import mx.core.Application;
[Embed("/img/doc_page.png")]
[Bindable]
public var grabbableItem:Class;
public function init():void{
this.addEventListener(MouseEvent.MOUSE_DOWN,Application.application.mouseDownSection);
this.addEventListener(MouseEvent.ROLL_OVER,Application.application.over);
this.addEventListener(MouseEvent.ROLL_OUT,Application.application.out);
}
]]>
</mx:Script>
This adds in all the event listeners I want on the Box that will be used as my icon. Now just add the box as an Icon and it's good to go.
panel.titleIcon = DraggableBox;
I guess since it's a separate mxml component it is now a class, though I don't think I understand why.
The Panel expecting titleIcon property value to be a IFactory and create an instance by himself.
Make your box a component (lets name it DraggableBox.mxml):
<?xml version="1.0" encoding="utf-8"?>
<mx:Box xmlns:mx="http://www.adobe.com/2006/mxml"
x="0" y="0" width="20" height="44"
horizontalAlign="center" verticalAlign="middle">
<mx:Image source="{grabbableItem}"/>
</mx:Box>
And assign it to titleIcon:
<mx:Panel titleIcon="{DraggableBox}" >
...
</mx:Panel>
If you want do it in ActionScript use ClassFactory:
panel.titleIcon = new ClassFactory(DraggableBox);
I'm trying componentize one of the pieces of UI in an AIR application that I'm developing in Flex. In this example, I want to display file information on a single line (which has an icon, some text/link and the size).
My code looks like this (component is called FileDisplay):
<?xml version="1.0" encoding="utf-8"?>
<mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:Script>
<![CDATA[
public function set iconType(source:String):void {
this.ficon.source = source;
}
public function set fileName(name:String):void {
this.fname.htmlText = name;
}
public function set fileSize(size:String):void {
this.fsize.text = size;
}
]]>
</mx:Script>
<mx:Image id="ficon" />
<mx:Label id="fname" left="20" right="30" text="Filename" />
<mx:Label id="fsize" right="0" text="0 K" />
</mx:Canvas>
When I'm using this component in my main application, the actionscript looks like:
for each (var file:XML in result.files) {
var fd:FileDisplay = new FileDisplay();
fd.fileName = ''+file.name+'';
fd.iconType = getFileTypeIcon(file.name);
fd.fileSize = getFileSizeString(file.size);
this.file_list.addChild(fd);
}
However, when I do this, I get an error: Error #1009: Cannot access a property or method of a null object reference. This is because the child components of the FileDisplay are null (or at least they show up that way in the debugger).
Does anyone know if there's a way around this? Am I supposed to be waiting for events indicating the child components were created? Is there a more common pattern that solves this problem?
For now I can manually do everything in ActionScript in my main app (create a Canvas and add children to it) but I would appreciate any insight on how to separate the code more cleanly.
Bindable to the rescue:
<?xml version="1.0" encoding="utf-8"?>
<mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:Script>
<![CDATA[
[Bindable]
public var iconType:String;
[Bindable]
public var fileName:String = "Filename";
[Bindable]
public var fileSize:String = "0 K";
]]>
</mx:Script>
<mx:Image id="ficon" source="{iconType}"/>
<mx:Label id="fname" left="20" right="30" text="{fileName}" />
<mx:Label id="fsize" right="0" text="{fileSize}" />
</mx:Canvas>
the values will be automatically updated when the components are created.
The subcomponents haven't been loaded yet.
Read this: http://livedocs.adobe.com/flex/3/html/help.html?content=ascomponents_advanced_2.html#203434.
Then, when like me, you don't understand it (and it's not reliable), listen for the FlexEvent.CREATION_COMPLETE within FileDisplay, and apply your child component properties there.
Or better yet, create the three children programmatically in the "createChildren" function, and apply the settings there.
Both of these methods assume that you're setting filename, icontype, and filesize as local members before applying them to the children components, which you should be doing regardless.
What is the parent component that holds the FileDisplay component? If you're sure that the error is coming from the fact that the child components of FileDisplay aren't being instantiated then you might want to look at the creationPolicy attribute and make sure it's set to ContainerCreationPolicy.ALL on that parent component.
=Ryan
In addition to setting the CreationPolicy to all, you need to add the DisplayObject to the stage via addChild. The children of FileDisplay are not created until you add it is added to the stage. So do:
for each (var file:XML in result.files) {
var fd:FileDisplay = new FileDisplay();
this.file_list.addChild(fd);
fd.fileName = ''+file.name+'';
fd.iconType = getFileTypeIcon(file.name);
fd.fileSize = getFileSizeString(file.size);
}