Timeline in Flex - apache-flex

Trying to build a timeline in Flex, Have a horizontal slider when i slide the slider there should be a line on the linechart as a needle moving with respect to value of slider,I Tried using gridlines to achieve this but the grid lines are visible across all the values of x axis,But I want to show only single gridline with respect to value of slider. Is there a way to hide few grid lines and show specific gridlines.

Here's something I created in about 30 minutes. It's very rudimentary, and has some issues that I will leave for you to solve (or you can post a new question specific to the remaining issues). It's likely you'll need to modify this to suit your application anyway, so I didn't bother looking at the remaining issues.
I've assumed you're using Date objects for the horizontal axis of the chart. As such you need to convert the date object to it's corresponding numerical value (in epoch time). This allows you to work with the slider component, which expects numbers.
If you do have further questions, I recommend trying to post whatever code you're using (or a simple version of it), that others can run. That way the answers you get will be specific to your case, and not generic like this one :)
<?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"
creationComplete="onCreationComplete()">
<fx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
import spark.primitives.Line;
[Bindable] private var chartData : ArrayCollection =
new ArrayCollection(
[
{ sales: 101000, month: new Date( '01/01/2013' ) },
{ sales: 350000, month: new Date( '02/01/2013' ) },
{ sales: 475000, month: new Date( '03/01/2013' ) },
{ sales: 425000, month: new Date( '04/01/2013' ) }
] );
private var line:Line;
private function onCreationComplete():void
{
line = new Line();
line.height = chart.height;
line.stroke = new SolidColorStroke(0x0000FF, 2);
chartContainer.addElement(line); }
private function getDateInEpochTime(date:Date):Number
{
return date.time;
}
private function dataTipFormatFunction(value:Number):Object
{
return new Date(value).toString();
}
private function onSliderChange():void
{
line.x = convertSliderValueToXCoordinate();
}
private function convertSliderValueToXCoordinate():Number
{
var min:Number = slider.minimum;
var max:Number = slider.maximum;
var adjustedValue:Number = slider.value - min;
var range:Number = max - min;
var percentOfRange:Number = adjustedValue/range
var xCoordinate:Number = slider.width * percentOfRange;
var thumbWidth:Number = slider.thumb.width;
if (percentOfRange > .5)
xCoordinate = xCoordinate - (thumbWidth * (percentOfRange - .5));
else if (percentOfRange < .5)
xCoordinate = xCoordinate + (thumbWidth * (.5 - percentOfRange));
return xCoordinate;
}
]]>
</fx:Script>
<s:Group id="chartContainer" width="800" height="600">
<mx:LineChart id="chart" dataProvider="{chartData}" left="0" right="0" top="0" bottom="20">
<mx:horizontalAxis>
<mx:DateTimeAxis id="hAxis" dataUnits="months" alignLabelsToUnits="true" displayLocalTime="true"/>
</mx:horizontalAxis>
<mx:series>
<mx:LineSeries displayName="Sales by Month" yField="sales" xField="month">
<mx:lineStroke>
<s:SolidColorStroke color="0xFF0000" />
</mx:lineStroke>
</mx:LineSeries>
</mx:series>
</mx:LineChart>
<s:HSlider id="slider" left="0" right="0" bottom="0"
dataTipFormatFunction="dataTipFormatFunction"
minimum="{getDateInEpochTime(chartData.getItemAt(0).month)}"
maximum="{getDateInEpochTime(chartData.getItemAt( chartData.length -1 ).month)}"
change="onSliderChange()"/>
</s:Group>
</s:Application>

Related

resize video child of VideoDisplay to the same size

