Friday, June 28, 2013

Flex: Playing with DataTip of a Chart

Not sure if any of you out there have been playing or using charts in Flex, but I have come across this simple looking problem the other day. The problem would be the border around the DataTip needs to be much more thicker. Although it might appear to be pretty easy, but it ended up to be a much more sophisticated problem.

Here's the source code of my main application - SimpleDataTipBorder.mxml
<?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="creationCompleteEvent(event)">
 <fx:Declarations>
  <!-- We are specifying the effect for the pie chart here. -->
  <mx:SeriesInterpolate id="selectEffect" 
         easingFunction="{Elastic.easeOut}"
         duration="1000" 
         effectEnd="effectEndEvent(event)"/>
 </fx:Declarations>
 <fx:Script>
  <![CDATA[
   import mx.charts.HitData;
   import mx.charts.events.ChartItemEvent;
   import mx.charts.series.items.PieSeriesItem;
   import mx.collections.ArrayCollection;
   import mx.effects.easing.Elastic;
   import mx.events.EffectEvent;
   import mx.events.FlexEvent;
   
   //Here's our data for the pie chart.
   [Bindable]
   public var expenses:ArrayCollection = new ArrayCollection([
    {Expense:"Taxes", Amount:2000},
    {Expense:"Rent", Amount:1000},
    {Expense:"Bills", Amount:100},
    {Expense:"Car", Amount:450},
    {Expense:"Gas", Amount:100},
    {Expense:"Food", Amount:200}
   ]);
   
   //A timer to reset the datatip after 1 sec.
   private var chartTimer:Timer = null;
   
   // Create a Array of explode radii.
   public var explodingArray:Array = [];
    
   protected function creationCompleteEvent(event:FlexEvent):void
   {
    //set the datatip renderer of the pie chart to the
    //custom datatip
    pie.setStyle("dataTipRenderer", CustomDataTip);
   }
   
   //Upon clicking on the pie chart, the selected chart item will
   //be explode from the pie chart.
   protected function pie_itemClickHandler(event:ChartItemEvent):void
   {
    var tempArray:Array = [];
    tempArray[event.hitData.id] = 0.2;
    pieSeries.perWedgeExplodeRadius = tempArray;
    
    //hide the datatip of the pie chart when it animates.
    pie.showDataTips = false;
    
    //refresh the data, which is needed to enforce the animation
    expenses.refresh();
   }
   
   //Show the datatip again after all the animations. This is
   //because if you enable the display of dataip of the chart
   //immediately, the color of the datatip will be wrong.
   protected function effectEndEvent(event:EffectEvent):void
   {
    if(chartTimer)
    {
     chartTimer.stop();
    }
    chartTimer = new Timer(1000,1);
    chartTimer.addEventListener(TimerEvent.TIMER, chartTimerEvent);
    chartTimer.start();
   }
   
   private function chartTimerEvent(event:Event):void
   {
    pie.showDataTips = true;
   }
   
  ]]>
 </fx:Script>
 <s:BorderContainer width="100%"
        height="100%">
  <s:VGroup verticalAlign="middle" 
      horizontalAlign="center"
      width="100%"
      height="100%">
   <s:VGroup>
    <mx:PieChart id="pie" 
        dataProvider="{expenses}"
        itemClick="pie_itemClickHandler(event)"
        showDataTips="true">
     <mx:series>
      <!--Apply the Array of radii to the PieSeries.-->
      <mx:PieSeries id="pieSeries"
           field="Amount" 
           nameField="Expense"  
           showDataEffect="selectEffect"
           labelPosition="callout"/>
     </mx:series>
    </mx:PieChart>
   </s:VGroup>   
  </s:VGroup>
 </s:BorderContainer>
</s:Application>
Here's the source code of my custom DataTip Class - CustomDataTip.as
package
{
 import flash.display.Graphics;
 import flash.geom.Rectangle;
 
 import mx.charts.chartClasses.DataTip;
 import mx.charts.chartClasses.GraphicsUtilities;
 import mx.graphics.IFill;
 import mx.graphics.SolidColor;
 import mx.graphics.SolidColorStroke;
 import mx.graphics.Stroke;
 
 public class CustomDataTip extends DataTip
 {
  public function CustomDataTip()
  {
   super();
  }
  
  //We are overwriting the updateDisplayList of the
  //datatip.
  override protected function updateDisplayList(w:Number,
               h:Number):void
  {
   super.updateDisplayList(w, h);
   
   var g:Graphics = graphics;
   g.clear();
   
   var xpos:Number = 0;
   var ypos:Number = 0;

   g.moveTo(measuredWidth, 2);
   var _shadowFill:IFill = 
    IFill(new SolidColor(0xAAAAAA, 0.55));
   _shadowFill.begin(g,
    new Rectangle(xpos, ypos, 
     measuredWidth, measuredHeight),null);
   g.lineTo(measuredWidth + 2, 2);
   g.lineTo(measuredWidth + 2, measuredHeight + 2);
   g.lineTo(2,measuredHeight + 2);
   g.lineTo(2,measuredHeight);
   g.lineTo(measuredWidth - 2, measuredHeight - 2);
   g.lineTo(measuredWidth - 2, 2);
   _shadowFill.end(g);
   
   var fill:IFill = IFill(
    new SolidColor(getStyle("backgroundColor"), 0.8));
   
   //You will specify the thickness of the border of the
   //data tip over here.
   
   //For older version of Flex use this
   //var stroke:Stroke = new Stroke(data.contextColor, 4, 1);
   //instead of this
   var stroke:SolidColorStroke = 
    new SolidColorStroke(data.contextColor, 4, 1);
   
   GraphicsUtilities.fillRect(g, xpos, ypos, measuredWidth,
    measuredHeight, fill, stroke);
  }  
 }
}
* Click here for the demo shown in this post.
^ Click here for the source files for the demo.

No comments:

Post a Comment