flex mobile TabbedViewNavigatorApplication back button - apache-flex

Why TabbedViewNavigatorApplication don’t have popView() (as in ViewNavigatorApplication I can use popView to go previous view)?
How can I do that in TabbedViewNavigatorApplication?
<fx:Script>
<![CDATA[
protected function BackBtn(event:MouseEvent):void{
navigator.popView(); //error
}
]]>
</fx:Script>
<s:ViewNavigator label="Page1" width="100%" height="100%" firstView="views.DurationView" >
<s:titleContent>
<s:Button label="Back" click="BackBtn(event)"/>
</s:titleContent>
</s:ViewNavigator>
<s:ViewNavigator label="Page2" width="100%" height="100%" firstView="views.FrequencyView"/>
</s:TabbedViewNavigatorApplication>
thanks.

<?xml version="1.0" encoding="utf-8"?>
<s:TabbedViewNavigatorApplication creationComplete="tabbedviewnavigatorapplication1_creationCompleteHandler(event)"
xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark">
<fx:Script>
<![CDATA[
import mx.events.FlexEvent;
import spark.events.IndexChangeEvent;
private var tabHistory : Array;
private var isLoadingFromHistory : Boolean;
protected function BackBtn(event : MouseEvent) : void
{
isLoadingFromHistory = true;
if (tabHistory.length == 0)
{
trace("You can't go back any further");
tabHistory.push(0);
}
tabbedNavigator.selectedIndex = tabHistory.pop();
}
protected function tabbedviewnavigatorapplication1_creationCompleteHandler(event : FlexEvent) : void
{
tabHistory = [];
tabbedNavigator.addEventListener(IndexChangeEvent.CHANGE, tabsChangedHandler);
}
private function tabsChangedHandler(event : IndexChangeEvent) : void
{
if (isLoadingFromHistory)
{
isLoadingFromHistory = false;
return;
}
tabHistory.push(event.oldIndex);
trace(tabHistory);
}
]]>
</fx:Script>
<s:ViewNavigator firstView="views.WhosAtTheDoorHomeView"
height="100%"
label="Page1"
width="100%">
<s:titleContent>
<s:Button click="BackBtn(event)"
label="Back"/>
</s:titleContent>
</s:ViewNavigator>
<s:ViewNavigator firstView="views.WhosAtTheDoorHomeViewCopy"
height="100%"
label="Page2"
width="100%">
<s:titleContent>
<s:Button click="BackBtn(event)"
label="Back"/>
</s:titleContent>
</s:ViewNavigator>
</s:TabbedViewNavigatorApplication>

Related

Flex s:DataGrid all ItemRenderer are fired