i have the following code to play a video stored in the media server:
<?xml version="1.0" encoding="utf-8"?>
<s:Group 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="592" height="372">
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>
<fx:Script>
<![CDATA[
import mx.events.ResizeEvent;
private var ns:NetStream;
private var nc:NetConnection;
private var video:Video;
private var meta:Object;
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.
myVid.width = video.width;
myVid.height = video.height;
}
private function fetch_rec():void {
var nsClient:Object = {};
nsClient.onMetaData = ns_onMetaData;
nc = new NetConnection();
nc.connect(null);
ns = new NetStream(nc);
ns.client = nsClient;
video = new Video();
video.attachNetStream(ns);
video.smoothing=true;
myVid.addChild(video);
ns.play("http://localhost:5080/oflaDemo/recordings/firstattempt.flv");
}
]]>
</fx:Script>
<mx:VideoDisplay id="myVid" bottom="0" width="100%" height="100%"
/*??how to make the progressbar work???*/ playheadUpdate="progBar.setProgress(myVid.playheadTime,myVid.totalTime)"/>
<s:HGroup right="10" top="7">
<mx:Button id="button4" x="498" y="250" label="Play/Reload" click="fetch_rec();"/>
<mx:Button id="button5" x="512" y="279" label="Pause" click="ns.pause()"/>
</s:HGroup>
<mx:ProgressBar id="progBar" left="10" right="10" bottom="7" height="10" label="" mode="manual"/>
</s:Group>
and i would like to know
1)how to make the video follow the resizing of the myVid VideoDisplay and does not show small inside it as it does and
2)how to make the progressbar work with the streamed video...
Thanks in advance for your help!
I can't answer for progress bar. For scaling video to video display I use Transform and Matrix, when VideoDisplay size is changed I just replace old Matrix with new scaled one. If your video will be smaller then target then it should expand.
Here are snippets of code (I skip creation, etc):
<!-- language: lang-xml -->
protected function scaleVideoToDisplay():void {
var ratio:Number;
if (video == null) {
return;
}else if (video.width == 0 || video.height == 0) {
ratio = 0.0;
}else {
ratio = Math.min(
videoDisplay.width / video.width,
videoDisplay.height / video.height);
}
video.transform.matrix = new Matrix(ratio, 0, 0, ratio);
}
<s:VideoDisplay width="100%" height="100%" resize="scaleVideoToDisplay()"/>
<!-- langugae:lang-none -->
For video creation I use:
<!-- language: lang-xml -->
video.transform = new Transform(video);
In addition to this You will probably need to scale video after creation.

Displaying black and red ToolTips as speech baloons

I'm porting a card game from pure Flash/AS3 to Flex 4.5:
I'm almost done, but the "speech baloons" marked by the blue color in the screenshot above are missing.
Those "speech baloons" fade in, display red (if they contain hearts or diamonds char) or black text and finally fade out.
I'm trying to implement those as mx.controls.ToolTips and have prepared a simple test case, where 3 users are represented by smiley-buttons and you can push a "Talk"-button to make them talk:
<?xml version="1.0"?>
<s:Application
xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:mx="library://ns.adobe.com/flex/mx"
xmlns:s="library://ns.adobe.com/flex/spark"
width="400" height="300"
initialize="init();">
<fx:Declarations>
<s:Fade id="fadeIn" alphaFrom="0" alphaTo="1" duration="2000"/>
</fx:Declarations>
<fx:Script>
<![CDATA[
import mx.managers.ToolTipManager;
private var i:uint = 0;
private function init():void {
ToolTipManager.enabled = false;
ToolTipManager.showEffect = fadeIn;
}
private function talk():void {
var str:String = 'Me plays 10' + (i % 2 ? '♥' : '♠');
// this does not make the ToolTip appear?
this['user' + (++i % 3)].toolTip = str;
// how to set color according to isRed(str)?
}
private function isRed(str:String):Boolean {
return (str.indexOf('♦') > 0 || str.indexOf('♥') > 0);
}
]]>
</fx:Script>
<s:Button id="user0" horizontalCenter="0" bottom="0" label=":-)" />
<s:Button id="user1" left="0" top="0" label=":-)" />
<s:Button id="user2" right="0" top="0" label=":-)" />
<s:Button right="0" bottom="0" label="Talk!" click="talk()" />
</s:Application>
Can anybody please give me hints?
How to make ToolTips appear at will? (and not just on mouse hover)
How to change their color (I only found how to set it once by CSS)
UPDATE:
I've tried the following
private var tip0:ToolTip;
private var tip1:ToolTip;
private var tip2:ToolTip;
private function talk():void {
var str:String = 'Me plays 10' + (++i % 2 ? '♥' : '♠');
var btn:Button = this['user' + (i % 3)];
var tip:ToolTip = this['tip' + (i % 3)];
tip = ToolTipManager.createToolTip(str, btn.x + 10, btn.y + 10, "errorTipBelow", IUIComponent(btn)) as ToolTip;
}
but this does not work too well - no effects, no disappearing (I guess I have to call destroyToolTip myself). I wonder if ToolTip can be (ab)used for my purpose of representing "speech baloons" in an elegant way at all...
Personally, I have found the tool tip system rather limiting, and any time I want to do something a bit more different, it just seems easier to implement it manually. Generally in this case I would add a PopUpAnchor control to the components that need these overlay displays. Then you have full manual control over what is shown, and exactly how it is shown.
http://blog.flexexamples.com/category/spark/popupanchor-spark/
There are quite a few ways to do this though as well as just building the tooltip component as a subclass of Group, adding it as a child, and keeping track of it.

