ItemRenderer Switching URLLoader Images - apache-flex

I don't even know how to explain this behavior but I'll try. I am loading images from an external url that requires basic auth so I am using URLLoader to load the image from a unique ID. The ID gets passed to the itemrenderer which then proceeds to load the image. But the images switch around on their own when I scroll. If I load more than 7 images or so it starts repeating images....
Youtube video of error:
http://www.youtube.com/watch?v=ZYoqlS14gWQ
Relevant code:
<s:ItemRenderer name="RandomItemRenderer" creationComplete="init();"
xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
autoDrawBackground="false">
<s:states>
<s:State name="normal" />
<s:State name="hovered" />
<s:State name="selected" />
</s:states>
<fx:Script>
<![CDATA[
import flash.net.URLLoader;
import flash.net.URLLoaderDataFormat;
import flash.net.URLRequest;
import flash.net.URLRequestHeader;
import flash.net.URLRequestMethod;
import mx.utils.ObjectProxy;
import customclasses.Settings;
[Bindable] private var coverArtImage:Image;
private var myCoverArtLoader:URLLoader;
[Bindable] private var coverArtSource:String;
private function init():void {
get_coverArt();
}
private function get_coverArt(): void {
if (!data.coverArt) {
set_nullCoverArt();
} else {
var requestString:String = "/rest/getCoverArt.view?v=1.5.0&c=AirSub&id=" + data.coverArt;
var requestURL:String = Settings.ServerURL + requestString;
myCoverArtLoader = new URLLoader();
var myRequest:URLRequest = new URLRequest();
var authHeader:URLRequestHeader = new URLRequestHeader();
authHeader.name = 'Authorization';
authHeader.value = 'Basic ' + Settings.EncryptedCreds();
myRequest.requestHeaders.push(authHeader);
myRequest.url = requestURL;
myRequest.method = URLRequestMethod.GET;
myCoverArtLoader.dataFormat = URLLoaderDataFormat.BINARY;
myCoverArtLoader.addEventListener(Event.COMPLETE, set_coverArt);
myCoverArtLoader.addEventListener(IOErrorEvent.IO_ERROR, set_failedCoverArt);
myCoverArtLoader.load(myRequest);
}
}
private function set_coverArt(evt:Event) : void {
coverArtImage = new Image();
coverArtImage.source = myCoverArtLoader.data;
myCoverArtLoader.removeEventListener(Event.COMPLETE, set_coverArt);
}
private function set_nullCoverArt() : void {
coverArtImage = new Image();
coverArtImage.source = "assets/nullCoverArt.jpg";
}
private function set_failedCoverArt() : void {
coverArtImage = new Image();
coverArtImage.source = "assets/nullCoverArt.jpg";
myCoverArtLoader.addEventListener(IOErrorEvent.IO_ERROR, set_nullCoverArt);
}
]]>
</fx:Script>
<s:Image source.normal="assets/coverOutline.png" source.selected="assets/coverOutlineYellow.png" source.hovered="assets/coverOutlineYellow.png"
height="226" width="226" />
<s:VGroup top="4.5" bottom="5" width="200" horizontalAlign="center" letterSpacing="10"
paddingBottom="5" paddingTop="9" verticalAlign="middle" x="13.5">
<s:Image id="ui_imgCoverArt" width="200" height="200" source="{coverArtImage.source}"/>
<s:Label text="{data.title}" width="160" styleName="RandomList" />
</s:VGroup>

ItemRenderers are reusable and cached, i.e. there are only limited count created in List to fill its area (rowCount +- couple). And when you scroll, new renderers are not instantiated, instead the one renderer that was scrolled out goes up or down and is filled with new data.
That's why you can not rely on creationComplete event, it will be fired only once for each instance of renderer.
The solution is to override data setter and build there the behaviour needed:
override public function set data(value:Object):void
{
super.data = value;
get_coverArt();
}
Useful link: How flex itemRenderer works ? (their life cycle)

Related

Flex, how to exchange user names and peer IDs between all connected users