I don't understand why when we change data on dataprovider, all the itemRenderers of the impacted column are called ? Here is a little example of my problem :
application :
<?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" creationComplete="application1_creationCompleteHandler(event)">
<fx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
import mx.events.FlexEvent;
private var _ac:ArrayCollection = new ArrayCollection();
protected function application1_creationCompleteHandler(event:FlexEvent):void
{
var i:int = 10;
while(i >= 0)
{
_ac.addItem({fieldA:Math.random() * 100, fieldB:Math.random() * 100});
i--;
}
dg.dataProvider = _ac;
dg.requestedRowCount = _ac.length;
}
protected function button1_clickHandler(event:MouseEvent):void
{
_ac.getItemAt(0)["fieldA"] = Math.random() * 100;
_ac.getItemAt(0)["fieldB"] = Math.random() * 100;
_ac.refresh();
}
]]>
</fx:Script>
<s:layout>
<s:VerticalLayout />
</s:layout>
<s:Button label="change data" click="button1_clickHandler(event)" />
<s:DataGrid id="dg">
<s:columns>
<s:ArrayList>
<s:GridColumn dataField="fieldA" itemRenderer="MyItemRenderer" />
<s:GridColumn dataField="fieldB" />
</s:ArrayList>
</s:columns>
</s:DataGrid>
</s:Application>
myItemRenderer :
<?xml version="1.0" encoding="utf-8"?>
<s:GridItemRenderer xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx" clipAndEnableScrolling="true">
<fx:Declarations>
<s:Sequence id="myAnimation" duration="2000" target="{col}" >
<s:Fade alphaFrom="0.0" alphaTo="1.0"/>
<s:Fade alphaFrom="1.0" alphaTo="0.0"/>
</s:Sequence>
</fx:Declarations>
<fx:Script>
<![CDATA[
private var _data:Object;
override public function set data(value:Object):void
{
super.data = value;
_data = value;
if(null != _data)
{
display();
}
}
private function display():void
{
lblData.text = _data[column.dataField];
myAnimation.play();
}
]]>
</fx:Script>
<s:Rect top="-2" left="0" right="0" bottom="-2">
<s:fill>
<s:SolidColor id="col" color="0xFF0000" />
</s:fill>
</s:Rect>
<s:Label id="lblData" top="9" left="7"/>
</s:GridItemRenderer>
I just want to play animation on the impacted cell but each time, animation on each cell is fired. How can I code that please ?
The reason for all that recycling is this: you're calling the refresh method, hence you're explicitly asking for all this recycling. It will invalidate all the sorts and filters applied to your collection, hence all the positions of the items in the datagrid are invalidated and every item is recycled.
So you should remove this call. After that something like this should then do what you expect:
private var colValue:String;
override public function prepare(hasBeenRecycled:Boolean):void {
if (data && colValue != data[column.dataField]) {
colValue = data[column.dataField];
lblData.text = colValue;
myAnimation.play();
}
}
The prepare method will still be called every time, but your animation will only be played when the value has actually changed.

How to render a collection of DisplayObject instances in a spark List?

Is it possible to have a Spark List display items from an ArrayCollection of DisplayObjects (i.e.: Canvas)? Something like the code below does not render anything unless I specify an itemRenderer.
<s:List dataProvider="{ displayObjects }">
<s:layout>
<s:VerticalLayout rowHeight="290" gap="20" />
</s:layout>
</s:List>
The reason is to leverage the List's drag and drop features for rearranging custom components. Currently, I am using a VGroup (without drag and drop) and do not want to recreate all the drag and drop functionality that you get "for free" with the List control. Other viable solutions can also be accepted.
TIA.
Try this:
//Application
<?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"
creationComplete="init()">
<fx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
import mx.containers.Canvas;
[Bindable]private var displayObjects:ArrayCollection = new ArrayCollection();
[Bindable]private var size:int = 40;
private const COLOR:Array = ["0x00ff00", "0xff0000", "0x0000ff", "0xffff00" , "0x00ffff"];
private function doAddCanvas(colorId:int):Canvas
{
var canvas:Canvas = new Canvas();
canvas.setStyle("backgroundColor", COLOR[colorId]);
canvas.width = size;
canvas.height = size;
return canvas;
}
protected function init():void
{
for (var i:int = 0; i < 5; i++)
displayObjects.addItem(doAddCanvas(i));
}
]]>
</fx:Script>
<s:VGroup x="20" y="20">
<s:List
width="100" height="150"
dataProvider="{displayObjects}"
itemRenderer="com.ListItemRenderer"
dragEnabled="true" dropEnabled="true"
dragMoveEnabled="true">
<s:layout>
<s:VerticalLayout rowHeight="{size}" gap="20" />
</s:layout>
</s:List>
</s:VGroup>
</s:Application>
//ItemRenderer
<?xml version="1.0" encoding="utf-8"?>
<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"
autoDrawBackground="true">
<fx:Script>
<![CDATA[
import mx.containers.Canvas;
override public function set data(value:Object):void
{
if (value != null)
{
hgMain.removeAllElements();
hgMain.addElement(value as Canvas);
}
super.data = value;
}
]]>
</fx:Script>
<s:HGroup id="hgMain" width="100%" height="100%"/>
</s:ItemRenderer>

How is an mx:Canvas measured when inside a renderer in Flex 3?