LineChart data tip won't display with single data point?

In Flex 3, I have a line chart.
My data provider for the line chart contains only one item.
When I draw the line chart, it plots the point. But it doesn't show the data tip.
It works if more than one item is present in dataprovider.
How can I make a data tip visible for line chart with dataprovider containing a single data item?
Use PlotSeries instead of LineSeries when you only have one point. You will get a nice round point with its dataTip. How to do it exactly depends on how your data is built - if the data doesn't change after being assigned to the dataProvider, you can choose which type of series to use at that moment.
try the following code,
in actionscript,
lineSeries.setStyle("itemRenderer", new ClassFactory(mx.charts.renderers.CircleItemRenderer));
in mxml,
<mx:LineSeries yField="Y" itemRenderer="mx.charts.renderers.CircleItemRenderer" xField="X" dataProvider="{lineDataProvider}">
There is a workaround to show the datatip. We need to add ROLL_OVER and ROLL_OUT mouse event listeners to the line series which has single datapoint.
<?xml version="1.0"?>
<!-- Simple example to demonstrate the LineChart and AreaChart controls. -->
<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">
<fx:Script>
<![CDATA[
import mx.charts.HitData;
import mx.charts.renderers.*;
import mx.charts.series.items.LineSeriesItem;
import mx.collections.ArrayCollection;
[Bindable]
private var expensesAC:ArrayCollection = new ArrayCollection( [
{ Month: "Jan", Profit: 2000 } ]);
private function lineseriesRollOverHandler(event:MouseEvent):void
{
linechart.showAllDataTips = true;
}
private function lineserieRollOutHandler(event:MouseEvent):void
{
linechart.showAllDataTips = false;
}
private function dataTipFunction(hitData:HitData):String
{
if(hitData && hitData.item)
{
var s:String = "";
if(hitData.element is LineSeries)
{
if(expensesAC.length <=1)
hitData.x = 56;
var lsi:LineSeriesItem = hitData.chartItem as LineSeriesItem;
if(lsi == null)
return "";
s += "<b>" + (hitData.element as LineSeries).displayName + "</b><br />";
s += lsi.xValue + "<br />";
s += lsi.yNumber;
}
return s;
}
return "";
}
]]>
</fx:Script>
<fx:Declarations>
</fx:Declarations>
<mx:Panel width="100%" height="100%" layout="horizontal" title="Single point LineChart Example">
<mx:LineChart id="linechart" width="45%" height="100%" dataProvider="{expensesAC}"
paddingLeft="5" paddingRight="5" showDataTips="true" dataTipFunction="dataTipFunction">
<mx:horizontalAxis>
<mx:CategoryAxis categoryField="Month"/>
</mx:horizontalAxis>
<mx:series>
<mx:LineSeries displayName="Profit" form="curve"
itemRenderer="mx.charts.renderers.CircleItemRenderer"
legendMarkerRenderer="mx.charts.renderers.BoxItemRenderer"
rollOut="lineserieRollOutHandler(event)"
rollOver="lineseriesRollOverHandler(event)" yField="Profit"/>
</mx:series>
</mx:LineChart>
<mx:Legend dataProvider="{linechart}"/>
</mx:Panel>
</s:Application>

PopUpButton with TileList and custom renderer

