Flex chart with multiple series and multiple axes giving weird results - apache-flex

There is a serious issue I am facing with flash builder charts.
When I load the chart the first time I get this -
But if I refresh the page immediately in the same browser, I get this -
The second one is the expected one. This issue just repeats if I close the browser window and open it in a new browser window.
For explaining properly, I am attaching the source code here -
<?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:defaultstatistics="services.defaultstatistics.*"
xmlns:mx="library://ns.adobe.com/flex/mx" width="800" height="800"
xmlns:Examples="Examples.*"
creationComplete="start();" >
<!--<fx:Style source="styles/Golibaje.css"/>-->
<fx:Script>
<![CDATA[
import CustomEvents.ChartLoadedEvent;
import mx.collections.ArrayCollection;
import mx.controls.Alert;
import mx.rpc.events.ResultEvent;
import mx.utils.object_proxy;
import org.axiis.core.AxiisSprite;
import org.axiis.data.DataSet;
private var ds:DataSet = new DataSet();
public var loaded:Boolean = false;
//Set this variable to the appropriate time period used by the data.
[Bindable]
public var timeIncrement:String = "month";
[Bindable]
private var performanceData:ArrayCollection = new ArrayCollection([
{month:"January", correct:100, correctOutsideBase:50,
incorrect:50, skipped:25},
{month:"February", correct:110, correctOutsideBase:40,
incorrect:50, skipped:30},
{month:"March", correct:120, correctOutsideBase:40,
incorrect:25, skipped:30},
{month:"April",correct:110, correctOutsideBase:45,
incorrect:20, skipped:20},
{month:"May", correct:135, correctOutsideBase:25,
incorrect:25, skipped:15},
{month:"June", correct:150, correctOutsideBase:15,
incorrect:15, skipped:5},
{month:"July", correct:155, correctOutsideBase:10,
incorrect:10},
{month:"August", correct:160, correctOutsideBase:5,
incorrect:15},
{month:"September",correct:170, correctOutsideBase:5,
incorrect:10}
]);
[Bindable]
private var pointsData:ArrayCollection = new ArrayCollection([
{month:"January", points:1000},
{month:"February", points:1200},
{month:"March", points:1400},
{month:"April",points:1500},
{month:"May", points:1600},
{month:"June", points:1650},
{month:"July", points: 1800},
{month:"August", points: 2000},
{month:"September", points: 2500}
]);
public function start():void
{
var t:Timer = new Timer(2000, 1);
t.addEventListener(TimerEvent.TIMER_COMPLETE, fireChartLoaded);
t.start();
}
private function fireChartLoaded(event){
loaded = true;
dispatchEvent(new Event("chartLoaded"));
}
]]>
</fx:Script>
<fx:Declarations>
</fx:Declarations>
<mx:CartesianChart id="PerformanceTimeChart" dataProvider="{performanceData}"
showDataTips="true" width="800" height="700"
verticalCenter="0" horizontalCenter="0">
<mx:verticalAxisRenderers>
<mx:AxisRenderer placement="left" axis="{columnAxis}"/>
<mx:AxisRenderer placement="right" axis="{pointsAxis}"/>
</mx:verticalAxisRenderers>
<mx:horizontalAxis>
<mx:CategoryAxis categoryField="{timeIncrement}"/>
</mx:horizontalAxis>
<mx:series>
<mx:ColumnSet type="stacked">
<mx:verticalAxis>
<mx:LinearAxis id="columnAxis" title="Result"/>
</mx:verticalAxis>
<mx:ColumnSeries
yField="correct"
displayName="Correct"
verticalAxis="{columnAxis}"/>
<mx:ColumnSeries
yField="correctOutsideBase"
displayName="Outside Base"
verticalAxis="{columnAxis}"/>
<mx:ColumnSeries
yField="incorrect"
displayName="Wrong Answer"
verticalAxis="{columnAxis}"/>
<mx:ColumnSeries
yField="skipped"
displayName="Skipped Question"
verticalAxis="{columnAxis}"/>
</mx:ColumnSet>
<mx:LineSeries
dataProvider="{pointsData}"
yField="points"
displayName="Points">
<mx:verticalAxis>
<mx:LinearAxis id="pointsAxis" title="Points"/>
</mx:verticalAxis>
</mx:LineSeries>
</mx:series>
</mx:CartesianChart>
<mx:Legend dataProvider="{PerformanceTimeChart}" direction="horizontal"/>
</s:Group>
Anyone has any ideas why this might be happening?

When ColumnSet are used within CartesianChart, there is a bug in Flex Charting SDK regarding setting elements to dataTranform object of ColumnSeries embraced by ColumnSet.
As a workaround for this issue, I set elements by myself to dataTransform object in extended ColumnSeries class:
public class GroupedColumnSeries extends ColumnSeries
{
override protected function commitProperties():void
{
super.commitProperties();
if (this.parent is ColumnSet)
{
dataTransform.elements=[ColumnSet(this.parent)];
}
}
}