I'm having a sizing issue with a canvases located inside an HBox. It seems "_graphic", "_border" and "_fill" canvases (in com.example.ThingRenderer.mxml) do not get measured at the same time as all the other measurements inside the renderer. However, this problem is only observed on the first pass-through. Refer to the images for a visual... 1st image shows the state of the app after it finished loading. 2nd image represents what the screen looks like after the Populate button is clicked. 3rd image shows what happens when the stepper is incremented. The question is how come the drawing in the 3rd image doesn't get rendered once the data is populated into the table?
RendererTest.mxml
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
layout="absolute"
creationComplete="handleCreationComplete(event)"
>
<mx:Script>
<![CDATA[
import com.example.Thing;
import mx.collections.ArrayCollection;
import mx.events.FlexEvent;
import mx.events.NumericStepperEvent;
private const _thingProvider:ArrayCollection = new ArrayCollection();
private var _thing1:Thing;
protected function handleCreationComplete(event:FlexEvent):void {
_thing1 = new Thing("thingy", 0xff0000, 0.3);
_stepper.value = _thing1.ratio;
}
protected function handlePopulateClick(event:MouseEvent):void {
_thingProvider.addItem(_thing1);
}
protected function handleStepperChange(event:NumericStepperEvent):void {
_thing1.ratio = event.value;
}
]]>
</mx:Script>
<mx:VBox>
<mx:Button label="Populate" click="handlePopulateClick(event)" />
<mx:NumericStepper id="_stepper" minimum="0" maximum="1" stepSize="0.01" change="handleStepperChange(event)" />
<mx:AdvancedDataGrid dataProvider="{_thingProvider}" variableRowHeight="true" width="100%" height="100%">
<mx:columns>
<mx:AdvancedDataGridColumn headerText="Name" dataField="name" />
<mx:AdvancedDataGridColumn headerText="Display"
width="150" sortable="false"
itemRenderer="com.example.ThingRenderer"
/>
</mx:columns>
</mx:AdvancedDataGrid>
</mx:VBox>
</mx:Application>
com.exampleThingRenderer.mxml
<?xml version="1.0" encoding="utf-8"?>
<mx:Canvas
xmlns:mx="http://www.adobe.com/2006/mxml"
width="100%"
horizontalScrollPolicy="off" verticalScrollPolicy="off"
>
<mx:Script>
<![CDATA[
import mx.binding.utils.ChangeWatcher;
private var _thing:Thing;
private var _ratioWatcher:ChangeWatcher;
private var _doClearContent:Boolean;
private var _doDrawBorder:Boolean;
private var _doUpdateFill:Boolean;
override public function set data(value:Object):void {
if(value && value is Thing) {
_thing = Thing(value);
if(_ratioWatcher) {
_ratioWatcher.unwatch();
}
_ratioWatcher = ChangeWatcher.watch(_thing, "ratio", handleRatioChanged);
_doClearContent = false;
_doDrawBorder = true;
_doUpdateFill = true;
_graphic.invalidateSize();
_border.invalidateSize();
}
else {
_doClearContent = true;
_doDrawBorder = false;
_doUpdateFill = false;
}
super.data = value;
}
private function handleRatioChanged(event:Event):void {
_doUpdateFill = true;
invalidateDisplayList();
}
override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void {
if(_doClearContent) {
_container.visible = false;
_container.includeInLayout = false;
_doClearContent = false;
}
super.updateDisplayList(unscaledWidth, unscaledHeight);
if(_doDrawBorder) {
trace("_thingContainer.width="+_container.width, "_thingGraphic.width="+_graphic.width, "_thingBorder.width="+_border.width);
_border.graphics.clear();
_border.graphics.moveTo(0, 0);
_border.graphics.lineStyle(1, _thing.color);
_border.graphics.lineTo(_border.width, 0);
_border.graphics.lineTo(_border.width, _border.height);
_border.graphics.lineTo(0, _border.height);
_border.graphics.lineTo(0, 0);
_doDrawBorder = false;
}
if(_doUpdateFill) {
_percentage.text = Math.round(_thing.ratio * 100.0) + "%";
_fill.graphics.clear();
_fill.graphics.beginFill(_thing.color);
_fill.graphics.drawRect(0, 0, _fill.width * _thing.ratio, _fill.height);
_doUpdateFill = false;
}
}
]]>
</mx:Script>
<mx:HBox id="_container" width="100%" paddingLeft="5" paddingTop="5" paddingRight="5" paddingBottom="5">
<mx:Label id="_percentage" width="45" />
<mx:Canvas id="_graphic" width="100%" height="15">
<mx:Canvas id="_border" x="0" y="0" width="100%" height="100%" />
<mx:Canvas id="_fill" x="0" y="0" width="100%" height="100%" />
</mx:Canvas>
</mx:HBox>
</mx:Canvas>
com.example.Thing.as
package com.example {
public class Thing {
[Bindable] public var name:String;
[Bindable] public var color:uint;
[Bindable] public var ratio:Number;
public function Thing(name:String, color:uint, ratio:Number) {
this.name = name;
this.color = color;
this.ratio = ratio;
}
}
}
All this happens because you can't use width and height properties in updateDisplayList, they are not updated yet. Make separate component (e.g. ThingProgressBar) and put drawing logick inside it, that will solve everything:
package
{
import mx.core.UIComponent;
public class ThingProgressBar extends UIComponent
{
private var _ratio:Number;
public function get ratio():Number
{
return _ratio;
}
public function set ratio(value:Number):void
{
_ratio = value;
invalidateDisplayList();
}
override protected function updateDisplayList(
unscaledWidth:Number, unscaledHeight:Number):void
{
super.updateDisplayList(unscaledWidth, unscaledHeight);
graphics.clear();
if (unscaledWidth > 0 && unscaledHeight > 0)
{
graphics.lineStyle(1, 0xFF0000);
graphics.drawRect(0, 0, unscaledWidth, unscaledHeight);
graphics.beginFill(0xFF0000);
graphics.drawRect(0, 0, unscaledWidth * ratio, unscaledHeight);
graphics.endFill();
}
}
}
}
So your renderer might look like this:
<mx:HBox
xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:mx="library://ns.adobe.com/flex/mx"
horizontalScrollPolicy="off" verticalScrollPolicy="off" xmlns:local="*"
>
<fx:Script>
<![CDATA[
[Bindable] private var _thing:Thing;
override public function set data(value:Object):void
{
_thing = value as Thing;
super.data = value;
}
]]>
</fx:Script>
<mx:HBox width="100%"
paddingLeft="5" paddingTop="5"
paddingRight="5" paddingBottom="5">
<mx:Label text="{_thing.name}" width="45" />
<local:ThingProgressBar width="100%" height="15"
ratio="{_thing.ratio}"/>
</mx:HBox>
</mx:HBox>
I removed watcher. Binding by watcher is considered a bad practice, use mxml binding or events instead.
I removed two Canvases with separated border and fill - they can be cobined together.
I used UIComponent instead of Canvas. Don't use containers unless you need layout, they are heavy.
I used HBox instead of Canvas in renderer because I like boxes more :) But you can't avoid using second container in renderer if you need custom styles since List overwrites renderer's stylesheet.