Hello I'm building a chat using Flex. The problem is how to do that new user get the list off all users online and added to the lists of all users. I try to put this information in DataGrid through dataProvider "callerns":
<s:DataGrid x="10" y="125" width="238" height="125" alternatingRowColors="[ #67676767, #555555]"
borderVisible="true" chromeColor="#555555" color="#CCCCCC"
contentBackgroundColor="#555555" dataProvider="{callerns}" fontWeight="bold"
requestedRowCount="4" rollOverColor="#08700D" selectionColor="#08700D"
symbolColor="#CCCCCC">
<s:columns>
<s:ArrayList>
<s:GridColumn dataField="name" headerText="USER ONLINE"></s:GridColumn>
<s:GridColumn dataField="peerID" headerText="USER ID"></s:GridColumn>
</s:ArrayList>
</s:columns>
</s:DataGrid>
Here is the part of code:
[Bindable]
private var callerns:ArrayCollection = new ArrayCollection();
...........
private function netStatusEvent(event:NetStatusEvent):void{
trace('NetConnection status event (1): ' + event.info.code);
//writeText(event.info.code);
switch(event.info.code){
case "NetConnection.Connect.Success":
log('Connected (NearID: '+nc.nearID+')', 'debug');
log('Connection sucsessful');
MyPeerID = nc.nearID;
txtFingerprint.text = MyPeerID;
initSendNetStream();
callerns.addItem({peerID: MyPeerID, name: myName});
setupGroup();
break;
case "NetGroup.Posting.Notify":
receiveMessage(event.info.message);
log('Message posted');
break;
case "NetGroup.Connect.Success":
log('Net Group connection sucsessful');
connected = true;
break;
case 'NetStream.Connect.Success':
log('Peer Connected (FarID: '+event.info.stream.farID+')', 'debug');
break;
case "NetGroup.Neighbor.Connect":
log('New user connected');
break;
}
}
private function initSendNetStream():void{
trace("initSendStream");
sendStream = new NetStream(nc,NetStream.DIRECT_CONNECTIONS);
sendStream.addEventListener(NetStatusEvent.NET_STATUS, netStatusEvent);
var clientObject:Object = new Object();
clientObject.onPeerConnect = function(callerns:NetStream):Boolean{
initRecvStream(callerns.farID);
callerns.send('onPeerNameUpdate', MyPeerID, myName);
return true;
}
sendStream.client = clientObject;
sendStream.publish('video');
log('Net Stream publish start');
}
private function initRecvStream(peerID:String):void {
//log('initRecvStream', 'debug');
var stream:NetStream = new NetStream(nc, peerID);
stream.addEventListener(NetStatusEvent.NET_STATUS, netStatusEvent);
stream.play('video');
var client:Object = new Object();
client.onPeerNameUpdate = onPeerNameUpdate;
stream.client = client;
var peer:Object = new Object();
peer.stream = stream;
recvStreams[peerID] = peer;
}
private function onPeerNameUpdate(peerID:String, name:String):void {
//log('onPeerNameUpdate received: '+peerID+':'+name, 'debug');
//log(name+' connected to your channel', 'debug');
callerns.addItem({peerID: peerID, name: name});
}
Unfortunately this doesn't work and new users aren't added to the grid. Could you please help me to solve this problem?
Removed original post seems I'm wrong, this works:
<?xml version="1.0" encoding="utf-8"?>
<s:Group creationComplete="group1_creationCompleteHandler(event)"
height="100%"
width="100%"
xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:mx="library://ns.adobe.com/flex/mx"
xmlns:s="library://ns.adobe.com/flex/spark">
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>
<fx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
import mx.events.FlexEvent;
[Bindable]
private var callerns : ArrayCollection;
protected function group1_creationCompleteHandler(event : FlexEvent) : void
{
callerns = new ArrayCollection();
}
protected function addItemClickHandler(event : MouseEvent) : void
{
callerns.addItem({peerID: "somePeerID", name: "someName"});
}
]]>
</fx:Script>
<s:layout>
<s:VerticalLayout/>
</s:layout>
<s:Button click="addItemClickHandler(event)"
label="Add Item"/>
<s:Button click="propertyChangeClickHandler(event)"
label="Dispatch Property Change"/>
<s:DataGrid alternatingRowColors="[ #67676767, #555555]"
borderVisible="true"
chromeColor="#555555"
color="#CCCCCC"
contentBackgroundColor="#555555"
dataProvider="{callerns}"
fontWeight="bold"
height="125"
requestedRowCount="4"
rollOverColor="#08700D"
selectionColor="#08700D"
symbolColor="#CCCCCC"
width="238"
x="10"
y="125">
<s:columns>
<s:ArrayList>
<s:GridColumn dataField="name"
headerText="USER ONLINE">
</s:GridColumn>
<s:GridColumn dataField="peerID"
headerText="USER ID">
</s:GridColumn>
</s:ArrayList>
</s:columns>
</s:DataGrid>
</s:Group>
So I was originally wrong, hitting the button does cause an update to the grid. It would have been extremely helpful if you had posted a link to the original tutorial you were building off of, I found it and got it working with spark just fine. I'm assuming you got this part figured out already but here's the basic chat client:
<?xml version="1.0" encoding="utf-8"?>
<s:Application minHeight="600"
minWidth="955"
xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:mx="library://ns.adobe.com/flex/mx"
xmlns:s="library://ns.adobe.com/flex/spark">
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>
<fx:Script>
<![CDATA[
private const SERVER_ADDRESS : String = "rtmfp://p2p.rtmfp.net/";
private const DEVELOPER_KEY: String = PUT_YOUR_DEV_KEY_HERE;
private var nc : NetConnection;
private var myPeerID : String;
private var farPeerID : String;
// streams
private var sendStream : NetStream;
private var recvStream : NetStream;
private function initConnection() : void
{
if (txtFingerprint.text)
{
farPeerID = txtFingerprint.text;
}
nc = new NetConnection();
nc.addEventListener(NetStatusEvent.NET_STATUS, ncStatus);
nc.connect(SERVER_ADDRESS + DEVELOPER_KEY);
}
private function ncStatus(event : NetStatusEvent) : void
{
trace(event.info.code);
myPeerID = nc.nearID;
txtFingerprint.text = myPeerID;
}
private function initSendStream() : void
{
trace("initSendStream");
sendStream = new NetStream(nc, NetStream.DIRECT_CONNECTIONS);
sendStream.addEventListener(NetStatusEvent.NET_STATUS, netStatusHandler);
sendStream.publish("media");
var sendStreamClient : Object = new Object();
sendStreamClient.onPeerConnect = function(callerns : NetStream) : Boolean
{
farPeerID = callerns.farID;
trace("onPeerConnect " + farPeerID);
return true;
}
sendStream.client = sendStreamClient;
}
private function initRecvStream() : void
{
recvStream = new NetStream(nc, farPeerID);
recvStream.addEventListener(NetStatusEvent.NET_STATUS, netStatusHandler);
recvStream.play("media");
recvStream.client = this;
}
public function receiveSomeData(str : String) : void
{
txtReceiveData.text = str;
}
private function sendSomeData() : void
{
sendStream.send("receiveSomeData", txtSendData.text);
}
private function netStatusHandler(event : NetStatusEvent) : void
{
trace(event.info.code);
}
]]>
</fx:Script>
<mx:TextInput id="txtFingerprint"
width="391"
x="10"
y="10"/>
<mx:Button click="initConnection()"
label="Connect"
x="409"
y="10"/>
<mx:TextInput id="txtSendData"
x="10"
y="40"/>
<mx:TextInput id="txtReceiveData"
width="251"
x="10"
y="70"/>
<mx:Button click="sendSomeData()"
label="Send data"
x="178"
y="40"/>
<mx:Button click="initSendStream()"
label="initSendStream"
x="10"
y="100"/>
<mx:Button click="initRecvStream();"
label="initReceiveStream"
x="132"
y="100"/>
<mx:Text height="122"
text="Hint: First running Flash app - click Connect to get Fingerprint PeerID. Copy and paste this PeerID to second running Flash app to the same field and click Connect. Then initSendStream and initReceiveStream on both of them and finally you can write some text and click Send data."
width="391"
x="10"
y="130"/>
</s:Application>
Get a developer key here:
http://www.adobe.com/cfusion/entitlement/index.cfm?e=cirrus
I'm not sure what's going wrong but as the commenter above says you need to include how exactly it's breaking, that is what happens when you debug (be sure to install the debug flash player for whatever browser your using, beware chrome manages it's own plugin by default) what do you see in the console, do you get any errors etc. The tutorial app works fine for me, having this deal with two way communication and maintaining a list of all connected peers seems to be the goal correct? I also don't understand the question about NetGroup, there's more on P2P that covers that here though:
http://www.adobe.com/devnet/flashmediaserver/articles/p2p_rtmfp_groups.html
I wish you would have included more information about where you started and where you're headed with all this though (and code, I always want more code :)