Related

Custom field in flex bar chart

I am using floating bar charts to represent schedule in flex. Data is defined like this:
public var schedule:ArrayCollection = new ArrayCollection([
{task:"task1",start:6,end:9},
{task:"task2",start:11,end:12},
{task:"task3",start:14,end:17}
]);
start and end here stands for the time that task begins and finishes.
My question is the xField (time) always start from 0, so that I will have quite a large empty space and the chart doesn't look nice. I want it start from 6, as the start time of task "task1". How can I do it? Please help.
Just use the properties "minimum" and "maximum" of LinearAxis. To get the actual min und max values you can search them in the data provider.
<?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.events.FlexEvent;
[Bindable]public var schedule:ArrayCollection = new ArrayCollection([
{task:"task1", start:6, end:9},
{task:"task2", start:11, end:12},
{task:"task3", start:14, end:17}
]);
[Bindable]private var minTime:Number = 24;
[Bindable]private var maxTime:Number = 0;
protected function init():void
{
for each (var task:Object in schedule)
{
if (task.start < minTime)
minTime = task.start;
if (task.end > maxTime)
maxTime = task.end;
}
}
]]>
</fx:Script>
<mx:BarChart
dataProvider="{schedule}"
showDataTips="true">
<mx:verticalAxis>
<mx:CategoryAxis
dataProvider="{schedule}"
categoryField="task"/>
</mx:verticalAxis>
<mx:horizontalAxis>
<mx:LinearAxis minimum="{minTime}" maximum="{maxTime}"/>
</mx:horizontalAxis>
<mx:series>
<mx:BarSeries
xField="end"
minField="start"/>
</mx:series>
</mx:BarChart>
</s:Application>

Timeline in 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>

Problem with displaying AS3 code in Flex

I have AS3 script bitfade.text.steel returning type Sprite. It works in Flash environment but not in FLEX. In Flex it displays nothing. No errors appear. It looks like the attached code also works. I use Flex SDK 4.5
Can you give me a clue what the problem with this code might be?
English is not my native language.
If there are any errors please correct me.
Chris
<?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" minWidth="955" minHeight="600"
creationComplete="creationComplete();" >
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>
<fx:Script>
<![CDATA[
import bitfade.text.steel;
import flash.display.Sprite;
import mx.core.UIComponent;
function creationComplete(){
var textSteel = new steel("config.xml");
if(textSteel is Sprite){
trace("it is a sprite");
}
//var textSteelName:String = getQualifiedClassName(textSteel);
//trace(textSteelName);
textSteel.x = 150;
trace("this is visible on the screen");
var sprite:Sprite = new Sprite();
sprite.graphics.beginFill(0xFFCC00);
sprite.graphics.drawCircle( 40, 40, 40 );
sprite.graphics.endFill();
var wrapper:UIComponent = new UIComponent();
wrapper.addChild(sprite);
wrapper.addChild(textSteel);
animationStage.addElement(wrapper);
}
]]>
</fx:Script>
<s:Group id="animationStage" visible="true" x="50" y="50">
<s:Label text="test">
</s:Label>
</s:Group>
</s:WindowedApplication>
If I switch your custom class to a Sprite, and draw something on it, it shows up. Therefore I Believe there is a bug within your steel class. A sprite won't have any size until something is inside it with a size. There is no indication of the code you've shown what happens inside your steel class.
My sample:
<?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="creationComplete();">
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>
<fx:Script>
<![CDATA[
// import bitfade.text.steel;
import flash.display.Sprite;
import mx.core.UIComponent;
public function creationComplete():void{
var textSteel : Sprite = new Sprite();
if(textSteel is Sprite){
trace("it is a sprite");
}
//var textSteelName:String = getQualifiedClassName(textSteel);
//trace(textSteelName);
textSteel.x = 150;
// JH Code added new
textSteel.graphics.beginFill(0xFFCC00);
textSteel.graphics.drawRect(0,0,100,100);
textSteel.graphics.endFill();
trace("this is visible on the screen");
var sprite:Sprite = new Sprite();
sprite.graphics.beginFill(0xFFCC00);
sprite.graphics.drawCircle( 40, 40, 40 );
sprite.graphics.endFill();
var wrapper:UIComponent = new UIComponent();
wrapper.addChild(sprite);
wrapper.addChild(textSteel);
animationStage.addElement(wrapper);
}
]]>
</fx:Script>
<s:Group id="animationStage" visible="true" x="50" y="50">
<s:Label text="test">
</s:Label>
</s:Group>
</s:WindowedApplication>
I think you need to set the width and height of the wrapper UIComponent instance.
UIComponents don't automatically resize to their content so it's showing up with a width and height of 0.

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.

Resources