I've trawled the net trying to find a solution, but everything seems to be mxml-centric. What I want is to dynamically create a series of Graphics objects each with a child BitmapImage. However, this doesn't seem to work:
var bmi:BitmapImage = new BitmapImage();
bmi.source="#Embed('custom-case.png')";
var gr:Graphic = new Graphic( );
gr.addElement( bmi );
gr.x = 50;
gr.y = 50;
this.addElement( gr );
Whereas, this does:
<s:Graphic x="250" y="250">
<s:BitmapImage source="#Embed('custom-case.png')">
</s:BitmapImage>
</s:Graphic>
Thanks in advance for any ideas.
Paul
it is quite different in AS3, you have to define a variable class type like shown below.
[Embed("custom-case.png")]
private var someImage:Class;
...
bmi.source=someImage;
To follow up Shruti's comment/question (I am unable to post comment since my current reputation insufficent):
The requirement for dynamic updating of images with mxml is the same as indicated with the original answer, which is that any images you might want to dynamically change to must be pre-embeded in your mxml:
[Embed(source="image.png")] private var theImage:Class;
which can be later used to update an image source as such:
<fx:Script>
<![CDATA[
[Embed(source="image.png")] private var theImage:Class;
private function updateImage():void {
image.source = theImage;
}
]]>
</fx:Script>
<s:BitmapImage id="image" source="#Embed('defaultImage.png')"/>
Related
I am trying to embed an image using an item renderer in my Flex project.
The image path however is a String passed in as a bound variable.
I am aware that
<s:BitmapImage source="#Embed('/../assets/image.png')" />
works because the image is embedded at runtime? (Could someone please clarify this)
How would i go about embedding my bound string, somewhat like this:
<s:BitmapImage source="#Embed('/../assets/{data.image}')" />
Many Thanks
I think a better choice if you'd like to embed the image but find it dynamically at runtime is: Embed all of the images it could be and then grab a reference to it dynamically. We generally use a pattern like this:
public class Icons {
[Embed(source="icons/icon1.png")]
public var icon1:Class;
[Embed(source="icons/icon2.png")]
public var icon2:Class;
}
Then you can dynamically grab the embedded images from your Icons instance at run-time.
Edit - self contained example - I'll use an item renderer since I think that's what you're doing.
Let's assume data.image can be 'plane' 'train' or 'automobile'
<s:ItemRenderer xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx">
<fx:Script>
<![CDATA[
[Embed(source="/assets/icons/plane.png")]
public var plane : Class;
[Embed(source="/assets/icons/train.png")]
public var train : Class;
[Embed(source="/assets/icons/automobile.png")]
public var automobile : Class;
]]>
</fx:Script>
<s:Image source="{this[data.image]}"/>
</s:ItemRenderer>
This is a really simple example and not the BEST way to implement, but you get the idea.
I like embed icons with css files. Then in ItemRenderer you can set css class and get image which you want.
css file or mxml css block:
.icons
{
bender: Embed(source="/assets/bender.png");
/* other icons */
}
In renderer, when you override set data method:
override public function set data(value:Object):void
{
super.data = value;
var iconName:String = data.image;
if ( iconName )
{
var cssDecl2:CSSStyleDeclaration = styleManager.getStyleDeclaration(".icons");
var IconClass:Class = cssDecl2.getStyle( iconName );
bmImage.source = new IconClass();
}
}
and bmImage as id s:BitmapImage:
<s:BitmapImage id="bmImage" />
May be this is very simple but right now I am unable to put a solution for it. Here is brief description of my problem:
I have a dictionary of clipart objects as:
clipartDict['cat'] = Cat; //Cat.mxml
clipartDict['dog'] = Dog; //Dog.mxml
Cat.mxml:
<s:Graphic>
<s:Path x="2.86723" y="-0.000106812" data="M3.45943 80.3419C3.06051 77.3605 0.002399>
</s:Path>
</s:Graphic>
MyView.mxml (relevant code):
<s:SkinnableDataContainer width="300" dataProvider="{clipArts}">
<s:layout>
<s:TileLayout requestedColumnCount="1"/>
</s:layout>
<s:itemRenderer>
<fx:Component>
<s:ItemRenderer>
<fx:Script>
<![CDATA[
import models.vo.ClipArtVO;
// (data as ClipArtVO).clipArtFileName represents the 'key' in dictionary.
// Now, how can I display the relevent clipart from dict based on the key
// this.addElement throws an Type Coercion error
]]>
</fx:Script>
</s:ItemRenderer>
</fx:Component>
</s:itemRenderer>
</s:SkinnableDataContainer>
Can anyone suggest me a solution or any idea to implement it in any different way?
Thanks.
What you've put in that Dictionary are Class references and not instances. You'll have to create an instance of the desired Graphic to add it to the displayList. So there are two methods to fix your issue.
Method 1
Put instances of the Graphics in the Dictionary (instead of Class references):
clipartDict['cat'] = new Cat();
clipartDict['dog'] = new Dog();
Then simply add it to the displayList:
var graphic:Graphic = clipartDict[(data as ClipArtVO).clipArtFileName];
addElement(graphic);
Method 2
Create an instance of the Class reference on the fly. We keep the Dictionary as it was:
clipartDict['cat'] = Cat;
clipartDict['dog'] = Dog;
and create an instance and add it to the displayList like this:
var Graphic:Class = clipartDict[(data as ClipArtVO).clipArtFileName];
addElement(new Graphic());
You should be able to wrap your cat.mxml file inside a SpriteVisualElement. It's a bit of tedium, but basically do this:
protected var sprite :SpriteVisualElement;
protected var class : Class;
protected var graphicInstance : Graphic;
protected function dataChangeMethod():void{
// (data as ClipArtVO).clipArtFileName represents the 'key' in dictionary.
// You told us about the key, but not the value. I'm assuming the value is an actual class
// and not an instance of the class
class = (data as ClipArtVO).clipArtFileName;
// create an instance of the class
graphicInstance = new class();
// create the new spriteVisualElement instance
sprite = new SpriteVisualElement();
// Add the graphic instance as a child to the spriteVisualElement
// you may need to do any sizing of the graphicInstance before adding it
sprite.addChild(graphicInstance);
// add the Sprite Visual Element as a child to your container
this.addElement(sprite);
}
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);
This is related to other question. But never mind it. I've fixed part of it.
I have a DataGrid, its data provider is a ArrayCollection, and i want it to parse all itens in it (Object Type) to a String.
For that I've done a "for each" loop, it manages to get the Object and its values, but if i have more that one object it only gets the last object, don't know why.
First i will show how these items are added to the ArrayCollection, that way you will understand the rest much easily.
In the Main Application i have the ArrayCollection:
<mx:ArrayCollection id="collection">
Then in other Component there is a Add Item Menu, and when you add a item:
private function fazerEncomenda():void
{
var novoitem:Object;
novoitem = new Object();
novoitem.id = "consumivel"+getProdInfo.lastResult.consumivel.id;
novoitem.tinteiroid = getProdInfo.lastResult.consumivel.id;
novoitem.label = getProdInfo.lastResult.consumivel.nome;
novoitem.ref = getProdInfo.lastResult.consumivel.refmarca;
novoitem.marca = getProdInfo.lastResult.consumivel.marca;
novoitem.genero = genero.text;
novoitem.quantidade = quantidade.text;
Application.application.collection.addItem(novoitem);
}
Then in another component the DataGrid as its dataProvider Binded to the ArrayCollection
<mx:DataGrid id="compras" x="0" y="0" width="556" dataProvider="{Application.application.collection}" editable="false">
<mx:columns>
<mx:DataGridColumn headerText="ID" dataField="tinteiroid" visible="false"/>
<mx:DataGridColumn headerText="Nome" dataField="label" width="120" />
<mx:DataGridColumn headerText="Ref" dataField="ref" width="100"/>
<mx:DataGridColumn headerText="Marca" dataField="marca" width="100"/>
<mx:DataGridColumn headerText="GĂ©nero" dataField="genero" width="155"/>
<mx:DataGridColumn headerText="Quantidade" dataField="quantidade" width="81"/>
</mx:columns>
</mx:DataGrid>
And when a Button is pressed the function to get all Objects and its values to an String.
And in this function its where it only gets the last item, in the ArrayCollection.
for each (novoitem in compras.dataProvider)
{
finish += "TinteiroID:"+novoitem.tinteiroid+"#TinteiroLABEL:"+novoitem.label+"#TinteiroREF:"+novoitem.ref+"#TinteiroMARCA:"+novoitem.marca+"#TinteiroGENERO:"+novoitem.genero+"#TinteiroQUANTIDADE:"+novoitem.quantidade+"#FIMPROD#";
trace(finish);
}
And of course the Vars used in the function:
private var finish:String;
private var novoitem:Object
As you see in the finish var i used += so it adds it self and the next object. Instead he adds null. And only one null event if there was 3 items before.
Don't know whats the problem with this loop.
Please Help. I'm loosing my mind here.
PS: Sorry for any bad English, its been 3 hours in this. And no progress.
EDIT: Missing Vars Declaration Added
An easier way to do all this (admittedly not with the labels you specified) is to just use ActionScript's built in ObjectUtil.toString method.
You would write something like this:
import mx.utils.ObjectUtil;
public function dumpObj():void {
myTextField.text = ObjectUtil.toString(obj);
}
This should pretty much print out every property of every multiple / nested object you have.
HOWEVER - you should make a fundamental change to your component if you want it to be reusable. You need a getter/setter for your collection. In the component, add this code:
[Bindable]
private var _myCollection:ArrayCollection;
public function set myCollection (data:ArrayCollection) : void {
_myCollection = data;
}
public function get myCollection () : ArrayCollection {
return _myCollection;
}
There are several other ways to do this - look it up if you need something different.
In your datagrid, use the private ArrayCollection variable like this:
<mx:DataGrid id="compras" x="0" y="0" width="556" dataProvider="{_myCollection}" editable="false">
<mx:columns>
<mx:DataGridColumn headerText="ID" dataField="tinteiroid" visible="false"/>
...
In the main application, you can populate your component like this:
<kgtm:myComponent x="0" y="20" myCollection="{queryDataAC}"
And you name your ArrayCollection like this:
<mx:ArrayCollection id="queryDataAC">
in your top level Application code, you define the kgtm namespace, so you can use your custom component, like so:
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:kgtm="com.kgtm.*"
Then put your component in the folder corresponding to this namespace definition.
This all leads to the final object print utility - which you define on the component, as it is the only thing that should know about how to print out it's data.
Define a public function, and get it to print out the private ArrayCollection data, using ObjectUtil or your own method.
public var getLastQueryOutput () : String {
private var output:String = "";
private var len:int = _myCollection.length;
for (var i:int = 0; i <len; i++) {
output = output +
"TinteiroID:"+_myCollection[i].tinteiroid+
"#TinteiroLABEL:"+_myCollection[i].label+
"#TinteiroREF:"+_myCollection[i].ref+
"#TinteiroMARCA:"+_myCollection[i].marca+
"#TinteiroGENERO:"+_myCollection[i].genero+
"#TinteiroQUANTIDADE:"+_myCollection[i].quantidade+
"#FIMPROD#";
}
trace(output);
}
Hopefully this will help. If you name the object correctly as you are putting it into the ArrayCollection, you can again just use ObjectUtil as I stated at the top.
Casp - Check out more of my (and my colleagues) blog entries here
Have you tried ".source" property of your array collection? I'm not sure if for-each loops work on ArrayCollection objects.
e.g.,
for each(novoitem in compras.dataProvider.source) { ... }
have you tried to just use a regular for loop
for (var i:int = 0; i < compras.dataProvider.length; i++) {
novoitem= compras.dataProvider[i];
trace(novoitem); // will output to the console during debugging.
...
}
in any case you shouldn't be looping on the dataProvider you sould be looping on the Application.application.collection
Guys i really want to thank you.
Thanks to your effort Glenn and AndrewB i did it. Once again thanks.
Now i will post the code so that someone with a similar problem can get some help.
Here goes the code to get the Objects and the Itens for each object inside a ArrayCollection.
[Bindable]
private var finish:String = "";
private var novoitem:Object
for (var i:int = 0; i <Application.application.collection.length; i++)
{
novoitem = compras.dataProvider[i];
finish = finish + "TinteiroID:"+novoitem.tinteiroid+"#TinteiroLABEL:"+novoitem.label+"#TinteiroREF:"+novoitem.ref+"#TinteiroMARCA:"+novoitem.marca+"#TinteiroGENERO:"+novoitem.genero+"#TinteiroQUANTIDADE:"+novoitem.quantidade+"#FIMPROD#";
trace(finish);
}
Thanks once again. I wanted to place both your awnsers as correct, but they aren't completely. So I've combined both to this code. And here it is.
I will be signing this answer as correct, but the credit its all yours. I wouldn't have it done if it weren't with you help.
EDIT
This is the code I've used however take a look at the code that "CaspNZ" as posted. Its probably a better and lighter approach in performance.
I'm wondering how to use a VideoDisplay object (defined in MXML) to display video streamed from FMS via a NetStream.
The Flex3 docs suggest this is possible:
The Video Display ... supports progressive download over HTTP, streaming from the Flash Media Server, and streaming from a Camera object.
However, later in the docs all I can see is an attachCamera() method. There doesn't appear to be an attachStream() method like the old Video object has.
It looks like you can play a fixed file served over HTML by using the source property, but I don't see anything about how to attach a NetStream.
The old Video object still seems to exist, though it's not based on UIComponent and doesn't appear to be usable in MXML.
I found this blog post that shows how to do it with a regular Video object, but I'd much prefer to use VideoDisplay (or something else that can be put directly in the MXML).
VideoDisplay is a wrapper on VideoPlayer, which in turn is a Video subclass. Unfortunately, the wrapper prevents you from attaching an existing NetStream to the Video object.
However, a reference to that component is held with in the mx_internal namespace, so the following should do the trick:
videoDisplay.mx_internal::videoPlayer.attachNetStream(incomingStream);
videoDisplay.mx_internal::videoPlayer.visible = true;
(you need to import the mx.core.mx_internal namespace)
Unfortunately you can attachNetStream() only on Video object. So you are doomed to use em if you want to get data from FMS.
By the way attachCamera() method publishes local camera video to the server so be careful ;)
it works.
mx:VideoDisplay live="true" autoPlay="true" source="rtmp://server.com/appname/streamname" />
that will give you live video through a videodisplay... problem is it won't use an existing netconnection object, it creates it's own... which is what I'm trying to find a work around for.
Here a link to example on how to use video:
http://blog.flexexamples.com/2008/03/01/displaying-a-video-in-flex-using-the-netconnection-netstream-and-video-classes/
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
layout="vertical"
verticalAlign="middle"
backgroundColor="white"
creationComplete="init();">
<mx:Script>
<![CDATA[
import mx.utils.ObjectUtil;
private var nc:NetConnection;
private var ns:NetStream;
private var video:Video;
private var meta:Object;
private function init():void {
var nsClient:Object = {};
nsClient.onMetaData = ns_onMetaData;
nsClient.onCuePoint = ns_onCuePoint;
nc = new NetConnection();
nc.connect(null);
ns = new NetStream(nc);
ns.play("http://www.helpexamples.com/flash/video/cuepoints.flv");
ns.client = nsClient;
video = new Video();
video.attachNetStream(ns);
uic.addChild(video);
}
private function ns_onMetaData(item:Object):void {
trace("meta");
meta = item;
// Resize Video object to same size as meta data.
video.width = item.width;
video.height = item.height;
// Resize UIComponent to same size as Video object.
uic.width = video.width;
uic.height = video.height;
panel.title = "framerate: " + item.framerate;
panel.visible = true;
trace(ObjectUtil.toString(item));
}
private function ns_onCuePoint(item:Object):void {
trace("cue");
}
]]>
</mx:Script>
<mx:Panel id="panel" visible="false">
<mx:UIComponent id="uic" />
<mx:ControlBar>
<mx:Button label="Play/Pause" click="ns.togglePause();" />
<mx:Button label="Rewind" click="ns.seek(0); ns.pause();" />
</mx:ControlBar>
</mx:Panel>
</mx:Application>
I've seen sample code where something like this works:
// Connect to the video stream in question.
var stream:NetStream = new NetStream( chatNC );
stream.addEventListener( NetStatusEvent.NET_STATUS, handleStreamStatus );
stream.addEventListener( IOErrorEvent.IO_ERROR, handleIOError );
// Build the video player on the UI.
var video:Video = new Video(246, 189);
var uiComp:UIComponent = new UIComponent();
uiComp.addChild( video );
uiComp.width = 246;
uiComp.height = 189;
stream.play( streamName );
video.attachNetStream( stream );
video.smoothing = true;
video.width = 246;
video.height = 189;
view.videoPlayerPanel.removeAllChildren();
view.videoPlayerPanel.addChild( uiComp );
But I can't actually get it to work myself. I'll post here later if I can figure it out.