Flex - parent not executing child's function

Many thanks to www.Flextras.com who has been helping me with this question the last couple of days, and I almost have it. I have a main.mxml, child.mxml and headermenu.mxml. I click a button on the headermenu that dispatches an event up to the main.mxml which then executes a method in child.mxml. I know this works because I put an AlertDialog in the function I'm calling inside of the child.mxml. The child.mxml contains a drawingArea object that has an erase(). When I call this directly from child.xml it executes, however if I put drawingArea.erase() inside of the function being called by child's parent (main.mxml) nothing happens.
Here is my code:
Telestrator.mxml
<?xml version="1.0" encoding="utf-8"?>
<s:MobileApplication xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark" firstView="views.TelestratorHome" creationComplete="creationCompleteHandler(event)">
<fx:Script>
<![CDATA[
import mx.effects.Fade;
import mx.events.ChildExistenceChangedEvent;
import mx.events.EffectEvent;
import mx.events.FlexEvent;
import mx.events.ResizeEvent;
import mx.managers.PopUpManager;
import qnx.dialog.AlertDialog;
import qnx.events.QNXApplicationEvent;
import qnx.system.QNXApplication;
import views.TelestratorHome;
private var headerTimer:Timer = new Timer(5000,1);
[Bindable] private var headerMenu:HeaderMenu;
[Bindable] private var headerMenuDisplayed:Boolean = false;
private var tele:TelestratorHome;
private var alert2:AlertDialog = new AlertDialog();
protected function creationCompleteHandler(event:FlexEvent):void
{
QNXApplication.qnxApplication.addEventListener(QNXApplicationEvent.SWIPE_DOWN, onApplicationSwipeDown);
headerMenu = new HeaderMenu();
headerMenu.addEventListener(MouseEvent.CLICK, onHeaderMenuClick);
this.tele = new TelestratorHome();
this.tele.width = 100;
this.tele.height = 100;
this.tele.x = 10;
this.tele.y = 10;
this.addChild( tele );
headerMenu.addEventListener('parentDoSomething', onHeaderBarToldMeTo);
headerTimer.addEventListener(TimerEvent.TIMER, onHeaderMenuTimerEvent);
}
protected function onApplicationSwipeDown(event:QNXApplicationEvent) : void
{
showHeaderMenuPopup();
}
private function showHeaderMenuPopup() : void
{
if (!headerMenuDisplayed)
{
headerMenu.x = 0;
headerMenu.y = 0;
PopUpManager.addPopUp(headerMenu,this, false);
headerPopupEffectIn.end();
headerPopupEffectIn.play();
headerMenuDisplayed = true;
}
headerTimer.reset();
headerTimer.start();
}
private function onHeaderMenuTimerEvent(event:TimerEvent) : void
{
hideHeaderMenuPopup();
headerTimer.stop();
}
private function onHeaderMenuClick(event:MouseEvent) : void
{
hideHeaderMenuPopup();
}
private function hideHeaderMenuPopup() : void
{
headerPopupEffectOut.end();
headerPopupEffectOut.play();
headerPopupEffectOut.addEventListener(EffectEvent.EFFECT_END, headerMenuHidden);
}
private function headerMenuHidden(event:EffectEvent) : void
{
PopUpManager.removePopUp(headerMenu);
headerPopupEffectIn.removeEventListener(EffectEvent.EFFECT_END, headerMenuHidden);
headerMenuDisplayed = false;
}
protected function onHeaderBarToldMeTo(event:Event):void{
tele.eraseCanvas(event);
}
]]>
</fx:Script>
<fx:Declarations>
<s:Parallel id="headerPopupEffectIn" target="{headerMenu}">
<s:Move duration="250" yFrom="-75" xTo="0" yTo="0" easer="{sineEaser}" />
</s:Parallel>
<s:Parallel id="headerPopupEffectOut" target="{headerMenu}">
<s:Move duration="250" yFrom="0" xTo="0" yTo="-75" easer="{sineEaser}" />
</s:Parallel>
<s:Sine id="sineEaser" easeInFraction="0.2" />
</fx:Declarations>
</s:MobileApplication>
TelestratorHome.mxml
------------------
<?xml version="1.0" encoding="utf-8"?>
<s:View xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:mx="library://ns.adobe.com/flex/mx"
xmlns:s="library://ns.adobe.com/flex/spark"
actionBarVisible="true" creationComplete="init()" overlayControls="false"
tabBarVisible="true" title="Telestrator">
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>
<s:actionContent>
<s:Button label="Erase" click="drawingArea.erase(event)"/>
<s:Button id="saveBtn" label="Save Image" click="save()" visible="false"/>
</s:actionContent>
<s:titleContent>
<s:Button label="Take Photo" click="takeSnapshot()"/>
</s:titleContent>
<s:Panel id="p" top="1" width="1024" height="600" backgroundAlpha="0.0" horizontalCenter="0">
<s:Panel id="imageViewer" top="-32" width="1024" height="600" backgroundAlpha="0.0" horizontalCenter="0"/>
<s:Panel id="preview" top="-32" width="1024" height="600" backgroundAlpha="0.0" horizontalCenter="0"/>
<DrawingArea xmlns="*" id="drawingArea" y="-32" width="100%" height="509"/>
</s:Panel>
<fx:Script>
<![CDATA[
import com.tinkerlog.WebCam;
import com.tinkerlog.util.Base64;
import flash.media.Camera;
import mx.core.UIComponent;
import mx.graphics.codec.JPEGEncoder;
import mx.graphics.codec.PNGEncoder;
import qnx.dialog.AlertDialog;
private var webCam:WebCam;
private var alert3:AlertDialog = new AlertDialog();
private function init():void {
webCam = new WebCam(1024, 600);
var ref:UIComponent = new UIComponent();
preview.removeAllElements();
preview.addElement(ref);
ref.addChild(webCam);
}
private function takeSnapshot():void {
imageViewer.visible = true;
preview.visible=false;
saveBtn.visible = true;
imageViewer.width = preview.width;
imageViewer.height = preview.height;
var uiComponent : UIComponent = new UIComponent();
uiComponent.width = webCam.width;
uiComponent.height = webCam.height;
var photoData:Bitmap = webCam.getSnapshot();
var photoBitmap:BitmapData = photoData.bitmapData;
uiComponent.addChild(photoData);
imageViewer.removeAllElements();
imageViewer.addElement(uiComponent);
}
private function deleteSnapshot():void {
imageViewer.removeAllElements();
}
public function save():void
{
var bd:BitmapData = new BitmapData(p.width, p.height);
bd.draw(this);
var ba:ByteArray = (new PNGEncoder()).encode(bd);
(new FileReference()).save(ba, "doodle.png");
}
public function eraseCanvas(event:Event):void{
drawingArea.erase(event);
}
]]>
</fx:Script>
</s:View>
HeaderMenu.mxml
--------------
<?xml version="1.0" encoding="utf-8"?>
<s:BorderContainer xmlns:fx="http://ns.adobe.com/mxml/2009" borderVisible="false"
xmlns:s="library://ns.adobe.com/flex/spark" width="1024" height="75" xmlns:mx="library://ns.adobe.com/flex/mx" >
<fx:Script>
<![CDATA[
import mx.managers.PopUpManager;
import qnx.dialog.AlertDialog;
private var tele:Telestrator = new Telestrator();
private var alert:AlertDialog = new AlertDialog();
protected function btn_clickHandler(event:MouseEvent):void
{
// Figure out which button was pressed, and load appropriate view.
}
protected function onButtonInheaderbarClick(event:Event):void{
dispatchEvent(new Event('parentDoSomething'));
}
]]>
</fx:Script>
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>
<s:backgroundFill>
<s:SolidColor alpha="0.9" color="#000000" />
</s:backgroundFill>
<s:layout>
<s:HorizontalLayout horizontalAlign="right" verticalAlign="middle" paddingTop="5" paddingBottom="5" paddingRight="5" paddingLeft="5" gap="5"/>
</s:layout>
<s:Button id="btn_refresh" label="Refresh" height="60" fontSize="18" click="onButtonInheaderbarClick(event)"/>
<s:Label text="APPLICATION NAME" fontSize="32" styleName="FeedItemTitle" textAlign="center" width="100%" height="100%" verticalAlign="middle" />
<s:Button id="btn_about" label="About" height="60" fontSize="18" click="btn_clickHandler(event)"/>
</s:BorderContainer>
OK, still looking it over but the first mistake I see is you nested function which could be causing scoping issues so lets start with that and see if that helps.
Fix your DrawingArea class like so.
package{
import flash.display.BitmapData;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.net.FileReference;
import flash.utils.ByteArray;
import mx.core.UIComponent;
import mx.events.FlexEvent;
import mx.graphics.codec.PNGEncoder;
public class DrawingArea extends UIComponent{
private var isDrawing:Boolean = false;
private var x1:int;
private var y1:int;
private var x2:int;
private var y2:int;
public var drawColor:uint = 0x000000;
public function DrawingArea(){
this.super();
this.addEventListener(Event.ADDED_TO_STAGE, this.erase );
this.addEventListener(MouseEvent.MOUSE_DOWN, this.mouseDown);
this.addEventListener(MouseEvent.MOUSE_MOVE, this.mouseMove);
this.addEventListener(MouseEvent.MOUSE_UP, this.mouseUp);
}
private function mouseDown( e:MouseEvent ):void{
this.x1 = mouseX;
this.y1 = mouseY;
this.isDrawing = true;
}
private function mouseMove(e:MouseEvent ):void{
if (!event.buttonDown){
this.isDrawing = false;
}
this.x2 = mouseX;
this.y2 = mouseY;
if (this.isDrawing){
this.graphics.lineStyle(2, this.drawColor);
this.graphics.moveTo(this.x1, this.y1);
this.graphics.lineTo(this.x2, this.y2);
this.x1 = this.x2;
this.y1 = this.y2;
}
}
private function mouseUp( e:MouseEvent ):void{
this.isDrawing = false;
}
public function erase( e:Event ):void{
this.graphics.clear();
this.graphics.beginFill(0xffffff, 0.00001);
this.graphics.drawRect(0, 0, this.width, this.height);
this.graphics.endFill();
}
}
}
In main.mxml do this
private var child:Child;
function init(){
this.child = = new Child();
this.child.width = 100;
this.child.height = 100;
this.child.x = 10;
this.child.y = 10;
this.addChild( child );
headerMenu.addEventListener('parentDoSomething', onHeaderBarToldMeTo);
}