Flex- drag and drop for fxg

I am building a flex project involving drag+drop of fxg graphics
my graphic is instantiated as below
<graphics:arrow2 id="object" mouseMove="mouseMoveHandler(event);" />
I get an error: "Call to a possibly undefined method Graphic."
This works if I change my drag-object to Image and replace the 'Graphic' below with 'Image'. What should I use to be able to reference the fxg graphic in the drag-drop?
my drag drop functionality is as follows
private function mouseMoveHandler(event:MouseEvent):void
{
var dragInitiator:Graphic = Graphic(event.currentTarget);
var ds:DragSource = new DragSource();
ds.addData(dragInitiator,"gph");
DragManager.doDrag(dragInitiator, ds, event);
}
private function dragEnterHandler(event:DragEvent):void {
if (event.dragSource.hasFormat("gph"))
{
DragManager.acceptDragDrop(SkinnableContainer(event.currentTarget));
}
}
private var objectX:Number;
private function dragDropHandler(event:DragEvent):void {
objectX= SkinnableContainer(event.currentTarget).mouseX+50;
Graphic(event.dragInitiator).x = objectX;
Graphic(event.dragInitiator).y = 100;
}
This is an example taken from SaturnBoy:
<s:Application
xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark">
<fx:Script>
<![CDATA[
imports...
private function mouseDownHandler(e:MouseEvent):void {
DragManager.doDrag(e.currentTarget as IUIComponent, null, e);
}
private function dragEnterHandler(e:DragEvent):void {
DragManager.acceptDragDrop(e.currentTarget as IUIComponent);
}
private function dragDropHandler(e:DragEvent):void {
e.currentTarget.addElement(e.dragInitiator);
}
]]>
</fx:Script>
<s:Panel title="src" width="100" minHeight="133" x="10" y="10">
<s:Graphic width="80" height="80"
mouseDown="mouseDownHandler(event)">
<s:Rect ... </s:Rect>
</s:Graphic>
<s:Graphic width="80" height="80"
mouseDown="mouseDownHandler(event)">
<s:Ellipse ... </s:Ellipse>
</s:Graphic>
...
</s:Panel>
<s:Panel title="target" width="100" minHeight="133" x="120" y="10"
dragEnter="dragEnterHandler(event);"
dragDrop="dragDropHandler(event);">
...
</s:Panel>
</s:Application>

