I'm having trouble with getting icons from resource bundle in Flex. Here's the scenario:
Directory structure looks like this:
-ResourceManagerTest
-resources
-icons
-icon1.png
-icon2.png
-icons.properties
-src
-MyButton.as
-ResourceManagerTest.mxml
In icons.properties I have:
CIRCLE_FILLED=Embed("icon1.png")
CIRCLE_CONTOUR=Embed("icon2.png")
I'd like to create ToggleButtonBar with buttons whose icons are pulled out from resource bundle.
Here's the source of programmatically created button:
package
{
import mx.resources.ResourceManager;
public class MyButton extends Object
{
public var icon:Class;
public function MyButton()
{
super();
icon = ResourceManager.getInstance().getClass("icons", "CIRCLE_FILLED");
}
}
}
And here is ResourceManagerTest where I define the ToggleButtonBar:
<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml"
layout="absolute"
creationComplete="onCreationComplete()">
<mx:Script>
<![CDATA[
[Bindable]
public var dataProvider:Array;
public function onCreationComplete():void {
dataProvider = new Array();
dataProvider.push(new MyButton());
dataProvider.push(new MyButton());
tgb.dataProvider = dataProvider;
}
]]>
</mx:Script>
<mx:ToggleButtonBar id="tgb"/>
Buttons do appear, however without any icons. What am I doing wrong?
Firstly it looks like you are not including the resource bundle in your build. You're probably going to need something like
<mx:Metadata>
[ResourceBundle("RegistrationForm")]
</mx:Metadata>
in the MXML or just
[ResourceBundle("RegistrationForm")]
at the top of your class
Once you've done that make sure you have the bundle... try adding just a string resource and see if you can get that. If you have the bundle and it still doesn't work have a play with different paths for you icons. They may not be relative to the resource (with out playing with it i can never remember what is relative to what).
The best resource I've found for information about how to setup ResourceBundle in Flex 3 is "Using Resources" in Adobe's livedocs.
[ResourceBundle("icons")]
In addition to including the resource bundle, you need to make sure you include the resource path at compile time. Read Adobe's docs for more information.
Related
this is a follow up question from this one, I don't want to keep going in the comments and preventing people from getting hard-earned reputation... :)
In my Cairngorm command class, to get it to compile I needed to tell it what myCanvas was, so I used this line:
var myCanvas : MyCanvas = new MyCanvas;
I'm guessing that's wrong, though, because although it compiles, if I try to do something like this:
if (myCanvas.subObject.value == 0) { ... }
it'll throw this error:
TypeError: Error #1009: Cannot access a property or method of a null object reference.
at com.foo.bar.command::MyCommand/execute()
as if the subObject doesn't exist. It looks like I might be getting a new instance of MyCanvas, not the instance I want from the main.mxml with an id of myCanvas. Am I right? How do I fix this?
Edit (10:59pm GMT+1): Okay, so it looks like I've been way too vague here. Here's my main.mxml:
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns:control="com.foo.bar.control.*"
xmlns:business="com.foo.bar.business.*"
xmlns:view="com.foo.bar.view.*"
applicationComplete="com.foo.bar.util.StartupUtil.init()"
horizontalScrollPolicy="off"
verticalScrollPolicy="off"
borderThickness="0"
paddingBottom="0"
paddingLeft="0"
paddingTop="0"
paddingRight="0"
>
<mx:Script>
<![CDATA[
import com.foo.bar.model.PlayerModelLocator;
[Bindable]
private var model : PlayerModelLocator = PlayerModelLocator.getInstance();
]]>
</mx:Script>
<!-- ========================================================================== -->
<!-- the ServiceLocator where we specify the remote services -->
<business:Services id="services" />
<!-- the FrontController, containing Commands specific to this application -->
<control:PlayerController id="controller" />
<!-- ========================================================================== -->
<mx:Style source="assets/main.css" />
<view:MyCanvas id="myCanvas" />
</mx:Application>
And here's my com/foo/bar/command/MyCommand.as:
package com.foo.bar.command {
/* add to controller
addCommand( MyEvent.EVENT_CHANGE_VOLUME, ChangeVolumeCommand );
*/
import flash.net.SharedObject;
import com.adobe.cairngorm.control.CairngormEvent;
import com.adobe.cairngorm.commands.ICommand;
import com.foo.bar.model.PlayerModelLocator;
import com.foo.bar.event.MyEvent;
import com.foo.bar.view.*;
public class ChangeVolumeCommand implements ICommand {
public function execute(event:CairngormEvent):void {
var model : PlayerModelLocator = PlayerModelLocator.getInstance();
var myEvent : MyEvent = MyEvent(event);
var myCanvas : MyCanvas = new MyCanvas();
var so:SharedObject = SharedObject.getLocal("fixie.video");
if (myCanvas.subObject.value == 0) {
trace("subobject value is 0");
}
}
}
}
Basically, I want to get a handle on the object with ID myCanvas in main.mxml using the myCanvas object in MyCommand.as
Could be a couple of things. First, you need parentheses on your class name after the "new" statement: new MyCanvas(); Second, you may be trying to access your sub component before the component lifecycle is ready for you to do so. (It's hard to tell from the code you posted since there isn't enough context.)
What is the scope of your myCanvas variable? Is it inside a method somewhere? You will need to make it public or give it getter/setter to be able to access it.
You may also be trying to reference it before it has been added to its parent, using the addChild() method.
There really isn't enough code in your examples to determine the problem, but these things should give you somewhere to start looking.
1 way is to add eventListener when your myCanvas will be ready after CreationComplete and to do all your stuff
and the second is:
define your subObject as in myCanvas class so you'll be able to access it on Init Stage of your component.
regards
Eugene
p.s. all of the time everybody want to get answer without well formed sample of their problem, its terrible!!
I'm an experienced AS developer, but this is something very basic about flex that I can't figure out. In the class below, I'm not sure why the function imageLoaded would not be executed when the image loads a url. Is there a race condition happening here? The application is completely loaded by the time this object is created and setPicture is called. How can I get the Event.COMPLETE event to fire properly in this case?
The line and Log.info functions are conveniences for me, and they definitely work.
Here's my MXML definition:
<?xml version="1.0" encoding="utf-8"?>
<photo:PhotoClass xmlns:mx="http://www.adobe.com/2006/mxml" xmlns:photo="com.xxx.widgets.photo.*">
<mx:Image id="image"/>
</photo:PhotoClass>
Here's my actionscript:
public class PhotoClass extends Box {
public var image : Image;
public function PhotoClass() {
image = new Image();
image.addEventListener(Event.COMPLETE, imageLoaded);
}
private function line(txt : *) : void {
Log.info(txt, "PhotoClass");
}
public function setPicture(url : String) : void {
line("setPicture: " + url);
image.source = url;
}
public function imageLoaded(event : Event) : void {
line("image loaded");
}
}
eventListeners won't register unless the sprite is attached to the application.
Can you give a reference? Because I believe the issue is something else.
When you declare an mxml file with PhotoClass as its root tag, you're extending the PhotoClass. The <mx:Image> tag in the mxml defines a public variable of type Image in the mxml class; and you already have a public var image:Image; in the super class. This will create a conflict - I'd expect a compile time error.
My guess is that since constructor is called first, it assigns an Image object to the public variable image. When the mxml is executed and the children are created, it assigns a new Image object to the public variable image (instead of correctly throwing an error). Now, the event listener was added to the Image instance created in the constructor, but by the time you set image.source in setProperty, it is a different object altogether. That is why it works when you move the event listener to setPicture - there you're assigning it to the new object.
Change the id of the mxml Image tag to something else and it will work even if the event listener was added from the constructor. Change the name of public var image to something else and you'll get a compile time error in PhotoClass.
Btw, what does the Image tag in the mxml do - is that for showing a different image? In that case you must change its id.
<!-- MyCanvas.mxml -->
<mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:Label id="lbl"/>
</mx:Canvas>
<!-- the main application class -->
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" xmlns:local="*"
creationComplete="onCreate()" layout="vertical">
<local:MyCanvas id="canvas1"/>
<mx:Script>
<![CDATA[
public var canvas2:MyCanvas;
public function onCreate():void
{
this.canvas1.lbl.text = "MyCanvas_1";
canvas2 = new MyCanvas();
this.addChild(canvas2);
canvas2.addEventListener(FlexEvent.CREATION_COMPLETE, onCanvas2Created);
}
public function onCanvas2Created(e:FlexEvent):void
{
this.canvas2.lbl.text = "MyCanvas_2";
}
]]>
</mx:Script>
</mx:Application>
I figured it out...
eventListeners won't register unless the sprite is attached to the application. The eventListener in this case was being added in the constructor, before the sprite was added to its parent class. I moved image.addEventListener to setPicture and it worked.
This could potentially be a dumb question so apologies in advance if it is.
I'm wondering if theres an equivilant of Interfaces in MXML?
Everytime I feel the need to use an interface I always wind up making an actionscript and not an MXML file because I don't know if / how you can.
For example I was going to have a component based on vbox. I have 4 different implementions of the same thing so I decided to use an interface. But instead of making a single MXML interface and implementing it I've created an interface in as3. I've implemented this interface in 4 different classes.
I then have made 4 different vbox containers each with one of the different implementations in the script tag.
Does this sound like a reasonable approach or am I going against the grain here?
EDIT -- adding examples
The interface
package components.content.contents
{
public interface IContent
{
function init():void;
function doSearch():void
function setSearchTerm(term:String):void
}
}
Implementation (1 of 4)
package components.content.contents
{
public class ClipContent extends AbstractContent implements IContent
{
public function ClipContent()
{
}
public function init():void
{
}
public function doSearch():void
{
}
public function setSearchTerm(term:String):void
{
}
}
}
MXML File (1 of 4)
<?xml version="1.0" encoding="utf-8"?>
<mx:VBox xmlns:mx="http://www.adobe.com/2006/mxml" width="400" height="300">
<mx:Script>
<![CDATA[
// ClipContent Container
import components.content.contents.ClipContent;
public var content:ClipContent= new ClipContent()
public function dostuff():void
{
content.init()
content.doSearch()
}
]]>
</mx:Script>
</mx:VBox>
You can use interfaces with MXML components this way:
// YourClass.mxml
<mx:HBox implements="IYourInterface">
is an MXML equivalent of
// YourClass.as
class YourClass extends HBox implements IYourInterface
But you still need to create the interface (in this example IYourInterface) in Actionscript.
MXML can implement an interface, like Robert Bak said, but it cannot define an interface.
No! MXML is a Declarative language for layout and positioning. By definition it needs an implementation. Interfaces are the definition of an API without an implementation.
It sounds like you're doing things exactly how I would. It is perfectly acceptable for an MXML Component to implement an interface. And it is perfectly acceptable to have multiple components implement the same interface to achieve different results.
For the sake of completeness, an MXML Component can implement an interface just like an ActionScript component ca:
<mx:myComp implements="com.myClass.Interface">
You are correct, there is no way to implement a true interface using MXML(edit: I stand corrected, you can use the "implements" keyword as described in the other answers.) Another approach to consider is to use a "code behind" actionscript file for each of your 4 MXML files:
MXML file (MyFancyVBox.mxml):
<?xml version="1.0" encoding="utf-8"?>
<MyFancyVBoxCode>
...
</MyFancyVBoxCode>
AS file (MyFancyVBoxCode.as):
package com.something.whatever
{
import com.something.another.IFancyInterface;
public class MyFancyVBoxCode implements IFancyInterface
{
...
}
}
The downside is that it doubles the number of source files.
Firstly, I agree with Wade that Code behind may help you.
Secondly, I am thinking do you need the interface in you case. In your question, you want "4 different implementions of the same thing". How about using the "state" in the mxml. It may solve you problems.
I have a MXML with a form, and inside it, two TextInputs. I hate having any piece of code inside the MXML file (I come from a JavaScript formation) so I use a
mx:Script source="external.as"
tag to include any code used in any MXML file. The problem is that if I have this code on the external.as file:
private function populateFromForm():void{
var vo:ValidObject= new ValidObject();
vo.market = marketInput.text;
vo.segment = segmentInput.text;
vo.priceLow = priceLowInput.text;
vo.priceHigh = priceHighInput.text;
}
Where marketInput, segmentInput, priceLowInput and priceHighInput are TextInputs defined in the MXML file. When I try to complile I get a 1120: Access to undefined property XXXXX
I have tried adding this lines prior to the function:
public var marketInput:TextInput;
public var segmentInput:TextInput;
public var priceLowInput:TextInput;
public var priceHighInput:TextInput;
But instead I get a 1151:A conflict exists with definition XXXX in namespace internal which makes perfect sense.
Is there a way to do this without having to pass all the input references to the function as parameters of it?
You need to create a reference to an instance of the TextInputs' parent container, and then use that reference to accsess the TextInputs and their properties. I think we need some clarification on your file structure. How are you creating the instance of the parent container? I'm thinking this is what you need to do:
MyForm.mxml:
<?xml version="1.0" encoding="utf-8"?>
<mx:VBox xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:TextInput id="marketInput" />
<mx:TextInput id="segmentInput" />
<mx:TextInput id="priceLowInput" />
<mx:TextInput id="priceHighInput" />
</mx:VBox>
SaveVOContainer.as:
package
{
public class SaveVoContainer extends Container
{
private var myForm:MyForm = new MyForm();
public function SaveVOContainer
{
this.addChild(myForm);
}
private function populateFromForm():void{
var vo:ValidObject= new ValidObject();
vo.market = myForm.marketInput.text;
vo.segment = myForm.segmentInput.text;
vo.priceLow = myForm.priceLowInput.text;
vo.priceHigh = myForm.priceHighInput.text;
}
}
}
Doing a "code-behind" is painful in Flex. There is no concept of partial classes or the flexibility of prototypal inheritance as in Javascript. Google for "code-behind in flex" for many resources.
I think it's better you get used to the idea of embedding code in mxml. Use script tags avoiding inline code as much as possible. If you have to write a lot of code within MXML, perhaps you may want to re-factor the code into multiple custom components. Bonus points if they are reusable.
The canonical way to do code-behind in Flex is via inheritance. Here's a good explanation from the docs: http://learn.adobe.com/wiki/display/Flex/Code+Behind. In a nutshell:
Declare an ActionScript class to use as your base class.
Set the base class as the root container in your MXML file.
For any controls declared in your MXML file, you have to redeclare them as public members of the base class using the exact same name (exactly as you are doing above for your script block with source tag, only it works :-)
So, your ActionScript file:
package mypackage
{
import mx.controls.TextInput;
public class myClass extends WindowedApplication
{
public var marketInput:TextInput;
private function populateFromForm():void{
/* As above */
}
}
}
And the corresponding MXML file:
<?xml version="1.0" encoding="utf-8"?>
<custom:myClass xmlns:custom="mypackage.*"
xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx">
<mx:TextInput id="marketInput"/>
</custom:myClass>
Et cetera for your other TextInput controls. And now your populateFromForm function should work.
It is kind of heinous to have to redeclare the same entities twice, but it's not quite the bag of hurt the earlier respondent made it out to be (although it's possible this changed in Flex 4 to make it less painful than it was).
import this in .AS:
import mx.core.Application;
in the .AS use this:
mx.core.Application.application.component.property = value;
mx.core.Application.application.myText.text = 'test';
do you have a script tag in your mxml file that points to your ActionScript file?
<mx:Script source='includes/foo.as' />
Is it possible to use the Flex Framework and Components, without using MXML? I know ActionScript pretty decently, and don't feel like messing around with some new XML language just to get some simple UI in there. Can anyone provide an example consisting of an .as file which can be compiled (ideally via FlashDevelop, though just telling how to do it with the Flex SDK is ok too) and uses the Flex Framework? For example, just showing a Flex button that pops open an Alert would be perfect.
If it's not possible, can someone provide a minimal MXML file which will bootstrap a custom AS class which then has access to the Flex SDK?
I did a simple bootstrap similar to Borek (see below). I would love to get rid of the mxml file, but if I don't have it, I don't get any of the standard themes that come with Flex (haloclassic.swc, etc). Does anybody know how to do what Theo suggests and still have the standard themes applied?
Here's my simplified bootstrapping method:
main.mxml
<?xml version="1.0" encoding="utf-8"?>
<custom:ApplicationClass xmlns:custom="components.*"/>
ApplicationClass.as
package components {
import mx.core.Application;
import mx.events.FlexEvent;
import flash.events.MouseEvent;
import mx.controls.Alert;
import mx.controls.Button;
public class ApplicationClass extends Application {
public function ApplicationClass () {
addEventListener (FlexEvent.CREATION_COMPLETE, handleComplete);
}
private function handleComplete( event : FlexEvent ) : void {
var button : Button = new Button();
button.label = "My favorite button";
button.styleName="halo"
button.addEventListener(MouseEvent.CLICK, handleClick);
addChild( button );
}
private function handleClick(e:MouseEvent):void {
Alert.show("You clicked on the button!", "Clickity");
}
}
}
Here are the necessary updates to use it with Flex 4:
main.mxml
<?xml version="1.0" encoding="utf-8"?>
<local:MyApplication xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:local="components.*" />
MyApplication.as
package components {
import flash.events.MouseEvent;
import mx.controls.Alert;
import mx.events.FlexEvent;
import spark.components.Application;
import spark.components.Button;
public class MyApplication extends Application {
public function MyApplication() {
addEventListener(FlexEvent.CREATION_COMPLETE, creationHandler);
}
private function creationHandler(e:FlexEvent):void {
var button : Button = new Button();
button.label = "My favorite button";
button.styleName="halo"
button.addEventListener(MouseEvent.CLICK, handleClick);
addElement( button );
}
private function handleClick(e:MouseEvent):void {
Alert.show("You clicked it!", "Clickity!");
}
}
}
This is a very simple app that does only the basic bootstrapping in MXML. This is the MXML:
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" creationComplete="onCreationComplete()">
<mx:Script source="Script.as" />
</mx:Application>
This is the Script.as:
import mx.controls.Button;
import flash.events.MouseEvent;
import mx.controls.Alert;
import mx.core.Application;
private function onCreationComplete() : void {
var button : Button = new Button();
button.label = "Click me";
button.addEventListener(MouseEvent.CLICK, function(e : MouseEvent) : void {
Alert.show("Clicked");
});
Application.application.addChild(button);
}
NOTE: The below answer will not actually work unless you initialize the Flex library first. There is a lot of code involved to do that. See the comments below, or other answers for more details.
The main class doesn't even have to be in MXML, just create a class that inherits from mx.core.Application (which is what an MXML class with a <mx:Application> root node is compiled as anyway):
package {
import mx.core.Application;
public class MyFancyApplication extends Application {
// do whatever you want here
}
}
Also, any ActionScript code compiled with the mxmlc compiler -- or even the Flash CS3 authoring tool -- can use the Flex classes, it's just a matter of making them available in the classpath (refering to the framework SWC when using mxmlc or pointing to a folder containing the source when using either). Unless the document class inherits from mx.core.Application you might run in to some trouble, though, since some things in the framework assume that this is the case.
Yes, you just need to include the flex swc in your classpath. You can find flex.swc in the flex sdk in frameoworks/lib/flex.swc
edit: One more thing: if you're using Flex Builder you can simply create a new ActionScript project, which will essentially do the same as above.