Cannot figure out why List won't display data from ArrayCollection

I'm working on a flash cards application and am using an ArrayCollection of Objects to store each cards individual data. When the user click the 'save' button, the text from the two textAreas and the 'title' textinput are stored in the AC as one object with .title, .side1 and .side2 properties that contain the text from the flash card.
I have made a List in a separate class I want to have display the title of each card the user has created, but after days of researching and looking around, I still cannot get the display to list the titles.
If anyone could point me in the right direction it would very appreciated.
Part of my NewCard.mxml:
<?xml version="1.0" encoding="utf-8"?>
<fx:Script>
<![CDATA[
import flash.events.EventDispatcher;
import mx.collections.ArrayCollection;
import spark.effects.SlideViewTransition;
import views.MyCards;
protected function button1_clickHandler(event:MouseEvent):void // back button
{
{
navigator.pushView(views.MyFlashCardsHome, event.relatedObject);
}
}
protected function button2_clickHandler(event:MouseEvent):void // save button
{
var myc:MyCards = new MyCards();
var card:Object = new Object();
myc.add();
titleCard.text = "Card Added!";
}
protected function button3_clickHandler(event:MouseEvent):void // flip button
{
rotateEffect.play();
if(rotateEffect.isPlaying)
{
if(mtext1.visible)
{
mtext2.visible = true;
mtext1.visible = false;
//mtext2.text = "two";
groupt.layoutDirection = "rtl";
}
else
{
mtext2.visible = false;
mtext1.visible = true;
//mtext1.text = "one";
groupt.layoutDirection = "rtl";
}
}
}
protected function button4_clickHandler(event:MouseEvent):void // push home button
{
var slideViewTransition:SlideViewTransition = new SlideViewTransition( 300, SlideViewTransition.SLIDE_RIGHT);
navigator.pushView(views.HomePage, event.relatedObject, slideViewTransition);
}
]]>
</fx:Script>
<fx:Declarations>
<s:Rotate3D id="rotateEffect" duration="300" target="{groupt}"
angleYFrom="0" angleYTo="180"
autoCenterTransform="true"
effectStart="flipButton.enabled=false;"
effectEnd="flipButton.enabled=true;"/>
</fx:Declarations>
<s:actionContent>
<s:Button height="50" label="Study" click="button1_clickHandler(event)" cornerRadius="0"
fontFamily="_sans"/>
<s:Button height="62" click="button4_clickHandler(event)" cornerRadius="0" skinClass="skins.homeButtonSkin"/>
</s:actionContent>
<s:Image x="0" y="-80" width="1024" height="600" source="#Embed('mainapp1.jpg')"/>
<s:TextInput id="titleCard" x="240" y="10" height="62" chromeColor="#515851" color="#060606"
contentBackgroundAlpha="1.0" contentBackgroundColor="#FFFFFF" text="Title"/>
<s:SkinnableContainer
id = "groupt" x="161" y="88" width="703" height="357" >
<s:TextArea id="mtext2" visible="false" x="0" y="0" width="703" height="357"
color="#000000" contentBackgroundAlpha="1.0"
contentBackgroundColor="#FFFFFF" editable="true" enabled="true"
paddingTop="70" text="Enter Text Here: (Side Two)" textAlign="center"/>
<s:TextArea id="mtext1" x="0" y="0" width="703" height="357" color="#030303"
contentBackgroundAlpha="1.0" contentBackgroundColor="#FFFFFF" editable="true"
enabled="true" fontFamily="Arial" fontStyle="normal" fontWeight="normal"
lineThrough="false" paddingTop="70" text="Enter Text Here: (Side One)"
textAlign="center" textDecoration="none" verticalAlign="middle"/>
</s:SkinnableContainer>
<s:Button x="763" y="10" height="62" label="Save" click="button2_clickHandler(event)"
cornerRadius="0" fontFamily="_sans"/>
<s:Label x="5" y="34" color="#49A6D6" fontFamily="Georgia" fontStyle="italic" fontWeight="bold"
paddingLeft="25" text="My"/>
<s:Label x="68" y="34" width="73" color="#E0B338" fontFamily="Georgia" fontStyle="italic"
fontWeight="bold" paddingLeft="0" text="Flash"/>
<s:Label x="138" y="34" color="#49A6D6" fontFamily="Georgia" fontStyle="italic" fontWeight="bold"
text="Cards!"/>
<s:Button id="flipButton" x="468" y="460" height="50" label="Flip" chromeColor="#2428D8"
click="button3_clickHandler(event)" fontFamily="_sans"/>
Part of my MyCards.mxml:
<?xml version="1.0" encoding="utf-8"?>
<fx:Script>
<![CDATA[
import flash.events.IOErrorEvent;
import flash.filesystem.File;
import flash.filesystem.FileMode;
import flash.filesystem.FileStream;
import mx.collections.ArrayCollection;
import mx.collections.ArrayList;
import mx.events.CollectionEvent;
import mx.events.FlexEvent;
import spark.effects.SlideViewTransition;
import spark.events.IndexChangeEvent;
import views.NewCard;
public var file:File;
public var fileStream:FileStream;
public var fileName:String = "Initial String";
private var directory:String = "SimpleSaveFromAIR";
public var nc:NewCard = new NewCard();
public var card:Object = new Object();
[Bindable]
public var cards:ArrayCollection = new ArrayCollection();
protected function button1_clickHandler(event:MouseEvent):void // pushed home button
{
var svt:SlideViewTransition = new SlideViewTransition(300, SlideViewTransition.SLIDE_RIGHT);
navigator.pushView(views.HomePage, event.relatedObject, svt);
}
public function add():void
{
var nc:NewCard = new NewCard();
var card:Object = new Object();
card.fTitle = nc.titleCard.text; //adding text to object from NewCard.mxml class
cards.addItem(card);
}
/* public function save():void
{
file = File.documentsDirectory.resolvePath(directory + "/" + fileName);
fileStream = new FileStream();
fileStream.open(file, FileMode.WRITE);
fileStream.writeObject(cards);
fileStream.close();
} */
public function myCardsList_creationCompleteHandler(event:FlexEvent):void
{
cards.addEventListener(CollectionEvent.COLLECTION_CHANGE, refreshList);
trace(cards.list); // no data at all shows up here
}
private function refreshList(event:CollectionEvent):void
{
trace("cards refreshed "+ cards.list);
}
public function testButton_clickHandler(event:MouseEvent):void
{
card.fTitle = nc.titleCard.text;
cards.addItem(card);
//trace(cards.list); // add data that has been added shows up here
}
]]>
</fx:Script>
<s:actionContent>
<s:Button id="testButton" label="Button" click="testButton_clickHandler(event)" />
<s:Button label="Delete"/>
<s:Button label="Home" click="button1_clickHandler(event)" skinClass="skins.homeButtonSkin"/>
</s:actionContent>
<s:Image x="0" y="-80" height="603" source="mainapp1.jpg"/>
<s:List id="myCardsList" x="10" y="10" left="0" right="0" top="0" bottom="0" width="1004"
height="500" dataProvider="{cards}" labelField="fTitle"
enabled="true" >
</s:List>
Again any help is greatly appreciated.
CardVO class:
package
{
public class CardVO
{
private var _title:String; //values returned from getter/setter functions
private var _side1:String;
private var _side2:String;
//get the "Title", "Side1" and "Side2" values from textAreas (later) and set them
// above variables
public function get Title():String {return _title;}
public function set Title(value:String):void { _title = value; }
public function get Side1():String {return _side1;}
public function set Side1(value:String):void {_side1 = value;}
public function get Side2():String {return _side2;}
public function set Side2(value:String):void {_side2 = value;}
}
}
** NewCard snippet:**
[Bindable]
public var myCard:CardVO = new CardVO(); // create new instance of CardVO
....
<!-- text property of mtext1 and mtext2 is bound and returned to the get/set functions in CardVO in the 'change' event-->
<!-- change sets setter values to those retrieved from textAreas-->
<s:TextArea id="mtext2" visible="false" x="0" y="0" width="703" height="357"
color="#000000" contentBackgroundAlpha="1.0"
contentBackgroundColor="#FFFFFF" editable="true" enabled="true"
paddingTop="70" text="{myCard.Side2}" change = "{myCard.Side2 = mtext2.text}"
textAlign="center"/>
<s:TextArea id="mtext1" x="0" y="0" width="703" height="357" color="#030303"
contentBackgroundAlpha="1.0" contentBackgroundColor="#FFFFFF" editable="true"
enabled="true" fontFamily="Arial" fontStyle="normal" fontWeight="normal"
lineThrough="false" paddingTop="70" text="{myCard.Side1}" change="{myCard.Side1 = mtext1.text}"
textAlign="center" textDecoration="none" verticalAlign="middle"/>
</s:SkinnableContainer>
MyCards snippet:
public function add():void
{
var nc:NewCard = new NewCard(); // create new instance of NewCard
cards.addItem(nc.myCard); // add new Item to ArrayCollection 'cards'
trace(cards.list);
}
Mycards List code
<s:List id="myCardsList" x="10" y="10" left="0" right="0" top="0" bottom="0" width="1004"
height="500" change="myCardsList_changeHandler(event)" dataProvider="{cards}"
enabled="true" >
<s:itemRenderer>
<fx:Component>
<s:MobileItemRenderer label="{data.title}"/>
</fx:Component>
</s:itemRenderer>
</s:List>
Assuming you're using the List component you should be able to specify the field you want to show using the labelField property.
<s:List id="myFlashCardList" dataProvider="{cards}" labelField="fTitle"/>
EDIT 2:
It seems like what you're trying to do here (and correct me if I'm wrong), is to have the user create a new instance of the NewCard object and then add it to your cards ArrayCollection. Your list then displays the titles of the cards the user has created.
Assuming this is the case, I think you're making it a little complicated than it needs to be. ArrayCollections can hold any type of class or object so you don't have to create a new Object and add it to the ArrayCollection every time they add a new card.
What I would do is create a Card class and populate it using your NewCard component. When you're done, you add that Card class to the ArrayCollection. Something like this:
The CardVO class:
package
{
public class CardVO
{
private var _title:String;
private var _side1:String;
private var _side2:String;
public function get Title():String { return _title; }
public function set Title(value:String):void { _title = value; }
public function get Side1():String { return _side1; }
public function set Side1(value:String):void { _side1 = value; }
public function get Side2():String { return _side2; }
public function set Side2(value:String):void { _side2 = value; }
}
}
Then in your NewCard.mxml file you use a CardVO to store the data:
<fx:Script>
<![CDATA[
...
[Bindable] public var myCard:CardVO = new CardVO();
...
]]>
</fx:Script>
<s:SkinnableContainer id = "groupt">
<s:TextArea id="mtext2" text="{myCard.Side2}" change="{myCard.Side2 = mtext2.text}"/>
<s:TextArea id="mtext1" text="{myCard.Side1}" change="{myCard.Side1 = mtext1.text}" />
</s:SkinnableContainer>
Then after the user has created their card, you pass the CardVO object to your ArrayCollection.
...
public function add():void
{
var nc:NewCard = new NewCard();
cards.addItem(nc.myCard);
}
...
This is a very abbreviated example so feel free to ask any questions that don't make sense. You should also look into Data Binding if you haven't already done so. It will save you a lot of time and make your apps more efficient once you get the hang of it. :)

