I've converted a PowerPoint into Flash so each slide is a .swf. I've used a number of different converters and the problem here is the same.
What I'd like to do is trigger the .swf's animation (contents of slide appearing) programatically. I can click on the .swf with a mouse and the animation advances. I can automate this when the .swf's embedded in a web page simply by doing:
document.getElementById('myMovie').play();
Each time I execute that code, the swf advances to the next animation. However, I can't find a way to do this in flex. I've used then MovieClipSWFLoader and tried:
private function animate():void {
var simulatedClick1:MouseEvent = new MouseEvent(MouseEvent.CLICK);
var simulatedClick2:MouseEvent = new MouseEvent(MouseEvent.MOUSE_DOWN);
var simulatedClick3:MouseEvent = new MouseEvent(MouseEvent.MOUSE_UP);
frameNo++;
myMovie.gotoAndStop(frameNo);
myMovie.nextFrame();
myMovie.nextScene();
myMovie.play();
myMovie.dispatchEvent(simulatedClick1); // with clicks 1, 2, and 3
}
<s:MovieClipSWFLoader id="myMovie" source=""/>
<s:Button id="btnAnimate" click="animate()"/>
I've also tried loading as an Image component and using the simulated click too... no good. Can anyone tell me how I can do this?
The problem was that the .swf was created with Flash 8 and wasn't compatible with my version of Flex. I found a great solution in ForcibleLoader at https://gist.github.com/nsdevaraj/409902. It converts old .swf's to new ones and then the code worked as it should.
private var libMC:MovieClip = new MovieClip();
ur = new URLRequest(guide_url);
var loader:Loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, swfComplete);
var fLoader:ForcibleLoader = new ForcibleLoader(loader);
fLoader.load(ur);
swfMovie.addChild(loader);
private function swfComplete(event:Event):void{
libMC = event.currentTarget.content as MovieClip;
}
private function animate():void {
libMC.nextFrame();
<mx:UIComponent id="swfMovie"/>
This is a question about OpenLaszlo (or rather Flex?) internals:
I was able to load a full Flex application swf into a OpenLaszlo (trunk release, older releases failed). It works in Flash 10 as well as in 11. But OpenLaszlo seems to capture or
block certain mouse events. When I add the SWF into
YFilesSWF.content.sprite (YFilesSWF is extending the window class)
then most mouse actions work (e.g. Flex buttons), but some don't (some clickable items on a kindo f canvas). I observed further that when I add the SWF to
YFilesSWF.sprite (YFilesSWF is extending the view)
, then the SWF does not react to ANY mouse events anymore. That means window is somewhat better, but not good enough.
I'm using the
flash.display.Loader
class for loading the SWF in a normal way. This is the AS3 Loader class implementation which I use for loading the swf and which I include inside the OpenLaszlo app :
public class LoadSwf extends Sprite
{
public var externalSwfLoader:Loader = new Loader();
public var swfDisplayObject:DisplayObject;
public var swfComObject:Object;
public function LoadSwf(url:String,p:Sprite):void
{
// externalSwfLoader.mouseChildren = false;
// this.mouseChildren = false;
// p.mouseChildren = false;
// externalSwfLoader.mouseEnabled = false;
// this.mouseEnabled = false;
// p.mouseEnabled = false;
p.addChild(this);
externalSwfLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, createSwfObjects);
externalSwfLoader.load(new URLRequest(url));
}
public function createSwfObjects(evt:Event):void
{
var loaderInfo:LoaderInfo = evt.target as LoaderInfo;
swfDisplayObject = evt.target.content;
swfComObject = loaderInfo.content;
addChild(swfDisplayObject);
}
}
This is the OpenLaszlo code of how the class is used:
<class name="YFilesSWF" extends="window">
<passthrough>
import LoadSwf;
</passthrough>
<attribute name="loadSwf" />
<handler name="oninit"><![CDATA[
this.loadSwf = new LoadSwf("SimpleGraphEditor.swf", this.content.sprite);
]]></handler>
</class>
Does anybody know what and where OpenLaszlo is destroying some
of the Flex mouse events and how to prevent it?
Is there a yet better component than window that will
preserve the Flex mouse events?
What would be required to modify in the OL components source code?
Thanks!
i'm loading a Flex Application in my AIR App and i'm using the childSandboxBridge and parentSandboxBridge to communicate between those two. Works like a charm.
But i've tried to load a Flash Application (the main Class extends Sprite, not Application) and therefore i get a SecurityError when trying to set the childSandboxBridge on the loaderInfo object.
In the Flex app it's like this:
I'm casting the loaderInfo since the childSandboxBridge Property is only available in AIR.
loaderInfo = FlexGlobals.topLevelApplication.systemManager.loaderInfo;
try {
Object(loaderInfo).childSandboxBridge = this;
} catch(e:Error) {
...
}
In my Flash app it's like this:
loaderInfo = myMainObject.loaderInfo; // myMainObject is the same class as 'root'
try {
Object(loaderInfo).childSandboxBridge = this;
} catch(e:Error) {
...
}
In the below example i get the following SecurityError:
Error #3206: Caller app:/airapp.swf/[[DYNAMIC]]/1 cannot set LoaderInfo property childSandboxBridge.
The SecuritySandbox for both examples is 'application'.
Any ideas why it doesn't work with the Flash app?
Thanks in advance.
You may need to explicitly indicate in Flash code that you will allow it to be loaded into another VM. Check out the flash.system.Security class documentation for details:
http://www.adobe.com/livedocs/flash/9.0/ActionScriptLangRefV3/flash/system/Security.html
I have the following problem.
In my application I have several modules and each of them have components CollapsableTitleWindow (extends Panel). After opening the window it is added to the container which is in the main application (CollapsableTitleWindowContainer). In these windows you can open another window (and so on).
Now, what is the problem.
When I change (reload) any module and I want to open a new window (sub window) with the already loaded window I get this error:
TypeError: Error #1009: Cannot access a property or method of a null object reference.
at mx.containers::Panel/layoutChrome()[C:\autobuild\3.2.0\frameworks\projects\framework\src\mx\containers\Panel.as:1405]
at com::CollapsableTitleWindow/layoutChrome()[D:\Flex 3 Workspace\WesobCrm\src\com\CollapsableTitleWindow.as:216]
at mx.core::Container/updateDisplayList()[C:\autobuild\3.2.0\frameworks\projects\framework\src\mx\core\Container.as:2867] (...)
Indicates that the main applications have object Panel
Please help.
P.S. I found a similar problem on http://www.nabble.com/Flex-Module-issue-with-Panel-td20168053.html
ADDED:
I extendes the Panel class and do something like that:
override protected function layoutChrome(unscaledWidth:Number, unscaledHeight:Number):void
{
use namespace mx_internal;
if(!(mx_internal::titleBarBackground is TitleBackground)) {
mx_internal::titleBarBackground = new TitleBackground();
}
super.layoutChrome(unscaledWidth, unscaledHeight);
}
But now i had something like that:
Before
(source: ak.bx.pl)
After
(source: ak.bx.pl)
You can see that it loos style declaration.
I found a solution but it is bad pratice:
I add in my main application
public function getProductWindow():ProductWindow {
return new ProductWindow();
}
And change in the module:
From
var productWindow:ProductWindow = new ProductWindow();
To
var productWindow:ProductWindow = Application.application.getProductWindow();
If anyone have a better solution ?
I am building a complex Flex app, and now I am at the point where navigation becomes a problem. I make use of Viewstacks with a Menu Bar, but I am not sure how to clearly structure this.
Depending on the logged in User and chosen Company by the user, he can see different pages. For now I restricted this hiding the appropriate buttons in the Menu Bar. However, not just the menu bar, but also buttons/links from within the app should be able to navigate to each existing page.
When I am loading up an existing page, it needs some initialization (depending on the context it is loaded from). In addition, when a company is chosen, I need to load the status from the backend, and depending on this status a specific page might be visible.
Are there any guidelines how to tackle more complex navigation/site hierarchies in Flex?
Now I am having all my views in a viewstack in the Application, and refer to it with Application.application.appViews.selectedChild -> but that's obviously not best practice, since it violates encapsulation.
Was thinking of implementing some sort of State Machine, which takes care of all this, but not quite sure it this would make sense, or if there is any better way.
Thanks guys,
Martin
If it's really complex, you might want to consider breaking your application up into modules.
Also, Mate is a great Flex framework for handling complex communication and navigation. Mate's EventMaps help you centralize the communication and logic between components, modules, etc. And, it keeps you away from the dreaded Application.application references.
Even if you don't use a framework like Mate, you can avoid the Application.application references by having components dispatch custom events that bubble up to the top-level of your application. The top level of the application can listen and catch these events and act on them. I've found this to be a much more flexible approach. I avoid Application.application as much as possible!
If you have a complex menu bar that needs to enable / disable a lot of buttons or options based on many different logic conditions, the State pattern is a decent way to handle it. I built an enterprise-level app that had a "Word-like" button bar at the top...and there were so many different conditions that affected the states of the buttons that I had to centralize the logic in one place. At first I didn't use the State pattern and maintaining the code was a difficult chore. One day, I bit the bullet and re-factored all the conditional logic into a StateManager class. It definitely made life easier from there on out.
Again, you might want to consider using Custom Events to broadcast important events to your application. You can make these events bubble up to the Application level. Then, by adding event listeners at the Application level, you can capture and respond to these events and target components or modules from the Application level. This gives you a central location for handling events and "directing traffic". It also prevents the tight-coupling of the Application.application approach. (Which quickly becomes a nightmare as your application grows and scales!)
For example, your StateManager can contain the case statements for deciding which state your application needs to be in. Once the decision about the current state is determined, you would dispatch a custom StateEvent. (Which might have properties like StateEvent.STATE_CHANGED and StateEvent.CURRRENT_STATE) This event can bubble up to the Application level and be caught by a listener. The listener then calls a method to load / change the state.
Does that clarify it for you? If not, perhaps I can spend an hour or two putting together a little sample.
Let me know,
=Bryan=
I can give you the approach I used for some of your sub-questions, the problem of initializing a page at runtime and how to encapsulate navigation.
For page initialization, the issue I came across is that it's not always known once you navigate to a page whether certain elements should be shown, since it not-only depends on overall user permissions, but also permissions against the currently-selected data. And if the information needed to determine this must be loaded from the server, you cannot show the page as-is while loading the information. So we created a control called LoadingPanel, which is a container that can cover content with a loading indicator until additional information has been received. Here's a shortened version of the ActionScript:
[DefaultProperty("children")]
public class LoadingPanel extends ViewStack
{
public function LoadingPanel()
{
this.resizeToContent = false;
super();
}
public function get children():Array { return _children }
public function set children(value:Array):void { _children = value; }
public function get loadingImageStyle():String {
return _loadingImgStyle; }
public function set loadingImageStyle(value:String):void {
_loadingImgStyle = value;
if (_loadingIndic)
_loadingIndic.loadingImageStyle = value;
}
public function showLoadingIndicator():void
{
if (_loadingIndic)
{
super.selectedChild = _loadingIndic;
}
else
{
_pendingLoadingIndic = true;
var me:LoadingPanel = this;
var listener:Function = function(event:Event):void
{
if (me._pendingLoadingIndic)
me.showLoadingIndicator();
}
addEventListener(FlexEvent.CREATION_COMPLETE, listener);
}
}
public function hideLoadingIndicator():void
{
_pendingLoadingIndic = false;
if (_content)
{
super.selectedChild = _content;
}
else
{
var me:LoadingPanel = this;
var listener:Function = function(event:Event):void
{
me.hideLoadingIndicator();
}
addEventListener(FlexEvent.CREATION_COMPLETE, listener);
}
}
public function waitForEvent(target:EventDispatcher, event:String):void
{
_eventCount++;
showLoadingIndicator();
var me:LoadingPanel = this;
target.addEventListener(
event,
function(evt:Event):void
{
me._eventCount--;
if (!me._eventCount)
{
me.hideLoadingIndicator();
}
}
);
}
override public function addChild(child:DisplayObject):DisplayObject
{
var result:DisplayObject = child;
if (_content)
{
result = _content.addChild(child);
invalidateDisplayList();
}
else
{
if (!_children)
{
_children = [];
}
_children.push(child);
}
return result;
}
override protected function createChildren():void
{
super.createChildren();
if (!_content)
{
_content = new Box();
_content.percentWidth = 1.0;
_content.percentHeight = 1.0;
super.addChild(_content);
}
if (!_loadingIndic)
{
_loadingIndic = new LoadingIndicator();
_loadingIndic.percentWidth = 1.0;
_loadingIndic.percentHeight = 1.0;
_loadingIndic.loadingImageStyle = _loadingImgStyle;
super.addChild(_loadingIndic);
}
if (_children)
{
for each (var child:DisplayObject in _children)
{
_content.addChild(child);
}
}
}
private var _loadingImgStyle:String = "loadingIndicatorDark";
private var _loadingIndic:LoadingIndicator = null;
private var _content:Box = null;
private var _children:Array = null;
private var _pendingLoadingIndic:Boolean = false;
private var _eventCount:int = 0;
}
We typically used these by wrapping a LoadingPanel around content then calling the panel's waitForEvent method. Typically, the event we'd wait for is for a web service response to come in. The class also lets you wait on multiple events before it will show its children.
Another recommendation I would make for your project is that you look into deep linking in Flex. Our users appreciated being able to bookmark a resource/location in our complex Flex application as well as being able to hit refresh in their browser and return to the same "page" they were on. But implementing deep linking also helped me out for one of the problems you mentioned; how do you send the UI to a specific page in an encapsulated manner? The way we did it is by raising a bubbling navigation event containing a destination "URL." A top-level navigation "manager" then handled interpreting the URL and "sending" the user to the appropriate area.
Hopefully this will give you some ideas for some of the challenges you face.