I have prepared a simple test case for a PopUpButton opening a TileList with black and red entries and it mostly works, but has 2 annoyances.
I've searched a lot, tried several variants (added [Bindable] members in my renderer; added color member to the bids array; created my public override set data() method; ...) and has been getting some answers too, but they are way too general.
I would appreciate if someone can suggest code to fix the 2 issues in my code:
1) Scrolling "tl2" right-left doesn't work well: the entries are displayed in a mix of red and black. I know the TileList reuses itemRenderer, but how do I fix the problem?
2) In debug-mode I get numerous warnings:
warning: unable to bind to property 'label' on class 'Object' (class is not an IEventDispatcher)
Thank you,
Alex
MyRenderer.mxml:
<?xml version="1.0" encoding="utf-8"?>
<mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml"
verticalScrollPolicy="off" horizontalScrollPolicy="off"
width="100%" height="100%">
<mx:Script>
<![CDATA[
public static function findColor(str:String):uint {
return (str.indexOf('♥') != -1 ||
str.indexOf('♦') != -1) ? 0xFF0000 : 0x000000;
}
]]>
</mx:Script>
<mx:Label truncateToFit="true" width="60"
text="{data.label}" color="{findColor(data.label)}"/>
</mx:Canvas>
MyTest.mxml:
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
creationPolicy="all" applicationComplete="init(event);">
<mx:Style>
#font-face {
src:url("C:\\WINDOWS\\Fonts\\arial.ttf");
fontFamily: myFont;
unicodeRange:
U+0020-U+0040, /* Punctuation, Numbers */
U+0041-U+005A, /* Upper-Case A-Z */
U+005B-U+0060, /* Punctuation and Symbols */
U+0061-U+007A, /* Lower-Case a-z */
U+007B-U+007E, /* Punctuation and Symbols */
U+0410-U+0451, /* cyrillic */
U+2660-U+266B; /* card suits */
}
List, CheckBox, Label, Button, PopUpButton, TileList {
fontFamily: myFont;
fontSize: 24;
}
</mx:Style>
<mx:Script>
<![CDATA[
import mx.controls.*;
import mx.events.*;
[Bindable]
private var bids:Array;
private var tl:TileList;
private function init(event:FlexEvent):void {
bids = createBids();
pub.popUp = createList(bids);
}
private function createBids():Array {
var arr:Array = [{label: 'Pass'}];
for (var i:uint = 6; i <= 10; i++)
for (var j:uint = 0; j < 5; j++)
arr.unshift({label: i+'♠♣♦♥ '.charAt(j%5)});
return arr;
}
private function createList(arr:Array):TileList {
tl = new TileList();
tl.maxColumns = 5;
tl.width = 350;
tl.height = 250;
tl.dataProvider = arr;
tl.itemRenderer = new ClassFactory(MyRenderer);
tl.addEventListener('itemClick', itemClickHandler);
if (arr.length > 0) {
tl.selectedIndex = arr.length - 1;
pub.label = arr[tl.selectedIndex].label;
}
return tl;
}
private function itemClickHandler(event:ListEvent):void {
var index:uint = tl.columnCount * event.rowIndex + event.columnIndex;
var label:String = bids[index].label;
pub.label = label;
pub.setStyle('color', MyRenderer.findColor(label));
pub.close();
tl.selectedIndex = index;
}
]]>
</mx:Script>
<mx:Panel title="TileList scrolling problem" height="100%" width="100%"
paddingTop="10" paddingBottom="10" paddingLeft="10" paddingRight="10">
<mx:Label width="100%" color="blue" text="Select your bid:"/>
<mx:TileList id="tl2" height="200" width="200"
maxColumns="5" rowHeight="30" columnWidth="60"
dataProvider="{bids}" itemRenderer="MyRenderer"/>
</mx:Panel>
<mx:ApplicationControlBar width="100%">
<mx:Spacer width="100%"/>
<mx:CheckBox id="auto" label="Auto:"/>
<mx:Button id="left" label="<<"/>
<mx:PopUpButton id="pub" width="90"/>
<mx:Button id="right" label=">>"/>
</mx:ApplicationControlBar>
</mx:Application>
Update:
Thank you Wade, the warning is gone now (I guess it was not ok to use {data.label} in my label), but the "tl2" still has scrolling issues.
New MyRenderer.mxml (still has scrolling issues):
<?xml version="1.0" encoding="utf-8"?>
<mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml"
verticalScrollPolicy="off" horizontalScrollPolicy="off"
width="100%" height="100%">
<mx:Script>
<![CDATA[
override public function set data(value:Object):void {
super.data = value;
var str:String = String(value.label);
myLabel.text = str;
myLabel.setStyle('color', findColor(str));
}
public static function findColor(str:String):uint {
return (str.indexOf('♥') != -1 ||
str.indexOf('♦') != -1) ? 0xFF0000 : 0x000000;
}
]]>
</mx:Script>
<mx:Label id="myLabel" truncateToFit="true" width="60"/>
</mx:Canvas>
You can take care of both of your issues by overriding the set data method on your item renderer:
<?xml version="1.0" encoding="utf-8"?>
<mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml"
verticalScrollPolicy="off" horizontalScrollPolicy="off"
width="100%" height="100%">
<mx:Script>
<![CDATA[
override public function set data(value:Object):void {
super.data = value;
var str:String = value.label;
this.myLabel.text = str;
this.myLabel.setStyle("color", (str.indexOf('♥') != -1 ||
str.indexOf('♦') != -1) ? 0xFF0000 : 0x000000);
}
]]>
</mx:Script>
<mx:Label id="myLabel" truncateToFit="true" width="60"/>
</mx:Canvas>
Since the renderers are re-used, the best way to ensure they are correctly updated is to use the set data method since it always gets called when a renderer gets re-used. This also gets rid of your binding warning since you are no longer binding to data.label. Note: I haven't tested this code, it may need some tweaking :) Hope that helps.
EDIT: Your "tl2" issue looks like it's caused by horizontally scrolling your tile list, whereas the TileList appears to be optimized for vertical scrolling. Since your data set is finite and relatively small, I would make the tile list full size to show all of the elements (eliminating item renderer re-use) and wrap it in a canvas set to the desired dimensions and let the canvas handle the scrolling. Probably not the answer you are looking for, sorry.