Dynamic Spark DropDownList ItemRenderer within Flex Datagrid

I have a datagrid which contains a Spark dropdownlist that needs to obtain dynamic data. The datagrid uses a separate dataProvider.
When I use a static ArrayCollection within my ItemRenderer, it works (please see listing 1).
However, when I use Swiz to mediate a 'list complete' event to load the ArrayCollection, the dropdownlist does not show the new data (please see listing 2).
Using the debugger, I inspected the dropdownlist ItemRenderer and have confirmed the new data is being loaded into the ArrayCollection. The new data is not shown in the UI control. I have tried invalidateProperties() + validateNow() and dispatching events on both the control and the renderer (this), but nothing seems to make the new data appear in the control on the datagrid.
Please help !!!
Listing 1: Datagrid and static ArrayCollection (this works):
<mx:DataGrid x="10" y="25" width="98%" id="dgInventory" paddingLeft="25" paddingRight="25" paddingTop="25" paddingBottom="25"
editable="true"
itemClick="dgInventory_itemClickHandler(event)" dataProvider="{acInventory}"
creationComplete="dgInventory_creationCompleteHandler(event)"
height="580">
<mx:columns>
<mx:DataGridColumn headerText="Item" dataField="itemName" itemRenderer="components.ItemRendererItem"
rendererIsEditor="true" editorDataField="selection" editable="true"/>
<mx:DataGridColumn headerText="Quantity Required" dataField="quantityReq" itemRenderer="components.ItemRendererQuantityRequired"
rendererIsEditor="true" editorDataField="selection" editable="true"/>
</mx:columns>
</mx:DataGrid>
<fx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
import spark.events.IndexChangeEvent;
public var selection:int;
[Bindable]
protected var acItem:ArrayCollection = new ArrayCollection(
[ { itemName: "Item1"},
{ itemName: "Item2"},
{ itemName: "Item3"},
]);
//
protected function dropdownlist1_changeHandler(e:IndexChangeEvent):void
{
selection = e.newIndex;
}
]]>
</fx:Script>
<s:DropDownList id="ddlItem" prompt="Select Item" dataProvider="{acItem}" labelField="itemName"
selectedIndex="{int(dataGridListData.label)}"
change="dropdownlist1_changeHandler(event)"
width="80%" top="5" bottom="5" left="5" right="5"/>
Listing 2: Dynamic ArrayCollection (does not work):
<?xml version="1.0" encoding="utf-8"?>
<s:MXDataGridItemRenderer xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
focusEnabled="true">
<fx:Script>
<![CDATA[
import event.ItemEvent;
import mx.collections.ArrayCollection;
import mx.events.FlexEvent;
import spark.events.IndexChangeEvent;
public var selection:int;
//
[Bindable]
protected var acItem:ArrayCollection = new ArrayCollection();
//
protected function dropdownlist1_changeHandler(e:IndexChangeEvent):void
{
selection = e.newIndex;
}
//
protected function ddlItem_creationCompleteHandler(event:FlexEvent):void
{
var eve : ItemEvent = new ItemEvent( ItemEvent.LIST_ITEM_REQUESTED );
dispatchEvent( eve );
}
//
[Mediate( event="ItemEvent.LIST_ITEM_COMPLETE", properties="acItem" )]
public function refreshness( _acItem : ArrayCollection ):void
{
acItem.removeAll();
var len:int = _acItem.length;
if (len > 0)
{
for (var i:int=0; i < len; i++)
{
var newItem:Object = new Object;
newItem["itemName"] = _acItem[i].itemName;
acItem.addItem(newItem);
}
}
this.invalidateProperties();
this.validateNow();
//dispatchEvent(new FlexEvent(FlexEvent.DATA_CHANGE));
}
]]>
</fx:Script>
<s:DropDownList id="ddlItem" prompt="Select Item" dataProvider="{acItem}" labelField="itemName"
selectedIndex="{int(dataGridListData.label)}"
creationComplete="ddlItem_creationCompleteHandler(event)"
change="dropdownlist1_changeHandler(event)"
width="80%" top="5" bottom="5" left="5" right="5"/>
</s:MXDataGridItemRenderer>
After re-reading Peter Ent's ItemRenderer series, this turned out to be quite simple.
I extended DataGrid to have the ArrayCollection property I needed, then added this to my renderer:
[Bindable]
protected var acItem:ArrayCollection = new ArrayCollection();
//
override public function set data( value:Object ) : void
{
super.data = value;
acItem = (listData.owner as MyDataGrid).itemList; // get the data from the renderer's container (by extending it to add a property, if necessary)
}

