I want to create a custom LinkBar in which the item which is selected shown with some space before the text or anything else as shown in the Image below.
when second item is selected then Two is having some effect and move slightly to left side.
when third item is selected then Three is having some effect and move slightly to left side and Two move at its original place.
Please Help me ...
Thanks in advance
This simple application demos a basic approach to doing this with spaces, its not bullet proof by any means, but it works, and its build on Flex SDK 3.6.
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical" width="100%" height="100%">
<mx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
import mx.events.FlexEvent;
import mx.events.ItemClickEvent;
private var _labels:Array = ["link 1", "link 2", "link 3"];
private var _links:ArrayCollection = new ArrayCollection();
/**
* Creation complete handler for linkbar.
* Sets up the link bar data provider.
*
* #param FlexEvent creation complete event dispatched from link bar
* */
private function links_creationCompleteHandler(event:FlexEvent):void{
resetLinks();
linkBar.dataProvider = _links;
}
/**
* Item click handler for link bar.
* Checks if user has selected the same link again.
* If not then resets the link bar data provider with original label values.
* Then adds event.label with 4 leading spaces at the index of the same string in array col.
* Then removes the original string form the array col.
*
* #param ItemClickEvent dispatched from link bar
* */
private function links_itemClickHandler(event:ItemClickEvent):void{
if (event.label.search(" ") == -1){
resetLinks();
_links.addItemAt(" " + event.label, event.index);
_links.removeItemAt(event.index + 1);
}
}
/**
* Remove all items from the link bar data provider.
* Then add back in the original items to reset item labels.
* */
private function resetLinks():void{
_links.removeAll();
for each (var str:String in _labels){
_links.addItem(str);
}
}
]]>
</mx:Script>
<mx:LinkBar id="linkBar"
direction="vertical"
creationComplete="links_creationCompleteHandler(event)"
width="100"
itemClick="links_itemClickHandler(event)"/>
</mx:Application>
In summary, it just adds some spaces onto the front of the item label selected. It does this by resetting the linkbars data provider each time the link bar dispatches the item click event. Then adding a replacement label for the selected item, removing the old one.
Feels a little clunky, and you could probably do something smoother with effects.
check this out, it could be a great beginning :
<s:layout>
<s:VerticalLayout gap="10"/>
</s:layout>
<fx:Script>
<![CDATA[
import mx.controls.LinkButton;
import mx.events.ItemClickEvent;
import spark.effects.animation.RepeatBehavior;
[Bindable] public var offset:Number = 50;
protected function animateLink(event:ItemClickEvent):void {
_resetOtherButtons(event.index);
var linkButton:LinkButton = event.relatedObject as LinkButton;
if (linkButton) {
var moveEffect:Move = moveEffects.getItemAt(event.index) as Move;
if (moveEffect) {
moveEffect.target = linkButton;
if (moveEffect.isPlaying || (linkButton.x - offset) >= linkbar.x) {
moveEffect.xTo = linkbar.x + linkbar.getStyle("paddingLeft");
} else {
moveEffect.xTo = linkbar.x + linkbar.getStyle("paddingLeft") + offset;
}
moveEffect.play();
}
}
}
protected function _resetOtherButtons(index:int):void {
var length:int = moveEffects.length;
for (var i:int=0; i < length; i++) {
if (i != index) {
var moveEffect:Move = moveEffects.getItemAt(i) as Move;
if (moveEffect) {
moveEffect.xTo = linkbar.x + linkbar.getStyle("paddingLeft");
moveEffect.play();
}
}
}
}
]]>
</fx:Script>
<mx:LinkBar id="linkbar" direction="vertical" itemClick="animateLink(event)" width="100" textAlign="left">
<mx:dataProvider>
<s:ArrayCollection>
<fx:String>Test1</fx:String>
<fx:String>Test2</fx:String>
<fx:String>Test3</fx:String>
</s:ArrayCollection>
</mx:dataProvider>
</mx:LinkBar>
<fx:Declarations>
<s:ArrayCollection id="moveEffects">
<s:Move/>
<s:Move/>
<s:Move/>
</s:ArrayCollection>
</fx:Declarations>
Related
I'm using mx:DataGrid and I cannot change to spark (it's a legacy project). The DataGrid is editable and when the users get in the last column I want to add a new column and start the edit mode in the first column of the new row. I can do this.
My problem is that sometimes the last column cannot be edited, so I added an itemEditBeginning listener to stop the edit and add the new row. That's my problem. When the user get in the last field, the new row is added but I cannot see it. I have to click in the column header to sort datagrid data, then the new rows appears. It's kind of a delay.
My code is bigger, but this simple code can reproduce the same problem:
<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"
creationComplete="application1_creationCompleteHandler(event)"
minWidth="955" minHeight="600">
<fx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
import mx.events.CollectionEvent;
import mx.events.CollectionEventKind;
import mx.events.DataGridEvent;
import mx.events.FlexEvent;
[Bindable] public var itens:ArrayCollection;
protected function application1_creationCompleteHandler(event:FlexEvent):void {
itens = new ArrayCollection();
itens.addEventListener(CollectionEvent.COLLECTION_CHANGE, onCollectionChange);
itens.addItem({a: '1', b: '2'});
}
protected function onCollectionChange(event:CollectionEvent):void {
if (event.kind == CollectionEventKind.ADD) {
var row:int = itens.length - 1;
var column:int = 0;
var args:Array = [row, column];
callLater(moveTo, args);
}
}
protected function moveTo(row:int, col:int):void {
itensDg.editedItemPosition = { rowIndex:row, columnIndex:col };
}
protected function addInfo():void {
itens.addItem({a: '10', b: '20'});
}
protected function itensDg_itemEditBeginningHandler(event:DataGridEvent):void {
if (event.columnIndex == 1) {
event.preventDefault();
addInfo();
}
}
]]>
</fx:Script>
<mx:DataGrid id="itensDg" dataProvider="{itens}" editable="true"
itemEditBeginning="itensDg_itemEditBeginningHandler(event)" />
</s:Application>
Any tips about how to solve this?
Thanks.
Try updating addInfo() as follows:
protected function addInfo():void {
itens.addItem({a: '10', b: '20'});
itens.refresh();
}
Trish answer led me to a solution that works. DataGrid has its own itemEditBeginning listener that modifies the editedItemPosition. Firstly I tried to stop the propagation of the itemEditBeginning event, but I realized some focus and mouse events modifies the editItemPosition too. So, the item was added in the array, my onCollectionChange listener was called, but then other events modified the editedItemPosition later.
So, instead of changing the editedItemPosition in the CollectionEvent listener, I simply changed it after adding the element:
protected function addInfo():void {
itens.addItem({a: '10', b: '20'});
var row:int = itens.length - 1;
var column:int = 0;
var args:Array = [row, column];
callLater(moveTo, args);
}
I wanna do a string effect like this website http://www.bcaa.it/
What kind of script or tutorial can I learn from?
Basically I want to achieve same as the website. Have the bouncing string effects, drag the item and move away others item that near it, drag and the sub object follow with easing...
Hey just started coding this up, need something similar for a graph display I'm working on. Here's my start, just two buttons but the idea could be extrapolated/optimized:
<?xml version="1.0" encoding="utf-8"?>
<s:WindowedApplication xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
creationComplete="windowedapplication1_creationCompleteHandler(event)">
<fx:Script>
<![CDATA[
import mx.events.FlexEvent;
private var optimalDistanceUpdateTimer:Timer;
private var optimalDistance:Number = 100;
private var objectUserGrabbed:Button;
private var delayDenominator:Number = 6;
protected function button1_mouseDownHandler(event:MouseEvent):void
{
objectUserGrabbed = event.target as Button;
objectUserGrabbed.startDrag();
}
protected function button1_mouseUpHandler(event:MouseEvent):void
{
objectUserGrabbed.stopDrag();
}
protected function windowedapplication1_creationCompleteHandler(event:FlexEvent):void
{
optimalDistanceUpdateTimer = new Timer(33);
optimalDistanceUpdateTimer.addEventListener(TimerEvent.TIMER, optimalDistanceTickedHandler);
optimalDistanceUpdateTimer.start();
}
private function optimalDistanceTickedHandler(event:TimerEvent):void
{
var targetPoint:Point;
var deltaX:Number;
var deltaY:Number;
if(!objectUserGrabbed)
return;
if(objectUserGrabbed == button1)
{
//Basically here I'm playing of Pythagorean theorem for a 45 degree triangle the x component and the y component are the distance of the hypotenuse over Sqrt(2) for different offsets, radially you'd need to adjust this to compute the target point for each object... would do something like var angleForEach:Number = 360/numObjects; then figure out the target point for each of the objects in a loop var angleForThis = angleForEach*index; (in this example only two objects, no loop needed)
targetPoint = new Point(objectUserGrabbed.x-optimalDistance/Math.SQRT2, objectUserGrabbed.y-optimalDistance/Math.SQRT2);
deltaX = (targetPoint.x-button2.x);
deltaY = (targetPoint.y-button2.y);
button2.move(button2.x+deltaX/delayDenominator, button2.y+deltaY/delayDenominator);
}
else
{
targetPoint = new Point(objectUserGrabbed.x+optimalDistance/Math.SQRT2, objectUserGrabbed.y + optimalDistance/Math.SQRT2);
deltaX = (targetPoint.x-button1.x);
deltaY = (targetPoint.y-button1.y);
button1.move(button1.x+deltaX/delayDenominator, button1.y+deltaY/delayDenominator);
}
lineDrawingArea.graphics.clear();
lineDrawingArea.graphics.lineStyle(1);
lineDrawingArea.graphics.moveTo(button1.x + button1.width/2,button1.y + button1.height/2);
lineDrawingArea.graphics.lineTo(button2.x + button2.width/2,button2.y + button2.height/2);
}
]]>
</fx:Script>
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>
<s:Group id="lineDrawingArea"/>
<s:Button id="button1" label="Item 1" mouseDown="button1_mouseDownHandler(event)" mouseUp="button1_mouseUpHandler(event)" x="0" y="0"/>
<s:Button id="button2" label="Item 2" mouseDown="button1_mouseDownHandler(event)" mouseUp="button1_mouseUpHandler(event)" x="0" y="0"/>
</s:WindowedApplication>
I'm not sure if that title is descriptive enough, but here is the basic issue. I have a spark.effects.Animate instance repeating n times with a listener on the EFFECT_REPEAT event. In that event's listener, I'm incrementing a variable. Logic would dictate that if the variable started at 0 and the animation was played with a repeatCount of 3, the variable would be 2 when finished. This works fine, until I focus a different tab. When doing this, on occasion, the repeat event isn't fired/handled (or the animation is just not actually doing the animation) and my count isn't correct when finished.
This is a fully functioning example built against Flex 4.5. To duplicate just click the GO button and maintain focus on the TAB. Then click it again and switch tabs and switch back. The counts don't match.
How can I fix this so that the repeat event is called consistently?
<?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" width="600" height="400" applicationComplete="application1_applicationCompleteHandler(event)">
<fx:Script>
<![CDATA[
import mx.controls.Image;
import mx.events.EffectEvent;
import mx.events.FlexEvent;
import spark.effects.Animate;
import spark.effects.animation.MotionPath;
import spark.effects.animation.SimpleMotionPath;
private var animate:Animate;
private var someCounter:int = 0;
protected function application1_applicationCompleteHandler(event:FlexEvent):void
{
// Create the graphic
var img:Image = new Image();
img.source = "http://www.google.com/intl/en_com/images/srpr/logo3w.png";
// Add the graphic
addElement( img );
// Create the motion path
var smp:SimpleMotionPath = new SimpleMotionPath();
smp.property = "x";
smp.valueFrom = 0;
smp.valueTo = this.width - 275;
// Add the motion path to a Vector
var paths:Vector.<MotionPath> = new Vector.<MotionPath>();
paths.push( smp );
// Create the animation
animate = new Animate();
animate.easer = null;
animate.target = img;
animate.duration = 150;
animate.repeatCount = 15;
animate.motionPaths = paths;
animate.addEventListener( EffectEvent.EFFECT_REPEAT, animate_effectRepeatHandler );
animate.addEventListener( EffectEvent.EFFECT_END, animate_effectEndHandler );
}
private function animate_effectRepeatHandler( event:EffectEvent ):void
{
lblCounter.text = someCounter.toString();
someCounter++;
}
private function animate_effectEndHandler( event:EffectEvent ):void
{
lblCounter.text = someCounter.toString(); // Sometimes doesn't equal animate.repeatCount - 1
}
protected function button1_clickHandler(event:MouseEvent):void
{
if( !animate.isPlaying )
{
someCounter = 0;
animate.play();
}
}
]]>
</fx:Script>
<mx:Label id="lblCounter" horizontalCenter="0" bottom="50" width="150" height="50" textAlign="center" />
<mx:Button horizontalCenter="0" bottom="0" label="GO" width="150" height="50" click="button1_clickHandler(event)" />
</s:Application>
I'm quite new to Flex and ActionScript, and I'm trying to do an application whose purpose is simply to be able to dynamically create rectangles on a panel: on mouse down the rectangle is created and updated on mouse move events (left button hold down), then the rectangle is achieved with mouse up.
<?xml version="1.0" encoding="utf-8"?>
<s:WindowedApplication xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
width="550" height="400"
creationComplete="this.onCreationComplete(event);">
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>
<fx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
import mx.controls.Alert;
import mx.events.FlexEvent;
import mx.graphics.SolidColor;
import spark.primitives.Graphic;
import spark.primitives.Rect;
public static const WIDTH:int = 480;
public static const HEIGHT:int = 300;
public static const BACKGROUND:int = 0xEFEFEF;
public static const CURRENT_FILL:SolidColor = new SolidColor(0x00AA00, 0.75);
protected var rectangles:ArrayCollection = null;
protected var currentRect:Rect = null;
protected var dragging:Boolean = false;
protected function onCreationComplete(event:FlexEvent):void
{
this.rectangles = new ArrayCollection();
this.sprite.graphics.beginFill(BACKGROUND);
this.sprite.graphics.drawRect(0, 0, this.sprite.width, this.sprite.height);
this.sprite.graphics.endFill();
this.sprite.addEventListener(MouseEvent.MOUSE_DOWN, this.onMouseDown);
this.sprite.addEventListener(MouseEvent.MOUSE_UP, this.onMouseUp);
}
protected function onMouseDown(event:MouseEvent):void
{
this.currentRect = new Rect();
this.currentRect.y = 10;
this.currentRect.height = this.sprite.height - (10 * 2);
this.currentRect.x = event.localX;
this.currentRect.width = 1;
this.panel.addElement(this.currentRect);
this.dragging = true;
this.sprite.addEventListener(MouseEvent.MOUSE_MOVE, this.onMouseMove);
}
protected function onMouseUp(event:MouseEvent):void
{
if (this.dragging)
{
this.sprite.removeEventListener(MouseEvent.MOUSE_MOVE, this.onMouseMove);
this.rectangles.addItem(this.currentRect);
this.dragging = false;
}
}
protected function onMouseMove(event:MouseEvent):void
{
if (this.dragging)
{
this.currentRect.width = event.localX - this.currentRect.x;
}
}
]]>
</fx:Script>
<s:Panel id="panel" x="10" y="10" title="Calendar">
<s:SpriteVisualElement id="sprite" width="{WIDTH}" height="{HEIGHT}" />
</s:Panel>
</s:WindowedApplication>
All works fine but now I want to do some actions when the user clicks (or double clicks, or what you want) on a rectangle. I tried to add an event on each rectangle (this.currentRect.addEventListener(MouseEvent.DOUBLE_CLICK, myMethod) added in onMouseUp), but the corresponding method is never executed...
What's the problem?
Objects drawn with the graphics API are not event dispatchers. So use addElement to add the Spark primitive rects that you're creating but not using in any way (to a container such a Group) instead of using the graphics api to draw the shapes.
You might find it's better, however, to simply have the ArrayCollection contain a bunch of bindable data objects and use a DataGroup with itemRenderers to display the data.
You could keep track of the rectangles you have drawn in a collection and do a hit test with the rectangles on mouse clicks and determine if any rectangle is being clicked on.
I am creating a tabNavigator object in mxml.
TabNavigator contains navigatorContent objects.
Each NavigatorContent further contains multiple hGroups with three radio buttons in it.
All these elements are populated in the tabNavigator via actionScript code dynamically.
I need to select the second radio button within an hgroup, but am not sure how to do this.
<mx:TabNavigator id="tabNav" height="100" width="500" creationComplete="init();" creationPolicy="all">
</mx:TabNavigator>
private function init():void
{
for(var i:int=0;i<=int(arrColl_items[arrColl_items.length - 1][1]);
i++)
{
//after reading from xml var navContent:NavigatorContent = new NavigatorContent();
for (var j:int=0;j<arrColl_items.length;j++)
{
if(arrColl_items[j][1] == i)
{
var hgrp:HGroup = new HGroup();
hgrp.id = String(arrColl_items[j][0]);
var rdBut1:RadioButton=new RadioButton();
hgrp.addElement(rdBut1);
rdBut1.addEventListener(MouseEvent.CLICK, setrdBut1);
var rdBut2:RadioButton=new RadioButton();
hgrp.addElement(rdBut2);
rdBut2.addEventListener(MouseEvent.CLICK, setrdBut2);
var rdBut3:RadioButton=new RadioButton();
hgrp.addElement(rdBut3);
rdBut3.addEventListener(MouseEvent.CLICK, setrdBut3);
}
}
navContent.addElement(hgrp);
tabNav.addChildAt(navContent,i);
}
}
Can anyone please help out on this?
Regards
Aparna
you did not post the data structure, i hope i got it right. The following demo creates and populates a TabNavigator with three NavigatorContent instances containing series of RadioButtons that can be selected using the method 'getHGroupById'.
I am sure it is possible to realize something like this using Bindable Data and ItemRenderes, probably the better solution.
*Nicholas
The 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">
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>
<mx:TabNavigator id="tabNav" height="300" width="300" creationComplete="__init();" creationPolicy="all">
</mx:TabNavigator>
<fx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
import spark.components.NavigatorContent;
import spark.components.VGroup;
/**
* The Data Structure, as I understood it
* perhaps you have to adapt the code if
* this does not match your desired structure
* or hierarchy
* */
private var arrColl_items:ArrayCollection = new ArrayCollection(
[
new ArrayCollection(
// 1st NavigatorContent
[
{id:"0",label:"HGroup_1.1"},
{id:"1",label:"HGroup_1.2"}
]
),
new ArrayCollection(
// 2nd NavigatorContent
[
{id:"2",label:"HGroup_2.1"},
{id:"3",label:"HGroup_2.2"},
{id:"4",label:"HGroup_2.3"}
]
),
new ArrayCollection(
// 3rd NavigatorContent
[
{id:"5",label:"HGroup_3.2"},
{id:"6",label:"HGroup_3.3"},
{id:"7",label:"HGroup_3.4"},
{id:"8",label:"HGroup_3.5"},
{id:"9",label:"HGroup_3.5"}
]
)
]
);
/**
* Invoked on creationComplete
* */
private function __init():void
{
// lets add some navigatorContent
for(var i:int=0;i<arrColl_items.length;i++){
// create the navigatorContent
var navContent:NavigatorContent = new NavigatorContent();
navContent.label = "navContent_"+i;
// add a VGroup
var vgrp:VGroup = new VGroup();
vgrp.id = "vGroup";
// and now we add custom HGroup Components called MyHGroup
for (var j:int=0;j<arrColl_items[i].length;j++)
{
var hgrp:MyHGroup = new MyHGroup();
hgrp.id = arrColl_items[i][j].id;
hgrp.label.text = arrColl_items[i][j].label;
vgrp.addElement(hgrp);
}
navContent.addElement(vgrp);
tabNav.addElementAt(navContent,i);
}
// Accessing the radioButtons is now possible
// using the getHGroupById Method
getHGroupById("0").rb1.selected = true;
getHGroupById("1").rb2.selected = true;
getHGroupById("3").rb3.selected = true;
getHGroupById("7").rb1.selected = true;
// I added a RadioGroup within MyHGroup, lets read the selectedValue
trace(getHGroupById("0").rbGroup.selectedValue);
}
/**
* getHGroupById
* Method that runs through the Data Structure
* and returns a MyHGroup Class with the given id
* #param $id:String The id of the MyHGroup Class you want to fetch
* #return MyHGroup or null if non existent
*
* */
public function getHGroupById($id:String):MyHGroup{
// running through the NavigatorContent Instances
for(var i:uint=0; i<tabNav.numElements; i++){
var navContent:NavigatorContent = NavigatorContent(tabNav.getElementAt(i));
// running through the HGroups within a VGroup
for(var j:uint=0; j<VGroup(navContent.getElementAt(0)).numElements; j++){
var hgrp:MyHGroup = VGroup(navContent.getElementAt(0)).getElementAt(j) as MyHGroup;
if(hgrp.id==$id)return hgrp;
}
}
return null;
}
]]>
</fx:Script>
</s:Application>
Finally the MyHGroup Component used in this example. Create a new MXML File name MyHGroup.mxml and paste the following code.
<?xml version="1.0" encoding="utf-8"?>
<!-- Create a new MXML File name MyHGroup and add this code -->
<s:HGroup xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx" width="200" height="20" paddingLeft="10" >
<fx:Declarations>
<!-- The RadioGroup the Buttons will be linked with -->
<s:RadioButtonGroup id="rbGroup"/>
</fx:Declarations>
<!-- Some fancy label -->
<s:Label id="label" height="20" verticalAlign="middle"/>
<!-- Your Radio Buttons -->
<s:RadioButton id="rb1" group="{rbGroup}"/>
<s:RadioButton id="rb2" group="{rbGroup}"/>
<s:RadioButton id="rb3" group="{rbGroup}"/>
</s:HGroup>