Filtering data shown on linechart in flex

i am working on a line chart on flex which enable me to view the progress of data according to the year. I have tried using a slider to filter but it doesn't seemed to work. any help please?
i am not exactly filtering the dataprovider, but the alpha. My function will retrieve all the information from my array collection, but set the alpha to 0, so when user drags the slider, if the year falls below that particular year, it will display the data, which i then set the alpha to 100.
The data is there, the axis are all set, alpha is set to 0. but the problem is, it doesn't display the information line by line as what i wanted it to be, instead, it display the whole graph only until i drag the slider to the end...
these are my codes
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute">
<mx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
import mx.rpc.events.ResultEvent;
[Bindable]
public var expenses:ArrayCollection = new ArrayCollection([
{Year:"1990", Profit:2000 },
{Year:"1991", Profit:1000 },
{Year:"1992", Profit:1500 },
{Year:"1993", Profit:2100 },
{Year:"1994", Profit:2500 },
{Year:"1995", Profit:1500 },
{Year:"1996", Profit:1900 },
]);
private function init():void {
expenses.filterFunction = sliderFilterFunc;
expenses.refresh();
}
private function sliderFilterFunc(item:Object):Boolean{
var result:Boolean = true;
pro.alpha=0;
if(item.Year<=slider.value || item.Year==slider.value)
{
pro.alpha=100;
return result;
}
return result;
}
]]></mx:Script>
<mx:VBox horizontalCenter="0" top="10" horizontalAlign="center" height="100%">
<mx:HSlider id="slider" minimum="1990" maximum="1996" value="220" liveDragging="true" change="init()" width="570" snapInterval="1" dataTipPrecision="0" labels="['1990','1996']" tickInterval="1" themeColor="#000000" borderColor="#FFFFFF" fillAlphas="[1.0, 1.0, 1.0, 1.0]" fillColors="[#000000, #000000, #FFFFFF, #1400D1]" height="48" styleName="myDataTip"/>
<mx:Panel title="Line Chart with One Shadow">
<mx:LineChart id="myChart" dataProvider="{expenses}" showDataTips="true" >
<mx:seriesFilters>
<mx:Array/>
</mx:seriesFilters>
<mx:horizontalAxis>
<mx:CategoryAxis
dataProvider="{expenses}"
categoryField="Year"
/>
</mx:horizontalAxis>
<mx:series>
<mx:LineSeries id="pro" alpha="0"
yField="Profit"
displayName="Profit"
/>
</mx:series>
</mx:LineChart>
<mx:Legend dataProvider="{myChart}" />
</mx:Panel>
</mx:VBox>
</mx:Application>
sorry for the messiness.:(
You seem to be using dates as your x axis, the slider can "slide" between numeric values.
What I would do is make my expenses ArrayCollection to:
public var expenses:ArrayCollection = new ArrayCollection([
{Year: new Date(1990), Profit:2000 },
{Year: new Date(1991), Profit:1000 },
...
Then for your filter function:
private function sliderFilterFunc(item:Object):Boolean {
pro.alpha = item.Year.getTime() <= slider.value ? 100 : 0;
return true;
}
Also, are you sure you want to set the alpha to 0 instead of just filtering out the data points? If you would like to shrink your ArrayCollection (don't worry this shrinks the ArrayCollection, not the source, the Array), you could just do:
private function sliderFilterFunc(item:Object):Boolean {
return = item.Year.getTime() <= slider.value;
}
Finally, you should also set your own dataTipFunction for the slider so instead of seeing numbers they see the actual date.
i created a Flex Library (DataFilterLib) that take care of all the filtering process, completly in MXML.
This library is free, you can find the project details there:
http://code.google.com/p/flex-datafilterlib/
If you want to have a look at the examples, they are all in the project's page (source available):
Check the examples online if you want to see how to filter on multiple criterias, using different Flex UI Components (CheckBox, Slider, List, ...).
Using these filters with a Slider (2-thumbs), you can easily filter your data and it will be automatically reflected on your Chart.
Thanks,
Fabien

Resources