Flex-iframe: problems in FF

I'm using http://code.google.com/p/flex-iframe/ for showing wikicontent in a flex app.
<?xml version="1.0"?>
<mx:TitleWindow
xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns:components="components.*"
title="Hjälp"
showCloseButton="true"
close="close();"
styleName="Popup"
paddingBottom="0"
paddingLeft="0"
paddingRight="0"
paddingTop="0"
width="700"
height="500">
<mx:Script>
<![CDATA[
import mx.core.Application;
import mx.managers.PopUpManager;
import offerta.Config;
import offerta.monkeywrench.Icons;
import offerta.utils.printf;
import flash.utils.setTimeout;
import flash.display.DisplayObject;
private static var _popup:HelpPopup = null;
[Bindable]
public var callback:Function;
[Bindable]
public var key:String;
private function close(cancel:Boolean = true):void
{
PopUpManager.removePopUp(this);
if(callback!=null) callback();
}
public static function create():HelpPopup
{
HelpPopup._popup =
HelpPopup(PopUpManager.createPopUp(DisplayObject(Application.application),
HelpPopup, true));
HelpPopup._popup.visible = false;
return HelpPopup._popup;
}
public function show():void
{
PopUpManager.centerPopUp(HelpPopup._popup);
this.visible = true;
setTimeout(function():void {
refresh();
},500);
}
public function refresh():void
{
if(!!key)
{
frameMain.label = key;
frameMain.source = printf(Config.DOCUMENTATION_URL,key);
}
}
]]>
</mx:Script>
<mx:VBox width="100%" height="100%" paddingBottom="0" paddingLeft="0"
paddingRight="0" paddingTop="0">
<mx:ApplicationControlBar width="100%">
<mx:HBox width="100%" id="pnlToolbar" horizontalGap="0">
<mx:LinkButton
icon="{Icons.refresh}"
click="refresh();"/>
<mx:LinkButton
icon="{Icons.previous}"
click="frameMain.historyBack()"/>
<mx:LinkButton
icon="{Icons.next}"
click="frameMain.historyForward()"/>
</mx:HBox>
</mx:ApplicationControlBar>
<components:IFrame id="frameMain"
loadIndicatorClass="components.IFrameLoadingIndicator"
width="100%"
height="100%"/>
</mx:VBox>
<mx:ControlBar>
<mx:Spacer width="100%"/>
<mx:Button
width="80"
height="30"
label="Stäng"
click="close();"/>
</mx:ControlBar>
</mx:TitleWindow>
When displaying the window: In Ie. it works perfectly, but in FF the
content flashes quickly and then the iframe becomes blank?
I answered you on the bug you opened on the project site ;)
I had missed to add wmode=opaque and it works in FF. Only problem is that flex loadingindicator isn't showing.

Resources