Flex: Saving mx:image with applied effects

I load image to control than I applie some effects, and when I save image it's saving without effects. What should i do?
Here is the code:
private var byteArr2:ByteArray;
private var fileRef:FileReference = new FileReference();
public function process():void
{
var ct:ColorTransform = new ColorTransform();
ct.redOffset = 99;
ct.blueOffset = 11;
ct.greenOffset = 22;
currImg.transform.colorTransform = ct;
callLater(toByteArray);
}
public function toByteArray():void
{
var data:BitmapData = new BitmapData(currImg.width, currImg.width);
data.draw(currImg);
var encod:JPEGEncoder = new JPEGEncoder(100);
byteArr2 = encod.encode(data);
}
public function saveFile():void
{
fileRef.save(byteArr2,"NewFileName1.jpg");
}
<mx:HBox>
<mx:VBox>
<s:Button x="69" y="98" label="open" click="open()()"/>
<s:Button label="show" click="show()"/>
<s:Button label="process" click="process()"/>
<s:Button label="save" click="saveFile()"/>
</mx:VBox>
<mx:Image id="currImg" width="200" height="300"/>
</mx:HBox>
UPDATE Appears new problem as I am using var data:BitmapData = new BitmapData(currImg.width, currImg.width); saved image is small(size like image control) but I need to save image with original size.
With var data:BitmapData = Bitmap(currImg.content).bitmapData; it worked
I would draw the component into a new BitmapData object rather than use the content of the currImg. This should give you what's drawn on the screen rather than the unmodified content. Something like so:
var data:BitmapData = new BitmapData(currImg.width, currImg.width);
data.draw(currImg);
Hope that helps.
Alright this isn't a great solution cause I don't know why it works but if you put a container around the image then save the results of drawing that it seems to work.
<?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.graphics.codec.JPEGEncoder;
private var byteArr2:ByteArray;
private var fileRef:FileReference = new FileReference();
public function process():void
{
var ct:ColorTransform = new ColorTransform();
ct.redOffset = 99;
ct.blueOffset = 11;
ct.greenOffset = 22;
currImg.transform.colorTransform = ct;
callLater(toByteArray);
}
public function toByteArray():void
{
var data:BitmapData = new BitmapData(everything.width, everything.width);
data.draw(everything);
var encod:JPEGEncoder = new JPEGEncoder(100);
byteArr2 = encod.encode(data);
}
public function saveFile():void
{
fileRef.save(byteArr2,"NewFileName1.jpg");
}
]]>
</fx:Script>
<mx:HBox>
<mx:VBox>
<!--<s:Button x="69" y="98" label="open" click="open()"/>-->
<!--<s:Button label="show" click="show()"/> -->
<s:Button label="process" click="process()"/>
<s:Button label="save" click="saveFile()"/>
</mx:VBox>
<mx:Box id="everything">
<mx:Image id="currImg" width="200" height="300" source="http://www.google.com/images/logos/ps_logo2.png"/>
</mx:Box>
</mx:HBox>
</s:Application>
Shaun

Resources