Showing posts with label Flex AS3. Show all posts
Showing posts with label Flex AS3. Show all posts

Sunday, June 15, 2014

Flex: Hiding the border on a side of a Group...

In flex 4, the style 'boderSides' has been taken out, but there's another way to hide the border of a selected side of a box away. And here's a simple example to give you a rough idea on how to get it done. :)

Source Code for the main application - 'SimpleSelectedBorderExample.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)" xmlns:Container="Container.*">
 <fx:Script>
  <![CDATA[
   import Container.SimpleGroup;
   
   import mx.events.FlexEvent;
   
   protected function creationCompleteEvent(e:FlexEvent):void
   {
    var tempBox:SimpleGroup;
    for(var i:int = 0; i < 8; i ++){
     for(var j:int = 0; j < 8; j ++){
      tempBox = new SimpleGroup();
      tempBox.x = i * 50 - 0.5;
      tempBox.y = j * 50 - 0.5;
      container.addElement(tempBox);
     } 
    }
   }
  ]]>
 </fx:Script>
 <s:BorderContainer width="100%"
        height="100%" 
        borderVisible="false">
  <s:layout>
   <s:VerticalLayout verticalAlign="middle"
         horizontalAlign="center"/>
  </s:layout>
  <!-- All the boxes will appear inside this container -->
  <s:BorderContainer width="400"
         height="400"
         id="container"
         borderVisible="false"/>
 </s:BorderContainer>      
</s:Application>
Source Code for the Group Class that we are using to repeat the pattern - 'SimpleGroup.mxml'
<?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="50" 
   height="50" 
   creationComplete="creationCompleteEvent(event)">
 <fx:Script>
  <![CDATA[
   import mx.events.FlexEvent;
   
   //Are we going to show the left border?
   [Bindable]
   private var _boolLeft:Boolean = false;
   
   //Are we going to show the right border?
   [Bindable]
   private var _boolRight:Boolean = false;
   
   //Are we going to show the top border?
   [Bindable]
   private var _boolTop:Boolean = false;
   
   //Are we going to show the bottom border?
   [Bindable]
   private var _boolBottom:Boolean = false;
   
   //-1 = border is outside the box
   //-0.5 = border is in the middle
   //0 = border is inside the box
   [Bindable]
   private var _typeValue:Number = 0;
   
   //Thickness of the border
   [Bindable]
   private var _borderThickness:Number = 0;
   
   //Color of the border
   [Bindable]
   private var _borderColor:uint = 0x333333;
   
   protected function creationCompleteEvent(e:FlexEvent):void
   {
    //Randomized the showing and hiding of the borders 
    var rand:Number = Math.round(Math.random());
    if(rand == 1){
     _boolLeft = true;
    }
    rand = Math.round(Math.random());
    if(rand == 1){
     _boolRight = true;
    }
    rand = Math.round(Math.random());
    if(rand == 1){
     _boolTop = true;
    }
    rand = Math.round(Math.random());
    if(rand == 1){
     _boolBottom = true;
    }
    
    _typeValue = Math.round(Math.random() * 2);
    if(_typeValue == 1){
     _typeValue = -0.5;
    }else if(_typeValue == 2){
     _typeValue = -1;
    }
    
    _borderThickness = Math.round(Math.random() * 2) + 1;
    
    _typeValue = _typeValue * _borderThickness;
   }
  ]]>
 </fx:Script>
 <!-- Left -->
 <s:Line left="{_typeValue}"
   top="{_typeValue}"
   bottom="{_typeValue}" 
   visible="{_boolLeft}">
  <s:stroke>
   <s:SolidColorStroke weight="{_borderThickness}"
         color="{_borderColor}"/>
  </s:stroke>
 </s:Line>
 <!-- Bottom -->
 <s:Line left="{_typeValue}"
   right="{_typeValue}"
   bottom="{_typeValue}"
   visible="{_boolBottom}">
  <s:stroke>
   <s:SolidColorStroke weight="{_borderThickness}"
        color="{_borderColor}"/>
  </s:stroke>
 </s:Line>
 <!-- Right -->
 <s:Line right="{_typeValue}"
   top="{_typeValue}"
   bottom="{_typeValue}"
   visible="{_boolRight}">
  <s:stroke>
   <s:SolidColorStroke weight="{_borderThickness}"
        color="{_borderColor}"/>
  </s:stroke>
 </s:Line>
 <!-- Top -->
 <s:Line left="{_typeValue}"
   right="{_typeValue}"
   top="{_typeValue}"
   visible="{_boolTop}">
  <s:stroke>
   <s:SolidColorStroke weight="{_borderThickness}"
        color="{_borderColor}"/>
  </s:stroke>
 </s:Line>
</s:Group>
* Click here for the demo shown in this post.
^ Click here for the source files for the demo.

Friday, May 23, 2014

Flex: Showing and Hiding the Origins of a chart

There might be cases where you wanted to show or replace the vertical and horizontal origin's of the chart with a different type of line. Here's a simple example to demo the hiding and showing of the origin of the chart..

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" 
    layout="absolute" 
    creationComplete="completeHandler(event)">
 <mx:Script>
  <![CDATA[
   import mx.charts.HitData;
   import mx.charts.series.LineSeries;
   import mx.collections.ArrayCollection;
   import mx.collections.Sort;
   import mx.collections.SortField;
   import mx.events.FlexEvent;
   import mx.events.ListEvent;
   import mx.formatters.DateFormatter;
   
   //Records used in the chart
   [Bindable]
   private var myData:XML = 
    <records>
     <record>
      <x>-7.81</x>
      <y>-4.98</y>
     </record>
     <record>
      <x>-2.84</x>
      <y>-4.62</y>
     </record>
     <record>
      <x>-8.66</x>
      <y>2.52</y>
     </record>
     <record>
      <x>3.56</x>
      <y>6.86</y>
     </record>
     <record>
      <x>-9.54</x>
      <y>-8.05</y>
     </record>
     <record>
      <x>7.48</x>
      <y>0.37</y>
     </record>
     <record>
      <x>9.2</x>
      <y>9.5</y>
     </record>
     <record>
      <x>9.32</x>
      <y>-1.59</y>
     </record>
     <record>
      <x>1.77</x>
      <y>-0.86</y>
     </record>
     <record>
      <x>-8.61</x>
      <y>-1.75</y>
     </record>
    </records>;
   
   private var dateCollection:ArrayCollection;
   
   protected function completeHandler(event:FlexEvent):void
   {
    // Parsing the xml data into ArrayCollection
    var objArray:ArrayCollection = new ArrayCollection();
    var tempObj:Object;
    for(var i:int = 0; i < myData.record.length(); i ++)
    {
     tempObj = new Object();
     tempObj.x = myData.record[i].x;
     tempObj.y = myData.record[i].y;
     objArray.addItem(tempObj);
    }
    
    // Create the new series and set its properties.
    var localSeries:LineSeries = new LineSeries();
    localSeries.dataProvider = objArray;
    localSeries.yField = "y";
    localSeries.xField = "x";
    
    // Back up the current series on the chart.
    var currentSeries:Array = mainChart.series;
    // Add the new series to the current Array of series.
    currentSeries.push(localSeries);
    // Add the new Array of series to the chart.
    mainChart.series = currentSeries;
   }
   
   //We are customizing the datatip / tool tip of the
   //chart data.
   public function myDataTipFunction(e:HitData):String {
    var s:String = "";
    s += "x: " + e.item.x + "<br>";
    s += "y: " + e.item.y;
    return s;
   }
  ]]>
 </mx:Script>
 <mx:VBox verticalGap="5" width="100%" height="100%" 
    verticalAlign="middle">
  <mx:HBox width="100%" horizontalAlign="center">
   <mx:LineChart id="mainChart" 
        showDataTips="true" 
        width="95%"
        dataTipFunction="myDataTipFunction">
    <mx:Stroke weight="2" 
         alpha="1" 
         color="#00FF00" 
         id="horiOriginStroke"/>
    <mx:Stroke weight="2" 
         alpha="1" 
         color="#FF0000" 
         id="vertOriginStroke"/>
    <mx:backgroundElements>
     <mx:GridLines direction="both"
          horizontalShowOrigin="{chkHori.selected}"
          horizontalOriginStroke="{horiOriginStroke}" 
          verticalShowOrigin="{chkVert.selected}"
          verticalOriginStroke="{vertOriginStroke}"/>
    </mx:backgroundElements>
   </mx:LineChart>
  </mx:HBox>
  <mx:HBox width="100%"
     horizontalAlign="center">
   <mx:CheckBox id="chkHori" 
       selected="true"       
       label="Show Horizontal Origin"/>
   <mx:CheckBox id="chkVert" 
       selected="true" 
       label="Show Vertical Origin"/>   
  </mx:HBox>
 </mx:VBox>
</mx:Application>
* Click here for the demo shown in this post.
^ Click here for the source files for the demo.

Friday, January 17, 2014

Problems with special characters on the web...

Sometimes when I was writing or editing a post on my blog, I'm encountering numerous situations where a ' or a " can mess up your contents when you publish it. The same problem also occur when I'm creating the asdocs for flex projects. If you have entered > or < characters not as a html node but part of your text, you might have problems creating the asdocs.

After look through numerous sites, I think that 'HTML entities' is one of
the better alternatives for Encoding and Decoding HTML Entities. On top of
that, it has also provided a list that shows all the special characters
and the corresponding HTML Entities.

* Click here for the website 'HTML Entities'.
^ Click here for my previous post on how to create asdocs for your flex projects.

Friday, January 3, 2014

Flex: JSON Deserialization

JSON a much shorter and faster response as compared to XML. Therefore, although some third party api out there still gives you the option of returning the results in XML format, it also gives you the option of getting the results in JSON format too. So I'm just doing up a simple JSON Deserialization example. :D

Main Application Class - SimpleJSONDeserialize.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">
 <fx:Declarations>
  <s:HTTPService url="http://maps.googleapis.com/maps/api/geocode/json" 
        method="GET" 
        concurrency="single" 
        useProxy="false" 
        id="httpService" 
        result="httpServiceResultEvent(event)" 
        fault="httpServiceFaultEvent(event)">
   <s:request xmlns="">
    <address>{txtInput.text}</address>
    <sensor>false</sensor>
   </s:request>
  </s:HTTPService>
 </fx:Declarations>
 <fx:Script>
  <![CDATA[
   import com.adobe.serialization.json.JSON;
   
   import model.MapResult;
   import model.MapServiceResult;
   
   import mx.collections.ArrayCollection;
   import mx.rpc.events.FaultEvent;
   import mx.rpc.events.ResultEvent;
   
   import org.osflash.vanilla.Vanilla;
   
   private var mapResult:MapServiceResult;
   
   [Bindable]
   private var resultCollection:ArrayCollection;
   
   //Upon clicking on the button, we will trigger the request
   protected function searchBtnClickHandler(event:MouseEvent):void
   {
    httpService.send();
   }
   
   //Upon making a successful request
   protected function httpServiceResultEvent(event:ResultEvent):void
   {
    //Converts the json result to a Object.
    var tempJSONObject:Object = JSON.decode(String(event.result));
    
    //Converts the Object into a Object of class type MapServiceResult
    mapResult = new Vanilla().extract(tempJSONObject, MapServiceResult);
    
    //Since we couldn't bind a Vector to a Datagrid directly,
    //we will just push the results to a ArrayCollection and
    //bind it to a DataGrid.
    resultCollection = new ArrayCollection();
    for each(var item:MapResult in mapResult.results)
    {
     resultCollection.addItem(item);
    }
    resultCollection.refresh();
   }
   
   //Upon making a failure request
   protected function httpServiceFaultEvent(event:FaultEvent):void
   {
   }
  ]]>
 </fx:Script>
 <s:VGroup width="100%" 
     height="100%" 
     verticalAlign="middle" 
     horizontalAlign="center">
  <s:Label text="Please enter an address:"/>
  <s:HGroup horizontalAlign="center">
   <s:TextInput id="txtInput"/>
   <s:Button label="Search Now!" 
       id="searchBtn" 
       click="searchBtnClickHandler(event)"/>
  </s:HGroup>
  <s:DataGrid dataProvider="{resultCollection}" 
     width="400" 
     height="300">
   <s:columns>
    <s:ArrayList>
     <s:GridColumn headerText="Formatted Address"
          dataField="formattedAddress"/>
    </s:ArrayList>
   </s:columns>
  </s:DataGrid>
 </s:VGroup>
</s:Application>

Model class for HttpServiceResults - MapServiceResult.as
package model
{
 public class MapServiceResult
 {
  public var status:String;
  private var _results: Vector.;

  public function get results():Vector.
  {
   return _results;
  }

  /**
   * Vanilla class will handle all these mapping, hence
   * it will map the object results into the Vector
   * of type MapResult.
   * 
   * But when we are getting a empty string rather than
   * an Array, we will have to handle such a condition 
   */
  public function set results(value:*):void
  {
   _results = new Vector.;
   if(String(value) != "")
   {
    _results = value; 
   }
  }
 }
}

Model class for individual Result - MapResult.as
package model
{
 public class MapResult
 {
  //Vanilla will map all the values of 
  //address_components into the Vector 
  //addrComponents.
  [Marshall (field="address_components")]
  public var addrComponents: Vector.;
  [Marshall (field="formatted_address")]
  public var formattedAddress:String;
  public var types: Vector.;
 }
}

Model class for individual address_components - AddrComponents.as
package model
{
 public class AddrComponents
 {
  //Vanilla will map the value of the field 
  //long_name to longName 
  [Marshall (field="long_name")]
  public var longName:String;
  [Marshall (field="short_name")]
  public var shortName:String;
  public var types: Vector.;
 }
}
* Click here for the source files for the demo.
  (This requires a valid Google Geocoding API key, but currently it will work if you
  are running it locally.)
^ Click here to find out more about the Vanilla as3 library.
~ Click here to find out more about the as3corelib.

Tuesday, October 15, 2013

Flex: XML appending

How to append a new node into a XML object in Flex/as3? Here's a simple example that teaches you how to append a XML node. Here's a guide on how to do it. But do take note that you have to use either _ or a letter for the first character of the name of the new node that you are trying to insert. However if you want to use numbers for the first character, there's a way to do it too. :P Besides for the ways that I have listed in this post, you can use dot notation (.) to append a new node too. :)

Source Code for the main application - 'SimpleXMLManipulation.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="creationCompleteEvt(event)" 
      xmlns:local="*">
 <fx:Declarations>
  <local:SimpleXMLConverter id="converter"/>
 </fx:Declarations>
 <fx:Script>
  <![CDATA[
   import mx.events.FlexEvent;
   
   protected function creationCompleteEvt(e:FlexEvent):void
   {
    converter.init();
   }
  ]]>
 </fx:Script>
 <s:VGroup width="100%" 
     height="100%" 
     verticalAlign="middle" 
     horizontalAlign="center">
  <s:Label text="Original XML"/>
  <s:TextArea text="@{converter.simpleXML}"/>
  <s:Spacer height="10"/>
  <s:HGroup>
   <s:VGroup horizontalAlign="center">
    <s:Label text="Modified XML using appendChild()"/>
    <s:TextArea text="{converter.simpleXML1}" 
       editable="false"
        width="30%" 
       minWidth="300"/>  
   </s:VGroup>
   <s:VGroup horizontalAlign="center">
    <s:Label text="Modified XML using []"/>
    <s:TextArea text="{converter.simpleXML2}"
       editable="false"
       width="30%" 
       minWidth="300"/>   
   </s:VGroup>
  </s:HGroup>
 </s:VGroup>
</s:Application>

Source code for the XML manipulating class
package
{
 import flash.events.Event;
 import flash.events.EventDispatcher;
 
 [Bindable]
 public class SimpleXMLConverter extends EventDispatcher
 {
  //XML String
  private var simpleXMLString:String = "" +
   "";
  
  //XML that contains the above simpleXMLString
  private var _simpleXML:XML;
  
  [Bindable(event="simpleXMLChanged")]
  public function get simpleXML():String
  {
   return _simpleXML;
  }

  public function set simpleXML(value:String):void
  {
   //If the input value isn't a valid XML,
   //we will set it to empty.
   try
   {
    _simpleXML = XML(value);
   }catch(e:Error)
   {
    _simpleXML = new XML();
   }
   
   //Modified XML using appendChild()
   simpleXML1 = XML(_simpleXML.toXMLString());
   if(simpleXML1)
   {
    var dummyXMLNode:XML;
    var dummyXMLNodeStr:String;
    dummyXMLNodeStr = "<" + name1Str + name2Str + ">";
    dummyXMLNodeStr += valueStr;
    dummyXMLNodeStr += "";
    dummyXMLNode = new XML(dummyXMLNodeStr);
    simpleXML1.appendChild(dummyXMLNode);
    if(simpleXML1.level1.length() > 0)
    {
     for(var i:int = 0; i < simpleXML1.level1.length(); i ++)
     {
      simpleXML1.level1[i].appendChild(dummyXMLNode);
     }
    }
   }
   
   //Modified XML using []
   simpleXML2 = XML(_simpleXML.toXMLString());
   if(simpleXML2)
   {
    simpleXML2[name1Str + name2Str] = 
     valueStr;
    if(simpleXML2.level1.length() > 0)
    {
     for(i = 0; i < simpleXML2.level1.length(); i ++)
     {
      simpleXML2.level1[i][name1Str + name2Str] = 
       valueStr;
     }
    }
   }
   
   //dispatching the 8 event to update the
   //respective text area
   dispatchEvent(new Event("simpleXML1Changed", 
    true, true));
   dispatchEvent(new Event("simpleXML2Changed", 
    true, true));
  }
  
  //XML that contains the modified XML based
  //on the above simpleXMLString and appendChild()
  private var _simpleXML1:XML;

  public function get simpleXML1():XML
  {
   return _simpleXML1;
  }

  [Bindable(event="simpleXML1Changed")]
  public function set simpleXML1(value:XML):void
  {
   _simpleXML1 = value;
  }
  
  //XML that contains the modified XML based
  //on the above simpleXMLString and []
  private var _simpleXML2:XML;

  public function get simpleXML2():XML
  {
   return _simpleXML2;
  }
  
  [Bindable(event="simpleXML2Changed")]
  public function set simpleXML2(value:XML):void
  {
   _simpleXML2 = value;
  }
  
  //The name of the empty node - part1
  private var name1Str:String = "empty";
  //The name of the empty node - part2
  private var name2Str:String = "node";
  
  //The value of the empty node
  private var valueStr:String = "dummyValue";
  
  public function init():void
  {
   simpleXML = new XML(simpleXMLString);
   
   //dispatching the event to update the main
   //respective text area
   dispatchEvent(new Event("simpleXMLChanged", 
    true, true));
  }
 }
}
* Click here for the demo shown in this post.
^ Click here for the source files for the demo.

Friday, September 13, 2013

Flex: Scrolling to a particular row of data of a Datagrid

I was looking for a much more easier way of scrolling to a particular row of data that belongs to the dataGrid. And I managed to find this.

Source Code for the main application - 'SimpleDataGridScrolling.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" >
 <fx:Declarations>
  <fx:XMLList id="records">
   <record>
    <id>01</id>
    <name>Eric</name>
    <address>3987 Mauris Rd.</address>
   </record>
   <record>
    <id>02</id>
    <name>Bradley</name>
    <address>692-535 Id Street</address>
   </record>
   <record>
    <id>03</id>
    <name>Flynn</name>
    <address>191-491 Ullamcorper Road</address>
   </record>
   <record>
    <id>04</id>
    <name>Akeem</name>
    <address>400-2992 Donec Rd.</address>
   </record>
   <record>
    <id>05</id>
    <name>Shad</name>
    <address>P.O. Box 490, 9377 Lorem, Avenue</address>
   </record>
   <record>
    <id>06</id>
    <name>Brady</name>
    <address>P.O. Box 203, 418 Amet Street</address>
   </record>
   <record>
    <id>07</id>
    <name>Igor</name>
    <address>5584 Ultrices Av.</address>
   </record>
   <record>
    <id>08</id>
    <name>Herman</name>
    <address>Ap #316-3329 Fermentum Avenue</address>
   </record>
   <record>
    <id>09</id>
    <name>Martin</name>
    <address>932-8151 Arcu. Road</address>
   </record>
   <record>
    <id>10</id>
    <name>Micah</name>
    <address>P.O. Box 244, 8472 Lacus. Ave</address>
   </record>
   <record>
    <id>11</id>
    <name>Isaiah</name>
    <address>3798 Risus. Avenue</address>
   </record>
   <record>
    <id>12</id>
    <name>Amos</name>
    <address>Ap #377-2082 Mollis. Road</address>
   </record>
   <record>
    <id>13</id>
    <name>Dominic</name>
    <address>P.O. Box 801, 2289 Malesuada Rd.</address>
   </record>
   <record>
    <id>14</id>
    <name>Ethan</name>
    <address>Ap #509-2519 Non, Avenue</address>
   </record>
   <record>
    <id>15</id>
    <name>Julian</name>
    <address>9373 Ut Avenue</address>
   </record>
   <record>
    <id>16</id>
    <name>Yoshio</name>
    <address>120-7486 Ornare, Av.</address>
   </record>
   <record>
    <id>17</id>
    <name>Harlan</name>
    <address>Ap #821-3336 Velit. Av.</address>
   </record>
   <record>
    <id>18</id>
    <name>Rigel</name>
    <address>Ap #689-7263 Consectetuer Rd.</address>
   </record>
   <record>
    <id>19</id>
    <name>Holmes</name>
    <address>Ap #790-6923 Tincidunt St.</address>
   </record>
   <record>
    <id>20</id>
    <name>Aidan</name>
    <address>948-115 Imperdiet Street</address>
   </record>
   <record>
    <id>21</id>
    <name>Colin</name>
    <address>P.O. Box 454, 9501 Lectus Avenue</address>
   </record>
   <record>
    <id>22</id>
    <name>Palmer</name>
    <address>Ap #402-1566 Varius Road</address>
   </record>
   <record>
    <id>23</id>
    <name>Sylvester</name>
    <address>852-8076 Enim. St.</address>
   </record>
   <record>
    <id>24</id>
    <name>Cole</name>
    <address>876-4551 Ornare Ave</address>
   </record>
   <record>
    <id>25</id>
    <name>Yardley</name>
    <address>P.O. Box 256, 4107 Tempor Rd.</address>
   </record>
   <record>
    <id>26</id>
    <name>Coby</name>
    <address>P.O. Box 467, 7273 Nulla Street</address>
   </record>
   <record>
    <id>27</id>
    <name>Beau</name>
    <address>743-8871 Sem Rd.</address>
   </record>
   <record>
    <id>28</id>
    <name>Kermit</name>
    <address>Ap #183-8772 Magna. St.</address>
   </record>
   <record>
    <id>29</id>
    <name>Josiah</name>
    <address>P.O. Box 645, 453 Ornare, St.</address>
   </record>
   <record>
    <id>30</id>
    <name>Elijah</name>
    <address>537 Turpis Avenue</address>
   </record>
  </fx:XMLList>
  <s:XMLListCollection id="tempXmlCollection" 
        source="{records}"/>
  <s:XMLListCollection id="tempXmlCollection1" 
        source="{records.id}"/>
 </fx:Declarations>
 <fx:Script>
  <![CDATA[
   import mx.events.FlexEvent;
   
   import spark.components.List;
   import spark.events.IndexChangeEvent;
   
   /**
    * Upon changing the selection of the drop down menu,
    * we would need to loop through the data of the
    * datagrid and find the one that matches the 
    * selection from the drop down menu and we would
    * select the row and scroll to that row.
    */
   protected function changeHandler(event:IndexChangeEvent):void
   {
    var tempData:Object = ddId.selectedItem;
    var tempDP:XMLListCollection = 
     gridData.dataProvider as XMLListCollection;
    for(var i:int = 0; i < tempDP.length; i ++)
    {
     if(tempDP.getItemAt(i).id == tempData)
     {
      gridData.selectedIndex = i;
      //This works in Flex 4 Spark Components.
      gridData.grid.verticalScrollPosition = 
       gridData.grid.getCellY(i, 0);
      //This works in Flex 3 Components but not 
      //for Spark Components.
      //gridData.scrollToIndex(i);
      break;
     }
    }
   }
   
  ]]>
 </fx:Script>
 <s:VGroup verticalAlign="middle" 
     horizontalAlign="center" 
     width="100%" 
     height="100%">
  <s:HGroup verticalAlign="middle" 
      horizontalAlign="center" >
   <s:Label text="Select a ID:"/>
   <s:DropDownList id="ddId"
       dataProvider="{tempXmlCollection1}"
       change="changeHandler(event)"/>
  </s:HGroup>
  <s:DataGrid id="gridData" 
     height="200"
      width="500"
     resizableColumns="true"
     dataProvider="{tempXmlCollection}">
   <s:columns>
    <s:ArrayList>
     <s:GridColumn dataField="id" 
          minWidth="50"
          headerText="ID"/>
     <s:GridColumn dataField="name" 
          minWidth="150"
          headerText="Name"/>
     <s:GridColumn dataField="address"  
          minWidth="280" 
          headerText="Address"/>
    </s:ArrayList>
   </s:columns>
  </s:DataGrid>
 </s:VGroup>
</s:Application>
* Click here for the demo shown in this post.
^ Click here for the source files for the demo.

Wednesday, August 14, 2013

Flex: Generating ASDoc

Rather than searching high and low for a particular function, wouldn't it be much more easier if you can just search through a set of HTML files in case you need to reference some codes that you have writen back then? Therefore, I have created a ant file to auto generated the HTML for the Flex 3 and Flex 4 projects that I have shown on this blog.

Main Ant file - build.xml
<?xml version="1.0" encoding="ISO-8859-1"?>
<project basedir="." default="dist.asdocs">
 <!-- project root directory -->
 <property name="APP_ROOT" location="${basedir}/.." />
 <property file="build.properties" />

 <target name="dist.asdocs" description="Create asdocs">
  <cleanAsdocsDir3 />
  <createAsdocs3 />
  <cleanAsdocsDir4 />
  <createAsdocs4 />
 </target>

 <macrodef name="createAsdocs3">
  <sequential>
      <tstamp prefix="dateStamp">
          <format property="date" pattern="MMM/dd/yyyy hh:mm:ss aa" unit="hour" />
      </tstamp>
   <java taskname="asdoc" jar="${FLEX_HOME_3}/lib/asdoc.jar" 
    failonerror="true" fork="true">
    <jvmarg value="-Dapplication.home=${FLEX_HOME_3}"/>
    <!-- Need to assign lots for memory for the ant job -->
    <jvmarg value="-Dsun.io.useCanonCaches=false"/>
    <jvmarg value="-Xms512m"/>
    <jvmarg value="-Xmx1024m"/>
    <jvmarg value="-XX:MaxPermSize=256m"/>

    <arg value="-warnings=false" />
    <!-- Root directory of files -->
    <arg value="-source-path" />
    <arg value="${APP_ROOT}/../AccessingSWFLoaderFunctions/src" />
    <arg value="${APP_ROOT}/../BubbleEventTest/src" />
    <arg value="${APP_ROOT}/../ClippingContent/src" />
    <arg value="${APP_ROOT}/../CustomiseToolTip/src" />
    <arg value="${APP_ROOT}/../DateCheckerApplication/src" />
    <arg value="${APP_ROOT}/../drawingShapes/src" />
    <arg value="${APP_ROOT}/../FlexCustomPreloader/src" />
    <arg value="${APP_ROOT}/../htmlEntity/src" />
    <arg value="${APP_ROOT}/../LinkList/src" />
    <arg value="${APP_ROOT}/../ListDragAndDrop/src" />
    <arg value="${APP_ROOT}/../MeasurePosition/src" />
    <arg value="${APP_ROOT}/../MultipleViewWithAlivePDF/src" />
    <arg value="${APP_ROOT}/../PlayingWithAlivePDF/src" />
    <arg value="${APP_ROOT}/../PlayingWithAlivePDF/fonts" />
    <arg value="${APP_ROOT}/../PlayingWithRegExp/src" />
    <arg value="${APP_ROOT}/../PopUpOverlay/src" />
    <arg value="${APP_ROOT}/../ScreenCaptureView/src" />
    <arg value="${APP_ROOT}/../SimpleChartHorizontalAxis/src" />
    <arg value="${APP_ROOT}/../SimpleSingleton/src" />
    <arg value="${APP_ROOT}/../stopBubbleEventTest/src" />
    <!-- List of files to document -->
    <arg value="-doc-sources" />
    <arg value="${APP_ROOT}/../AccessingSWFLoaderFunctions/src" />
    <arg value="${APP_ROOT}/../BubbleEventTest/src" />
    <arg value="${APP_ROOT}/../ClippingContent/src" />
    <arg value="${APP_ROOT}/../CustomiseToolTip/src" />
    <arg value="${APP_ROOT}/../DateCheckerApplication/src" />
    <arg value="${APP_ROOT}/../drawingShapes/src" />
    <arg value="${APP_ROOT}/../FlexCustomPreloader/src" />
    <arg value="${APP_ROOT}/../htmlEntity/src" />
    <arg value="${APP_ROOT}/../LinkList/src" />
    <arg value="${APP_ROOT}/../ListDragAndDrop/src" />
    <arg value="${APP_ROOT}/../MeasurePosition/src" />
    <arg value="${APP_ROOT}/../MultipleViewWithAlivePDF/src" />
    <arg value="${APP_ROOT}/../PlayingWithAlivePDF/src" />
    <arg value="${APP_ROOT}/../PlayingWithRegExp/src" />
    <arg value="${APP_ROOT}/../PopUpOverlay/src" />
    <arg value="${APP_ROOT}/../ScreenCaptureView/src" />
    <arg value="${APP_ROOT}/../SimpleChartHorizontalAxis/src" />
    <arg value="${APP_ROOT}/../SimpleSingleton/src" />
    <arg value="${APP_ROOT}/../stopBubbleEventTest/src" />
    <!-- Adding a external library, if u are using one -->
    <arg value="-external-library-path" />
    <arg value="${APP_ROOT}/../PlayingWithAlivePDF/libs" />
    <!-- Title of the generated asdoc webpage -->
    <arg value="-window-title" />
    <arg value="Flex 3.5.0 Projects Documentation" />
    <!-- Header shown in the generated asdoc webpage -->
    <arg value="-main-title" />
    <arg value="Flex 3.5.0 Projects Documentation" />
    <!-- Footer shown in the generated asdoc webpage -->
    <arg value="-footer" />
    <arg value="Created on ${dateStamp.date}" />
    <!-- Output folder of the generated asdoc webpage -->
    <arg value="-output" />
    <arg value="${OUTPUT_DOCS_DIR_3}" />
    <!-- If your project requires some TrueType, OpenType fonts -->
    <!--<arg value="-managers" />
    <arg value="flash.fonts.AFEFontManager" />-->
   </java>
  </sequential>
 </macrodef>

 <macrodef name="cleanAsdocsDir3">
  <sequential>
   <echo message="Clean Asdocs Directory" />
   <delete dir="${OUTPUT_DOCS_DIR_3}" failonerror="false" />
   <mkdir dir="${OUTPUT_DOCS_DIR_3}" />
   <echo>Cleaned asdocs output directory</echo>
  </sequential>
 </macrodef>

 <macrodef name="createAsdocs4">
  <sequential>
      <tstamp prefix="dateStamp">
          <format property="date" pattern="MMM/dd/yyyy hh:mm:ss aa" unit="hour" />
      </tstamp>
   <java taskname="asdoc" jar="${FLEX_HOME_4}/lib/asdoc.jar" 
    failonerror="true" fork="true">
    <jvmarg value="-Dapplication.home=${FLEX_HOME_4}"/>
    <!-- Need to assign lots for memory for the ant job -->
    <jvmarg value="-Dsun.io.useCanonCaches=false"/>
    <jvmarg value="-Xms512m"/>
    <jvmarg value="-Xmx1024m"/>
    <jvmarg value="-XX:MaxPermSize=256m"/>

    <arg value="-warnings=false" />
    <!-- Root directory of files -->
    <arg value="-source-path" />
    <arg value="${APP_ROOT}/../AutoresizableTextfield/src" />
    <arg value="${APP_ROOT}/../ImageSourceIssue/src" />
    <arg value="${APP_ROOT}/../NotReallyGlobalTooltip/src" />
    <arg value="${APP_ROOT}/../OpenURLOfHTMLText/src" />
    <arg value="${APP_ROOT}/../Simple2WayBinding/src" />
    <arg value="${APP_ROOT}/../Simple3StateCheckBox/src" />
    <arg value="${APP_ROOT}/../SimpleAreaChartToolTip/src" />
    <arg value="${APP_ROOT}/../SimpleButtonBarSelect/src" />
    <arg value="${APP_ROOT}/../SimpleChartAnimation/src" />
    <arg value="${APP_ROOT}/../SimpleChartDataClick/src" />
    <arg value="${APP_ROOT}/../SimpleChartPositionOfAPoint/src" />
    <arg value="${APP_ROOT}/../SimpleChartVisibility/src" />
    <arg value="${APP_ROOT}/../SimpleCopyingOfObjects/src" />
    <arg value="${APP_ROOT}/../SimpleDataTipBorder/src" />
    <arg value="${APP_ROOT}/../SimpleDoubleClicking/src" />
    <arg value="${APP_ROOT}/../SimpleMouseReleaseOutside/src" />
    <arg value="${APP_ROOT}/../SimpleMouseReleaseOutside/src" />
    <arg value="${APP_ROOT}/../SimplePastingDetection/src" />
    <arg value="${APP_ROOT}/../SimpleStackingColumn/src" />
    <arg value="${APP_ROOT}/../SimpleSudokuGrid/src" />
    <arg value="${APP_ROOT}/../SimpleTabSequence/src" />
    <arg value="${APP_ROOT}/../SimpleTwitterPost/src" />
    <!-- List of files to document -->
    <arg value="-doc-sources" />
    <arg value="${APP_ROOT}/../AutoresizableTextfield/src" />
    <arg value="${APP_ROOT}/../ImageSourceIssue/src" />
    <arg value="${APP_ROOT}/../NotReallyGlobalTooltip/src" />
    <arg value="${APP_ROOT}/../OpenURLOfHTMLText/src" />
    <arg value="${APP_ROOT}/../Simple2WayBinding/src" />
    <arg value="${APP_ROOT}/../Simple3StateCheckBox/src" />
    <arg value="${APP_ROOT}/../SimpleAreaChartToolTip/src" />
    <arg value="${APP_ROOT}/../SimpleButtonBarSelect/src" />
    <arg value="${APP_ROOT}/../SimpleChartAnimation/src" />
    <arg value="${APP_ROOT}/../SimpleChartDataClick/src" />
    <arg value="${APP_ROOT}/../SimpleChartPositionOfAPoint/src" />
    <arg value="${APP_ROOT}/../SimpleChartVisibility/src" />
    <arg value="${APP_ROOT}/../SimpleCopyingOfObjects/src" />
    <arg value="${APP_ROOT}/../SimpleDataTipBorder/src" />
    <arg value="${APP_ROOT}/../SimpleDoubleClicking/src" />
    <arg value="${APP_ROOT}/../SimpleMouseReleaseOutside/src" />
    <arg value="${APP_ROOT}/../SimpleMouseReleaseOutside/src" />
    <arg value="${APP_ROOT}/../SimplePastingDetection/src" />
    <arg value="${APP_ROOT}/../SimpleStackingColumn/src" />
    <arg value="${APP_ROOT}/../SimpleSudokuGrid/src" />
    <arg value="${APP_ROOT}/../SimpleTabSequence/src" />
    <arg value="${APP_ROOT}/../SimpleTwitterPost/src" />
    <!-- Title of the generated asdoc webpage -->
    <arg value="-window-title" />
    <arg value="Flex 4.6.0 Projects Documentation" />
    <!-- Header shown in the generated asdoc webpage -->
    <arg value="-main-title" />
    <arg value="Flex 4.6.0 Projects Documentation" />
    <!-- Footer shown in the generated asdoc webpage -->
    <arg value="-footer" />
    <arg value="Created on ${dateStamp.date}" />
    <!-- Output folder of the generated asdoc webpage -->
    <arg value="-output" />
    <arg value="${OUTPUT_DOCS_DIR_4}" />
    <!-- If your project requires some TrueType, OpenType fonts -->
    <!--<arg value="-managers" />
    <arg value="flash.fonts.AFEFontManager" />-->
   </java>
  </sequential>
 </macrodef>

 <macrodef name="cleanAsdocsDir4">
  <sequential>
   <echo message="Clean Asdocs Directory" />
   <delete dir="${OUTPUT_DOCS_DIR_4}" failonerror="false" />
   <mkdir dir="${OUTPUT_DOCS_DIR_4}" />
   <echo>Cleaned asdocs output directory</echo>
  </sequential>
 </macrodef>
</project>

Main Ant Properties file - build.properties
# change this to your Flex SDK directory path
FLEX_HOME_3=C:/Program Files (x86)/Adobe/Adobe Flash Builder 4.5/sdks/3.6.0
FLEX_HOME_4=C:/Program Files (x86)/Adobe/Adobe Flash Builder 4.5/sdks/4.5.0

# output location for the asdocs for flex 3.6
OUTPUT_DOCS_DIR_3 = ${APP_ROOT}/../asdocs_3.6

# output location for the asdocs for flex 4.5
OUTPUT_DOCS_DIR_4 = ${APP_ROOT}/../asdocs_4.5
* Click here to find out more about ASDoc.
* Click here for the ASDoc that I have generated for my Flex 3 projects.
* Click here for the ASDoc that I have generated for my Flex 4 projects.
* Click here to download the ant build files that I have shown in this post.

Friday, August 2, 2013

Flex: Creating the Sudoku Base Grid

I have been trying out all sorts of method in creating a Sudoku Grid and I finally managed to do it. The solution was far more easier than I thought ...

Let's take a look at the source code - SimpleSudokuGrid.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>
  <!-- Place non-visual elements (e.g., services, value objects) here -->
 </fx:Declarations>
 <fx:Script>
  <![CDATA[
   import mx.collections.ArrayCollection;
   import mx.events.FlexEvent;
   
   import spark.components.Button;
   import spark.events.IndexChangeEvent;
   
   /**
    * Holds the data to form a Grid
    */
   private var baseGrid:ArrayCollection;
   
   /**
    * Boolean to determine if there's a need to recreate the grid
    */
   private var needToReset:Boolean = true;
   
   /**
    * Number to count the number of failure in the current
    * grid data forming.
    */
   private var tries:Number = 0;
   
   /**
    * Maximum number of failure before recreate.
    */
   private static const MAX_TRIES:Number = 9;
   
   /**
    * A set of data that will be used to populate the ButtonBar
    */
   [Bindable]
   private var dummyData:ArrayCollection = new ArrayCollection(
    [{label: "1", value:1},
     {label: "2", value:2},
     {label: "3", value:3},
     {label: "4", value:4},
     {label: "5", value:5},
     {label: "6", value:6},
     {label: "7", value:7},
     {label: "8", value:8},
     {label: "9", value:9}]);
   
   /**
    * Upon Create Complete, we will create a Sudoku(9x9) Grid
    */
   protected function creationCompleteEvent(e:FlexEvent):void
   {
    //Will keep recreating the grid if 'needToReset' is
    //true
    while(needToReset)
    {
     //Set 'needToReset' to false
     needToReset = false;
     createGrid();
     createNumbers();
    }
    
    createGridUI();
    btnBar.dispatchEvent(
     new IndexChangeEvent(IndexChangeEvent.CHANGE));
   }
   
   /**
    * Create new (9*9) DataCollection and fill it up with 0.
    */
   private function createGrid():void
   {
    baseGrid = new ArrayCollection();
    
    var tempCol:ArrayCollection;
    for(var i:int = 0; i < 9; i ++)
    {
     tempCol = new ArrayCollection();
     for(var j:int = 0; j < 9; j++)
     {
      tempCol.addItem(0);
     }
     baseGrid.addItem(tempCol);
    }
   }
   
   /**
    * Start populating the numbers from 1 - 9.
    * If 'needToReset' is true, move out of this function.
    */ 
   private function createNumbers():void
   {
    for(var i:int = 1; i < 10; i ++)
    {
     insertNumber(i);
     
     if(needToReset)
     {
      break; 
     }
    }
    
    if(needToReset)
    {
     return; 
    }
    
    printNumbers();
   }
   
   /**
    * Find the empty boxes that you can slot the value
    * @param value a value that you are going to slot in
    */   
   private function insertNumber(value:Number):void
   {
    //Used to store the rows avaliable in a column
    var numbersFree:Array;
    //Is the 1st segment taken?
    var fromSegment1:Boolean = false;
    //Rows that we will need to remove if 1st segment
    //is taken
    var seg1Array:Array = [0,1,2];
    //Is the 2nd segment taken?
    var fromSegment2:Boolean = false;
    //Rows that we will need to remove if 2nd segment
    //is taken
    var seg2Array:Array = [3,4,5];
    //Is the 3rd segment taken?
    var fromSegment3:Boolean = false;
    //Rows that we will need to remove if 3rd segment
    //is taken
    var seg3Array:Array = [6,7,8];
    //Stores the current selected row of a column
    var rand:Number;
    //Stores all the boxes that has the value 
    var tempArray:Array = new Array();
    for(var i:int = 0; i < 9; i ++)
    {
     //In sudoku, it's divided into 9 (3*3) boxes.
     //Each each of this box, the same number can 
     //appear once.
     //Therefore every 3 column, we need to reset
     //these 3 values.
     if(i % 3 == 0)
     {
      fromSegment1 = false;
      fromSegment2 = false;
      fromSegment3 = false;
     }
     //Find the empty slots of the column first
     numbersFree = findEmptySlot(i);
     //Remove the rows based on the previous selections
     if(fromSegment1)
     {
      numbersFree = removeSlot(numbersFree, seg1Array);
     }
     if(fromSegment2)
     {
      numbersFree = removeSlot(numbersFree, seg2Array);
     }
     if(fromSegment3)
     {
      numbersFree = removeSlot(numbersFree, seg3Array);
     }
     //Remove the rows that have been selected already.
     if(tempArray.length > 0)
     {
      numbersFree = removeSlot(numbersFree, tempArray);
     }
     
     //If there are rows left
     if(numbersFree.length > 0)
     {
      //Select one of them
      rand = Math.floor(Math.random() 
       * numbersFree.length);
      rand = numbersFree[rand];
      //Check which Segment the row number is in
      if(rand < 3)
      {
       fromSegment1 = true;
      }else if(rand < 6)
      {
       fromSegment2 = true;
      }else{
       fromSegment3 = true;
      }
      //Add it to the list of boxes with the 
      //same value
      tempArray.push(rand);
      
      //Update the value in the main DataCollection
      insertSlot(i, rand, value);
     }else{
      //else if '0' row is avaliable
      //increase the number of failures/tries
      tries ++;
      //Revert back all the slots
      revertSlots(tempArray);
      tempArray = new Array();
      i = -1;
      //Check if it has reach the Maximum tries/failures
      if(tries == MAX_TRIES)
      {
       //Need to recreate the grid again.
       tries = 0;
       needToReset = true;
       return;
      }
     }
    }
   }
   
   /**
    * Find all the empty rows of a selected Column
    * @param col the selected column index
    * @return Array of rows that currently has a value of '0'
    */
   private function findEmptySlot(col:Number):Array
   {
    var tempArray:Array = new Array();
    var tempArrayCol:ArrayCollection;
    tempArrayCol = ArrayCollection(baseGrid.getItemAt(col));
    for(var i:int = 0; i < tempArrayCol.length; i ++)
    {
     if(Number(tempArrayCol.getItemAt(i)) == 0)
     {
      tempArray.push(i);
     }
    }
    return tempArray;
   }
   
   /**
    * Compare 2 Arrays.
    * @param tempArray1 Array of values
    * @param tempArray2 Array of values
    * @return the Array of values tempArray1 that are different from 
    * tempArray2
    */ 
   private function removeSlot(tempArray1:Array, tempArray2:Array):Array
   {
    var tempArray:Array = new Array();
    for(var i:int = 0; i < tempArray1.length; i ++)
    {
     tempArray.push(tempArray1[i]);
    }
    for(i = 0; i < tempArray.length; i ++)
    {
     for(var j:int = 0; j < tempArray2.length; j ++)
     {
      if(tempArray[i] == tempArray2[j])
      {
       tempArray.splice(i, 1);
       i --;
       break;
      }
     }
    }
    return tempArray;
   }
   
   /**
    * Replace a particular slot with a new value.
    * @param col selected column
    * @param row selected row
    * @param value the new value
    */
   private function insertSlot(col:Number, row:Number, value:Number):void
   {
    var tempArrayCol:ArrayCollection;
    tempArrayCol = ArrayCollection(baseGrid.getItemAt(col));
    tempArrayCol.setItemAt(value, row);
   }
   
   /**
    * This function will change the newly numbers of the same group 
    * back to 0. For example, halfway through the number 7, a problem 
    * has occur, therefore need to replace all the number 7 from the 
    * first box again. Hence, need to reset all the existing number 
    * 7 box to '0' first.
    * @param tempArray1 Boxes that requires the values to be reset to '0'
    */
   private function revertSlots(tempArray1:Array):void
   {
    var tempArrayCol:ArrayCollection;
    for(var i:int = 0; i < tempArray1.length; i ++)
    {
     tempArrayCol = ArrayCollection(baseGrid.getItemAt(i));
     tempArrayCol.setItemAt(0,tempArray1[i])
    }
   }
   
   /**
    * This is merely for debugging purposes. You can use
    * this function to track the valus in the (9x9) DataCollection.
    */
   private function printNumbers():void
   {
    var tempArrayCol:ArrayCollection;
    var tempArray:Array = new Array();
    for(var i:int = 0; i < 9; i ++)
    {
     tempArray.push("");     
    }
    for(var col:int = 0; col < baseGrid.length; col ++)
    {
     tempArrayCol = 
      ArrayCollection(baseGrid.getItemAt(col));
     for(var row:int = 0; row < tempArrayCol.length; row ++)
     {
      tempArray[row] += 
       Number(tempArrayCol.getItemAt(row));
     }
    }
    for(i = 0; i < tempArray.length; i ++)
    {
     trace(tempArray[i]);
    }
   }
   
   /**
    * Create all the buttons base on the values in the ArrayCollection
    */ 
   private function createGridUI():void
   {
    var btn:Button;
    var tempArrayCol:ArrayCollection;
    
    for(var row:int = 0; row < 9; row ++)
    {
     for(var col:int = 0; col < baseGrid.length; col ++)
     {
      tempArrayCol = 
       ArrayCollection(baseGrid.getItemAt(col));
      btn = new Button();
      btn.width = 30;
      btn.height = 30;
      btn.label = 
       Number(tempArrayCol.getItemAt(row)).toString();
      btn.name = "btn_" + row + "_" + col; 
      grpGrid.addElement(btn);
     }
    }
   }
   
   /**
    * Upon clicking on one of the buttons of the ButtonBar, we will
    * show buttons with the same value with a alpha value of 1 and
    * those that are different will have a alpha value of 0.3.
    */
   protected function changeEvent(event:IndexChangeEvent):void
   {
    var tempBar:ButtonBar = ButtonBar(event.target);
    var tempObj:* = tempBar.selectedItem;
    var selectedValue:Number = -1;
    if(tempObj)
    {
     selectedValue = tempObj.value;
    }
    
    var btn:Button;
    for (var i:int = 0; i < grpGrid.numElements; i ++)
    {
     btn = Button(grpGrid.getElementAt(i));
     if(Number(btn.label) == selectedValue)
     {
      btn.alpha = 1;
     }else{
      btn.alpha = 0.3;
     }
    }
   }
  ]]>
 </fx:Script>
 <s:VGroup verticalCenter="0" 
     horizontalCenter="0" 
     horizontalAlign="center">
  <s:Group id="grpGrid" >
   <s:layout>
    <s:TileLayout requestedRowCount="9" 
         requestedColumnCount="9"
         horizontalGap="3" 
         verticalGap="3"/>
   </s:layout>
  </s:Group>
  <s:Label text="Click on the following to show the buttons with the same value."/>
  <s:ButtonBar id="btnBar" 
      dataProvider="{dummyData}"  
      selectedIndex="-1"
      labelField="label"
      change="changeEvent(event)"/>
 </s:VGroup>
</s:Application>
* Click here for the demo shown in this post.
^ Click here for the source files for the demo.

Friday, July 26, 2013

Tour de Flex

I was trying to look for the Style Explorer for Flex 4 but it seems that the closest alternative right now would be...

The contents shown in the above are taken from 'Adobe' website.

You can click on the button shown in the above to download and
install the desktop application 'Tour de Flex' too.

* Click here for the official website for 'Tour de Flex'.
^ Click here for the web version of 'Tour de Flex'.

Wednesday, July 3, 2013

Flex: Accessing the buttons of a ButtonBar

I was trying to figure out a way to access the individual buttons of the ButtonBar yesterday. After spending a bit of time working on it, I finally managed to do it. Here's a solution to the problem.

Here's the source code of my main application - SimpleButtonBarSelect.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:Script>
  <![CDATA[
   import mx.collections.ArrayCollection;
   import mx.events.FlexEvent;
   import mx.events.IndexChangedEvent;
   
   import spark.components.ButtonBarButton;
   import spark.events.IndexChangeEvent;
   
   //A set of data that will be used to populate the ButtonBar
   [Bindable]
   private var dummyData:ArrayCollection = new ArrayCollection(
    [{label: "1", value:9},
    {label: "2", value:8},
    {label: "3", value:7},
    {label: "4", value:6},
    {label: "5", value:5},
    {label: "6", value:4},
    {label: "7", value:3},
    {label: "8", value:2},
    {label: "9", value:1}]);
   
   //Upon clicking on one of the checkbox,
   protected function clickEvent(event:MouseEvent):void
   {
    var isOddDisable:Boolean = chkOdd.selected;
    var isEvenDisable:Boolean = chkEven.selected;
    
    var tempButton:ButtonBarButton;
    var tempLabelNum:Number;
    
    //Loop through all the buttons of the ButtonBar
    for(var i:int = 0; i < btnBar.dataGroup.numElements; i ++)
    {
     tempButton = ButtonBarButton(btnBar.dataGroup.getElementAt(i));
     tempLabelNum = Number(tempButton.label);
     if(tempLabelNum % 2 == 0)
     {
      //If the label is a odd number
      //check it against the value of the chkOdd checkbox
      tempButton.enabled = !isEvenDisable;
     }else{
      //If the label is a even number
      //check it against the value of the chkEven checkbox
      tempButton.enabled = !isOddDisable;
     }
    }
   }
   
   //Upon clicking on one of the buttons of the ButtonBar, we
   //will update the 'Result:"
   protected function changeEvent(event:IndexChangeEvent):void
   {
    var tempBar:ButtonBar = ButtonBar(event.target);
    var tempObj:* = tempBar.selectedItem;
    
    lblResult.text = "Result: Button " + tempObj.label +
     " with a value of " + tempObj.value + " has been selected."
   }
   
   //Dispatch a change event at the start of the application since
   //one of the buttons are selected by default.
   protected function creationCompleteEvent(event:FlexEvent):void
   {
    btnBar.dispatchEvent(new IndexChangeEvent(IndexChangeEvent.CHANGE));
   }
   
  ]]>
 </fx:Script>
 <s:VGroup width="100%"
     height="100%" 
     verticalAlign="middle" 
     horizontalAlign="center">
  <s:Label text="Click on the following to disable the respective buttons."/>
  <s:HGroup width="100%" >
   <s:Spacer width="100%"/>
   <s:CheckBox id="chkOdd"
      label="Disable Odd Buttons"
      click="clickEvent(event)"/>
   <s:CheckBox id="chkEven"
      label="Disable Even Buttons"
      click="clickEvent(event)"/>
   <s:Spacer width="100%"/>
  </s:HGroup>
  <s:ButtonBar id="btnBar" 
      dataProvider="{dummyData}" 
      requireSelection="true" 
      labelField="label"
      change="changeEvent(event)"/>
  <s:Label id="lblResult" text="Result:"/>
 </s:VGroup>
</s:Application>
* Click here for the demo shown in this post.
^ Click here for the source files for the demo.

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.

Saturday, June 1, 2013

AS 3: Opening a url with utf-8 encode parameters

Not sure if any of you out there might encounter the following problem, but with all these social networking sites floating around the Internet, there might be a requirement that requires you to open up a website with some parameters. Therefore...

Here's the source code of my main application - SimpleTwitterPost.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"
      backgroundColor="#CDCDCD">
 
 <fx:Script>
  <![CDATA[
   //A default message.
   private var msg:String = "僕は男性です。";
   //URL of Twitter.
   private var strTwitter:String = "http://www.twitter.com";
   
   //Label
   private var strLabel:String = "Please enter your " +
    "message in the text box provided below."
   
   //Upon clicking on one of the buttons,
   protected function clickHandler(event:MouseEvent):void
   {
    var tempStr:String = txtInput.text;
    if(event.currentTarget == btn1)
    {
     //encode the message in utf-16,
     //which is not supported across most browsers.
     tempStr = escape(tempStr);
    }else{
     //encode the message in utf-8,
     //which is supported across all browsers.
     tempStr = encodeURIComponent(tempStr);
    }
    tempStr = strTwitter + "?status=" + tempStr;
    
    var tempURLReq:URLRequest;
    tempURLReq = new URLRequest(tempStr);
    
    //Open the URL in a new window.
    navigateToURL(tempURLReq, "_blank");
   }
  ]]>
 </fx:Script>
 <fx:Declarations>
  <!-- Place non-visual elements (e.g., services, value objects) here -->
 </fx:Declarations>
 <s:VGroup verticalAlign="middle" 
     horizontalAlign="center" 
     width="100%" 
     height="100%">
  <s:Spacer height="100%"/>
  <s:Label text="{strLabel}"/>
  <s:TextArea maxChars="140"  
     id="txtInput"
     heightInLines="4"
      width="50%" 
     text="{msg}"/>
  <s:Button label="Using escape()" 
      click="clickHandler(event)" 
      width="200"
      id="btn1"/>
  <s:Button label="Using encodeURIComponent()"
      click="clickHandler(event)"
      width="200"
      id="btn2"/>
  <s:Spacer height="100%"/>
 </s:VGroup>
</s:Application>
* Click here for the demo shown in this post.
^ Click here for the source files for the demo.

Friday, May 10, 2013

Flex: Stacking Column Chart

There might be a situation where you would want to stack 2 different set of column data together. (For example, you are managing a toys department and you wanted to show the total of the number of toy cars and toy aeroplane that you have sold on that day. On top of that, you also want to find out the breakdown of the number of toy cars and toy aeroplanes that you have sold that particular day.) So how to stack all the column data together? Here's a simple example that I have created.

Here's the source code of my main application - SimpleStackingColumn.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"
      backgroundColor="#CDCDCD"
      creationComplete="creationCompleteEvent(event)"> 
 <fx:Declarations>
  <!-- 
  We need to specify the types of animations over here
  -->
  <s:Parallel id="parallelEffect">
   <s:Fade duration="1000" alphaFrom="0" alphaTo="1"/>
   <mx:SeriesSlide duration="1000" direction="up"/>
  </s:Parallel>
  <s:Parallel id="parallelCarEffect">
   <s:Fade duration="1000" alphaFrom="0" alphaTo="1"/>
  </s:Parallel>
 </fx:Declarations>
 <fx:Script>
  <![CDATA[
   import flashx.textLayout.conversion.TextConverter;
   
   import mx.charts.HitData;
   import mx.charts.chartClasses.IChartElement2;
   import mx.charts.chartClasses.Series;
   import mx.charts.series.ColumnSeries;
   import mx.charts.series.LineSeries;
   import mx.collections.ArrayCollection;
   import mx.collections.Sort;
   import mx.collections.SortField;
   import mx.events.FlexEvent;
   import mx.formatters.DateFormatter;
   import mx.graphics.Stroke;
   
   import spark.events.IndexChangeEvent;
   
   //Records used in the chart
   [Bindable]
   private var myData:XML = 
    <records> 
     <record>
      <date>01/09/2013</date>
      <people>63</people>
      <car>23</car>
     </record>
     <record>
      <date>02/09/2013</date>
      <people>61</people>
      <car>81</car>
     </record>
     <record>
      <date>03/09/2013</date>
      <people>67</people>
      <car>47</car>
     </record>
     <record>
      <date>04/09/2013</date>
      <people>75</people>
      <car>95</car>
     </record>
     <record>
      <date>05/09/2013</date>
      <people>65</people>
      <car>45</car>
     </record>
     <record>
      <date>06/09/2013</date>
      <people>32</people>
      <car>52</car>
     </record>
     <record>
      <date>07/09/2013</date>
      <people>66</people>
      <car>46</car>
     </record>
     <record>
      <date>08/09/2013</date>
      <people>85</people>
      <car>105</car>
     </record>
     <record>
      <date>09/09/2013</date>
      <people>37</people>
      <car>57</car>
     </record>
     <record>
      <date>10/09/2013</date>
      <people>80</people>
      <car>100</car>
     </record>
    </records>;
   
   private var localSeries:ColumnSeries = new ColumnSeries();
   private var localCarSeries:ColumnSeries = new ColumnSeries();
   
   protected function creationCompleteEvent(event:FlexEvent):void
   {
    //Create the SortField object for the "time" field in 
    //the ArrayCollection object, and make sure we do a 
    //numeric sort.
    var dataSortField:SortField = new SortField();
    dataSortField.name = "time";
    dataSortField.numeric = true;
    
    //Create the Sort object and add the SortField object 
    //created earlier to the array of fields to sort on.
    var numericDataSort:Sort = new Sort();
    numericDataSort.fields = [dataSortField];
    
    // Parsing the xml data into ArrayCollection
    var objArray:ArrayCollection = new ArrayCollection();
    var tempObj:Object;
    var dateArray:Array;
    var tempDate:Date;
    for(var i:int = 0; i < myData.record.length(); i ++)
    {
     tempObj = new Object();
     dateArray = String(myData.record[i].date).split("/");
     //Convert the date data into a Date Object
     tempDate = new Date(dateArray[2], 
      Number(dateArray[1]) - 1, 
      dateArray[0]);
     tempObj.date = tempDate;
     tempObj.time = tempDate.time;
     //Adding the values of all the series together
     tempObj.data = Number(myData.record[i].people) + 
      Number(myData.record[i].car);
     //Create a new object for the data tooltip
     tempObj.people = myData.record[i].people;
     tempObj.label = dateFormatter(tempDate);
     objArray.addItem(tempObj);
    }
    
    objArray.sort = numericDataSort;
    objArray.refresh();
    
    //Create the new series and set its properties.
    localSeries.dataProvider = objArray;
    localSeries.yField = "data";
    localSeries.xField = "date";
    //Create alternate colors for the columns
    localSeries.setStyle("fill", 0xCDFFCD);
    //Create the strokes for the columns
    localSeries.setStyle("stroke", 
     new Stroke(0xFFFFFF, 0.1, 0.5));
    localSeries.displayName = "col_people";
    
    objArray = new ArrayCollection();
    for(i = 0; i < myData.record.length(); i ++)
    {
     tempObj = new Object();
     dateArray = String(myData.record[i].date).split("/");
     //Convert the date data into a Date Object
     tempDate = new Date(dateArray[2], 
      Number(dateArray[1]) - 1, 
      dateArray[0]);
     tempObj.date = tempDate;
     tempObj.time = tempDate.time;
     //Don't need to adding the values of all the 
     //series together
     tempObj.data = myData.record[i].car;
     //Create a new object for the data tooltip
     tempObj.car = myData.record[i].car;
     tempObj.label = dateFormatter(tempDate);
     objArray.addItem(tempObj);
    } 
    
    objArray.sort = numericDataSort;
    objArray.refresh();
    
    //Create the new series and set its properties.
    localCarSeries.dataProvider = objArray;
    localCarSeries.yField = "data";
    localCarSeries.xField = "date";
    //Create alternate colors for the columns
    localCarSeries.setStyle("fill", 0xCDCDFF);
    //Create the strokes for the columns
    localCarSeries.setStyle("stroke", 
     new Stroke(0xFFFFFF, 0.1, 0.5));
    localCarSeries.displayName = "line_car";
    
    //We will remove all the series attach to the chart
    //first
    chart.series = null;
    
    //End all the effects first, else some glich will
    //appear.
    parallelEffect.end();
    parallelCarEffect.end();
    
    //Base on the type of animation selected, attach 
    //the effect to the column
    localSeries.setStyle("creationCompleteEffect", 
     parallelEffect);
    localCarSeries.setStyle("creationCompleteEffect", 
     parallelCarEffect);
    
    // Back up the current series on the chart.
    var currentSeries:Array = chart.series;
    // Add the new series to the current Array of series.
    currentSeries.push(localSeries);
    currentSeries.push(localCarSeries);
    // Add the new Array of series to the chart.
    chart.series = currentSeries;
   }
   
   //This function will return a string based on the
   //Date format DD/MM/YYYY.
   private function dateFormatter(tempDate:Date):String
   {
    var fmt:DateFormatter = new DateFormatter();
    fmt.formatString = "DD/MM/YYYY";
    return fmt.format(tempDate);
   }
   
   //We are customizing the datatip / tool tip of the
   //chart data.
   public function myDataTipFunction(e:HitData):String {
    var s:String = "";
    var tempDate:Date = e.item.date as Date;
    s += "Date: " + dateFormatter(tempDate) + "<br>";
    if(Series(e.element).displayName == "col_people")
    {
     s += "No. of People: " + e.item.people;
    }else{
     s += "No. of Cars: " + e.item.car;
    }
    return s;
   }
   
   //This function will be used to change the date labels of
   //the chart to match the data.
   public function createDate(s:Date):Date {    
    var newDate:Date = new Date();
    newDate.time = s.time;
    //We need to increase a day to the labels.
    newDate.date += 1;
    return newDate;
   }
  ]]>
 </fx:Script>
 <s:VGroup width="100%" 
     height="100%"
     verticalAlign="middle"
     horizontalAlign="center">
  <s:BorderContainer width="100%"
         backgroundAlpha="0"
         borderVisible="false">
   <s:HGroup verticalAlign="middle" horizontalAlign="center"
       width="100%"
       height="100%">
    <!-- Need to set the gutterLeft and 
    gutterTop of the chart -->
    <mx:CartesianChart id="chart"
           gutterTop="0"
           gutterLeft="50"
           showDataTips="true" 
           width="80%"
           height="80%"
           dataTipFunction="myDataTipFunction">
     <mx:horizontalAxis>
      <mx:DateTimeAxis dataUnits="days" id="dateAxis" 
           alignLabelsToUnits="false" 
           parseFunction="createDate"/> 
     </mx:horizontalAxis>
    </mx:CartesianChart>
   </s:HGroup>
  </s:BorderContainer>
 </s:VGroup>
</s:Application>
* Click here for the demo shown in this post.
^ Click here for the source files for the demo.

Friday, April 26, 2013

Flex: Chart Data interaction

There might be numerous situations where you have to interact with a chart data and at the same time, perform some type of actions. Therefore, I'm going to show you how you can perform a simple action upon clicking on a chart data.

Here's the source code of my main application - SimpleChartDataClick.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"
      backgroundColor="#CDCDCD"
      creationComplete="creationCompleteEvent(event)"> 
 <fx:Declarations>
  <!-- 
  We need to specify the types of animations over here
  -->
  <s:Parallel id="parallelEffect">
   <s:Fade duration="1000" alphaFrom="0" alphaTo="1"/>
   <mx:SeriesSlide duration="1000" direction="up"/>
  </s:Parallel>
  <s:Parallel id="parallelCarEffect">
   <s:Fade duration="1000" alphaFrom="0" alphaTo="1"/>
  </s:Parallel>
 </fx:Declarations>
 <fx:Script>
  <![CDATA[
   import flashx.textLayout.conversion.TextConverter;
   
   import mx.charts.HitData;
   import mx.charts.chartClasses.IChartElement2;
   import mx.charts.chartClasses.Series;
   import mx.charts.events.ChartItemEvent;
   import mx.charts.series.ColumnSeries;
   import mx.charts.series.LineSeries;
   import mx.collections.ArrayCollection;
   import mx.collections.Sort;
   import mx.collections.SortField;
   import mx.events.FlexEvent;
   import mx.formatters.DateFormatter;
   import mx.graphics.Stroke;
   
   import spark.events.IndexChangeEvent;
   
   //Records used in the chart
   [Bindable]
   private var myData:XML = 
    <records> 
     <record>
      <date>01/09/2013</date>
      <people>63</people>
      <car>23</car>
     </record>
     <record>
      <date>02/09/2013</date>
      <people>61</people>
      <car>81</car>
     </record>
     <record>
      <date>03/09/2013</date>
      <people>67</people>
      <car>47</car>
     </record>
     <record>
      <date>04/09/2013</date>
      <people>75</people>
      <car>95</car>
     </record>
     <record>
      <date>05/09/2013</date>
      <people>65</people>
      <car>45</car>
     </record>
     <record>
      <date>06/09/2013</date>
      <people>32</people>
      <car>52</car>
     </record>
     <record>
      <date>07/09/2013</date>
      <people>66</people>
      <car>46</car>
     </record>
     <record>
      <date>08/09/2013</date>
      <people>85</people>
      <car>105</car>
     </record>
     <record>
      <date>09/09/2013</date>
      <people>37</people>
      <car>57</car>
     </record>
     <record>
      <date>10/09/2013</date>
      <people>80</people>
      <car>100</car>
     </record>
    </records>;
   
   private var localSeries:ColumnSeries = new ColumnSeries();
   private var localCarSeries:ColumnSeries = new ColumnSeries();
   
   protected function creationCompleteEvent(event:FlexEvent):void
   {
    //Create the SortField object for the "time" field in 
    //the ArrayCollection object, and make sure we do a 
    //numeric sort.
    var dataSortField:SortField = new SortField();
    dataSortField.name = "time";
    dataSortField.numeric = true;
    
    //Create the Sort object and add the SortField object 
    //created earlier to the array of fields to sort on.
    var numericDataSort:Sort = new Sort();
    numericDataSort.fields = [dataSortField];
    
    // Parsing the xml data into ArrayCollection
    var objArray:ArrayCollection = new ArrayCollection();
    var tempObj:Object;
    var dateArray:Array;
    var tempDate:Date;
    for(var i:int = 0; i < myData.record.length(); i ++)
    {
     tempObj = new Object();
     dateArray = String(myData.record[i].date).split("/");
     //Convert the date data into a Date Object
     tempDate = new Date(dateArray[2], 
      Number(dateArray[1]) - 1, 
      dateArray[0]);
     tempObj.date = tempDate;
     tempObj.time = tempDate.time;
     tempObj.people = myData.record[i].people;
     tempObj.label = dateFormatter(tempDate);
     objArray.addItem(tempObj);
    }
    
    objArray.sort = numericDataSort;
    objArray.refresh();
    
    //Create the new series and set its properties.
    localSeries.dataProvider = objArray;
    localSeries.yField = "people";
    localSeries.xField = "date";
    //Create alternate colors for the columns
    localSeries.setStyle("fill", 0xCDFFCD);
    //Create the strokes for the columns
    localSeries.setStyle("stroke", 
     new Stroke(0xFFFFFF, 0.1, 0.5));
    localSeries.displayName = "col_people";
    //Set the width(%) of the column 
    localSeries.columnWidthRatio = 0.3;
    //Set the offset
    localSeries.offset = -0.15;
    
    objArray = new ArrayCollection();
    for(i = 0; i < myData.record.length(); i ++)
    {
     tempObj = new Object();
     dateArray = String(myData.record[i].date).split("/");
     //Convert the date data into a Date Object
     tempDate = new Date(dateArray[2], 
      Number(dateArray[1]) - 1, 
      dateArray[0]);
     tempObj.date = tempDate;
     tempObj.time = tempDate.time;
     tempObj.car = myData.record[i].car;
     tempObj.label = dateFormatter(tempDate);
     objArray.addItem(tempObj);
    } 
    
    objArray.sort = numericDataSort;
    objArray.refresh();
    
    //Create the new series and set its properties.
    localCarSeries.dataProvider = objArray;
    localCarSeries.yField = "car";
    localCarSeries.xField = "date";
    //Create alternate colors for the columns
    localCarSeries.setStyle("fill", 0xCDCDFF);
    //Create the strokes for the columns
    localCarSeries.setStyle("stroke", 
     new Stroke(0xFFFFFF, 0.1, 0.5));
    localCarSeries.displayName = "line_car";
    //Set the width(%) of the column 
    localCarSeries.columnWidthRatio = 0.3;
    //Set the offset
    localCarSeries.offset = 0.15;
    
    //We will remove all the series attach to the chart
    //first
    chart.series = null;
    
    //End all the effects first, else some glich will
    //appear.
    parallelEffect.end();
    parallelCarEffect.end();
    
    //Base on the type of animation selected, attach 
    //the effect to the column
    localSeries.setStyle("creationCompleteEffect", 
     parallelEffect);
    localCarSeries.setStyle("creationCompleteEffect", 
     parallelCarEffect);
    
    // Back up the current series on the chart.
    var currentSeries:Array = chart.series;
    // Add the new series to the current Array of series.
    currentSeries.push(localCarSeries);
    currentSeries.push(localSeries);
    // Add the new Array of series to the chart.
    chart.series = currentSeries;
   }
   
   //This function will return a string based on the
   //Date format DD/MM/YYYY.
   private function dateFormatter(tempDate:Date):String
   {
    var fmt:DateFormatter = new DateFormatter();
    fmt.formatString = "DD/MM/YYYY";
    return fmt.format(tempDate);
   }
   
   //We are customizing the datatip / tool tip of the
   //chart data.
   public function myDataTipFunction(e:HitData):String {
    var s:String = "";
    var tempDate:Date = e.item.date as Date;
    s += "Date: " + dateFormatter(tempDate) + "<br>";
    if(Series(e.element).displayName == "col_people")
    {
     s += "No. of People: " + e.item.people;
    }else{
     s += "No. of Cars: " + e.item.car;
    }
    return s;
   }
   
   //This function will be used to change the date labels of
   //the chart to match the data.
   public function createDate(s:Date):Date {    
    var newDate:Date = new Date();
    newDate.time = s.time;
    //We need to increase a day to the labels.
    newDate.date += 1;
    return newDate;
   }  
   
   //This function will toggle the visibility of the chart
   //Data based on the values of the respective check boxes.
   protected function chkChartChangeEvent(event:Event):void
   {
    var showCarData:Boolean = chkCar.selected;
    var showPeopleData:Boolean = chkPeople.selected;
    for(var i:int = 0; i < chart.series.length; i ++)
    {
     if(chart.series[i].displayName == "line_car")
     {
      chart.series[i].visible = showCarData;
     }
     if(chart.series[i].displayName == "col_people")
     {
      chart.series[i].visible = showPeopleData;
     }
    }
   }
   
   //Upon clicking on one of the columns, this function
   //will update the label 'lblResult' based on the data
   //from the selected chart item.
   protected function itemClickEvent(e:ChartItemEvent):void
   {
    var str:String = "You have click on the item on ";
    var tempDate:Date = new Date();
    tempDate.time = e.hitData.item.date.time;
    str += dateFormatter(tempDate) + " with the data ";
    var tempValue:String = "";
    if(Series(e.hitData.element).displayName == "col_people")
    {
     str += "People: "; 
     tempValue = e.hitData.item.people;
    }else{
     str += "Car: ";
     tempValue = e.hitData.item.car;
    }
    str += tempValue;
    lblResult.text = str;
   }
   
  ]]>
 </fx:Script>
 <s:VGroup width="100%" 
     height="100%"
     verticalAlign="middle"
     horizontalAlign="center">
  <s:BorderContainer width="100%"
         backgroundAlpha="0"
         borderVisible="false">
   <s:HGroup verticalAlign="middle" horizontalAlign="center"
       width="100%"
       height="100%">
    <!-- Need to set the gutterLeft and 
    gutterTop of the chart -->
    <mx:CartesianChart id="chart"
           gutterTop="0"
           gutterLeft="50"
           showDataTips="true" 
           width="80%"
           height="80%"
           itemClick="itemClickEvent(event)"
           dataTipFunction="myDataTipFunction">
     <mx:horizontalAxis>
      <mx:DateTimeAxis dataUnits="days" id="dateAxis" 
           alignLabelsToUnits="false" 
           parseFunction="createDate"/> 
     </mx:horizontalAxis>
    </mx:CartesianChart>
   </s:HGroup>
  </s:BorderContainer>
  <s:HGroup width="100%" horizontalAlign="center" 
      verticalAlign="middle">
   <s:CheckBox id="chkCar"
      change="chkChartChangeEvent(event)" 
      selected="true"
      label="Show Car Data"/>
   <s:CheckBox id="chkPeople"
      change="chkChartChangeEvent(event)" 
      selected="true"
      label="Show People Data"/>
  </s:HGroup>
  <s:Spacer height="10"/>
  <s:HGroup width="100%" horizontalAlign="center" 
      verticalAlign="middle">
   <s:Label text="For your info:"/>
  </s:HGroup>
  <s:HGroup width="100%" horizontalAlign="center" 
      verticalAlign="middle">
   <s:Label id="lblResult"/>
  </s:HGroup>
 </s:VGroup>
</s:Application>
* Click here for the demo shown in this post.
^ Click here for the source files for the demo.
~ Click here to find out more about Chart Data Interactions in Flex.

Saturday, April 13, 2013

FB + Adobe AIR: Posting Bug

My friends were asking me to help them to debug an interest problem that they are facing with the Facebook Graph Desktop API.

The scenario as follows. A user log in to the desktop application (Adobe AIR) through Facebook and he decided to post something on his Facebook wall through the application and decides to log out of the Facebook and Application. If a second user tries to log in and post something to his Facebook wall using the application, the message will appear on the wall on the first user, rather than the second user. Therefore here's a fix to that issue if you are using the source files of the 'Facebook Graph Desktop API'.
You have to modify the following function of the following file - com\facebook\graph\FacebookDesktop.as From
    public static function api(method:String,
                     callback:Function,
                     params:* = null,
                     requestMethod:String = 'GET'
    ):void {
      getInstance().api(method,
        callback,
        params,
        requestMethod
      );
    }
To
    public static function api(method:String,
                     callback:Function,
                     params:* = null,
                     requestMethod:String = 'GET'
    ):void {

      if(params != null)
      {
        if (getInstance().session != null) {
          params.access_token = getInstance().session.accessToken;
        }
      }
      getInstance().api(method,
        callback,
        params,
        requestMethod
      );
    }
* Click here for the updated file 'FacebookDesktop.as'.
^ Click here to find out more about the 'Facebook Graph Desktop API'.

Wednesday, March 27, 2013

Flex 4: Simple 2 Way Binding

In the older versions of flex, we can make use of the 'Bindable' tag to reduce the amount of coding required. However, once we give users the ability to change a value of a component that was previous bind to a value, that's where all the extra coding comes in. Therefore in the newer version of flex, we have the privilege to do 2 way binding, which is pretty helpful. Anyway let's take a look at the codes of the simple demo that I have created.

Source Code for the main application - 'Simple2WayBinding.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"
      backgroundColor="#CDCDCD" 
      creationComplete="creationCompleteEvent(event)" xmlns:zcs="com.zcs.*">
 <fx:Declarations>
  <zcs:OppositeBooleanObj id="oppObj"/>
  <zcs:OppositeBooleanObj id="oppObj1"/>
 </fx:Declarations>
 <fx:Script>
  <![CDATA[
   import mx.binding.utils.BindingUtils;
   import mx.events.FlexEvent;
   
   protected function creationCompleteEvent(event:FlexEvent):void
   {
    //We are creating a 2-way binding using the following liners.
    //As you can see, the selected value of the following
    //checkboxes, 'chk1' and 'chk4' will be bind 2-ways.
    BindingUtils.bindProperty(chk1, "selected", chk4, "selected");
    BindingUtils.bindProperty(chk4, "selected", chk1, "selected");
   }
  ]]>
 </fx:Script>
 <fx:Binding twoWay="true" 
    source="chk1.selected" 
    destination="chk2.selected"/>
 <s:VGroup width="100%" 
     height="100%"
     verticalAlign="middle"
     horizontalAlign="center">
  <s:Spacer height="100%"/>
  <s:HGroup verticalAlign="middle" 
      horizontalAlign="center"
      width="100%">
   <s:TextArea borderVisible="false"
      contentBackgroundAlpha="0"
      textAlign="center"
      width="100%" height="20">
    This is using <fx:Binding> to bind the 
    values of the properties together.
   </s:TextArea>   
  </s:HGroup>
  <s:HGroup verticalAlign="middle" 
      horizontalAlign="center"
      width="100%">
   <s:CheckBox label="This is (1)."
       id="chk1"/>
  </s:HGroup>
  <s:HGroup verticalAlign="middle" 
      horizontalAlign="center"
      width="100%">
   <s:CheckBox label="This will always have the same value as (1)."
      id="chk2"/>
  </s:HGroup>
  <s:Spacer height="100%"/>
  <s:HGroup verticalAlign="middle" 
      horizontalAlign="center"
      width="100%">
   <s:TextArea borderVisible="false"
      contentBackgroundAlpha="0"
      textAlign="center"
      width="100%" height="20">
    This is using a '@' symbol before the curly brackets
    to bind the values of the properties to a object.    
   </s:TextArea>   
  </s:HGroup>
  <s:HGroup verticalAlign="middle" 
      horizontalAlign="center"
      width="100%">
   <s:CheckBox label="This is (2)."
      selected="@{oppObj.value1}"/>
  </s:HGroup>
  <s:HGroup verticalAlign="middle" 
      horizontalAlign="center"
      width="100%">
   <s:CheckBox label="This will always have a different value as (2)."
       selected="@{oppObj.value2}"/>
  </s:HGroup>
  <s:Spacer height="100%"/>
  <s:HGroup verticalAlign="middle" 
      horizontalAlign="center"
      width="100%">
   <s:TextArea borderVisible="false"
      contentBackgroundAlpha="0"
       textAlign="center"
      width="100%" height="20">
    This is using 'BindingUtils' to bind the value of the
    property of this check box to '(1)'.
   </s:TextArea>   
  </s:HGroup>
  <s:HGroup verticalAlign="middle" 
      horizontalAlign="center"
      width="100%">
   <s:CheckBox label="This will always have the same value as (1)."
      id="chk4"/>
  </s:HGroup>
  <s:Spacer height="100%"/>
 </s:VGroup>
</s:Application>

Source Code for the simple Object used in this demo - 'OppositeBooleanObj.as'
package com.zcs
{
 import flash.events.Event;
 import flash.events.EventDispatcher;
 import flash.events.IEventDispatcher;
 
 public class OppositeBooleanObj extends EventDispatcher
 {
  private var _value1:Boolean = true;
  private var _value2:Boolean = false;

  [Bindable(event="value1Changed")]
  public function get value1():Boolean
  {
   return _value1;
  }

  //change the value of 'value2' when 'value1' changes
  //We are dispatching the event 'value2Changed' to
  //trigger data binding.
  public function set value1(value:Boolean):void
  {
   _value1 = value;
   _value2 = !_value1;
   dispatchEvent(new Event("value2Changed")); 
  }
  
  [Bindable(event="value2Changed")]
  public function get value2():Boolean
  {
   return _value2;
  }

  //change the value of 'value1' when 'value2' changes
  //We are dispatching the event 'value1Changed' to
  //trigger data binding.
  public function set value2(value:Boolean):void
  {
   _value2 = value;
   _value1 = !_value2;
   dispatchEvent(new Event("value1Changed"));
  }
 }
}
* Click here for the demo shown in this post.
^ Click here for the source files for the demo.

Saturday, March 23, 2013

AS3: How to enable Double Clicking

Well, probably everyone knows about this, but just if case you are new to the as3 world... Here goes.

Here's the codes for my main application file.
<?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"
      backgroundColor="#CDCDCD">
 <fx:Script>
  <![CDATA[
   [Bindable]
   private var _enabledDoubleClick:Boolean = false;
   
   [Bindable]
   private var _bkgdColor:uint = 0xFFFFFF;
   
   //Upon each successful double click, we will change the 
   //background color of the box. :D
   protected function doubleClickHandler(event:MouseEvent):void
   {
    _bkgdColor = Math.random() * 0xFFFFFF; 
   }
  ]]>
 </fx:Script>
 <s:VGroup width="100%" 
     height="100%"
     verticalAlign="middle"
     horizontalAlign="center">
  <s:Spacer height="100%"/>
  <s:HGroup verticalAlign="middle" 
      horizontalAlign="center"
      width="100%">
   <s:TextArea borderVisible="false"
      contentBackgroundAlpha="0"
      width="50%" height="50">
    Double Clicking will only work when the CheckBox is selected. 
    (When the checkbox is selected, it will bind the value of the 
    CheckBox to a Boolean variable that will change the value of 
    the property doubleClickEnabled of the box below.)
   </s:TextArea>
  </s:HGroup>
  <s:HGroup verticalAlign="middle" 
      horizontalAlign="center"
      width="100%">
   <s:CheckBox selected="@{_enabledDoubleClick}"
       label="Enable Double Clicking?"/>
  </s:HGroup>
  <s:HGroup verticalAlign="middle" 
      horizontalAlign="center"
      width="100%">
   <s:BorderContainer width="50%"
          height="50%"
          backgroundColor="{_bkgdColor}"
          cornerRadius="10"
          mouseChildren="false"
          doubleClickEnabled="{_enabledDoubleClick}"
          doubleClick="doubleClickHandler(event)">
    <s:layout>
     <s:HorizontalLayout horizontalAlign="center"
           verticalAlign="middle"/>
    </s:layout>
    <s:Label text="Try Double Clicking Me!"/>
   </s:BorderContainer>
  </s:HGroup>
  <s:Spacer height="100%"/>
 </s:VGroup>
</s:Application>

* Click here for the demo shown in this post.
^ Click here for the source files for the demo.

Friday, March 15, 2013

AS3: event.stopImmediatePropagation() v.s. event.stopPropagation()

When you are working on a flash/flex project and there's multiple layers inside the project, there ought to be changes where you have multiple places listening for the same Events. Luckily for us, there are ways to stop the Events from dispatching upwards. However there seems to be 2 different methods to stop it and what's the difference between the 2 methods event.stopImmediatePropagation() and event.stopPropagation()? I have created a simple demo to show you the differences between both methods.

Source code of the Main Application file - stopBubbleEventTest.mxml
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" 
    minWidth="955" minHeight="600" 
    xmlns:Comp="*" creationComplete="creationCompleteEvent(event)" 
    name="stage">
 <!-- Styling the VBox -->
 <mx:Style>
  VBox{
   paddingLeft:10;
   paddingRight:10;
   backgroundAlpha:0.2;
   backgroundColor:#00FF00;
  }
 </mx:Style>
 <mx:Script>
  <![CDATA[
   import mx.events.FlexEvent;
   
   //Upone creation complete of this mxml files
   protected function creationCompleteEvent(event:FlexEvent):void
   {
    //Add an Event Listener to the button / 
    //Upon clicking on the button, run the actions in clickEvent function
    btnClick.addEventListener(MouseEvent.CLICK, clickEvent);
    //Change the state of the checkbox of the highest level
    stageControls.chkOption.dispatchEvent(new MouseEvent(MouseEvent.CLICK));
   }
   
   private function clickEvent(event:Event):void{
    //Reset all the values in the textfields
    stageControls.txtResult.text = "";
    level0Controls.txtResult.text = "";
    level1Controls.txtResult.text = "";
    level2Controls.txtResult.text = "";
    level3Controls.txtResult.text = "";
    level4Controls.txtResult.text = "";
    stageControls.txtResult2.text = "";
    level0Controls.txtResult2.text = "";
    level1Controls.txtResult2.text = "";
    level2Controls.txtResult2.text = "";
    level3Controls.txtResult2.text = "";
    level4Controls.txtResult2.text = "";
    //Dispatch an event that will bubble / move all the way to the highest level
    btnClick.dispatchEvent(new DataEvent(DataEvent.DATA,true,false,"Click"));
   }
   
  ]]>
 </mx:Script>
 <mx:HBox width="100%" horizontalAlign="center">
  <mx:CheckBox id="chkEventType" selected="false"
      label="Are we using event.stopImmediatePropagation()?"/>
 </mx:HBox>
 <Comp:FormControls id="stageControls" 
        useImmediate="{chkEventType.selected}"
        top="30"/>
 <mx:VBox name="level0" width="100%" height="100%" verticalGap="0" top="80">
  <Comp:FormControls id="level0Controls"
         useImmediate="{chkEventType.selected}"/>
  <mx:VBox name="level1" width="100%" height="100%" verticalGap="0">
   <Comp:FormControls id="level1Controls"
          useImmediate="{chkEventType.selected}"/>
   <mx:VBox name="level2" width="100%" height="100%" verticalGap="0">
    <Comp:FormControls id="level2Controls"
           useImmediate="{chkEventType.selected}"/>
    <mx:VBox name="level3" width="100%" height="100%" verticalGap="0">
     <Comp:FormControls id="level3Controls"
            useImmediate="{chkEventType.selected}"/>
     <mx:VBox name="level4" width="100%" height="100%" verticalGap="0">
      <Comp:FormControls id="level4Controls"
             useImmediate="{chkEventType.selected}"/>
      <mx:Button id="btnClick" label="Click me..."/>
     </mx:VBox>
    </mx:VBox>
   </mx:VBox>
  </mx:VBox>
 </mx:VBox>
</mx:Application>
SOurce code of my custom component - FormControls.mxml
<?xml version="1.0" encoding="utf-8"?>
<mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml" width="100%" creationComplete="creationCompleteEvent(event)">
 <mx:Style>
  
 </mx:Style>
 <mx:Script>
  <![CDATA[
   import mx.events.FlexEvent;
   
   //This bindable variable will be used to determine the
   //following:
   //_useStopWithImmediate = false => 
   // we use event.stopPropagation();
   //_useStopWithImmediate = true => 
   // we use event.stopImmediatePropagation();
   [bindable]
   private var _useImmediate:Boolean = false;

   public function get useImmediate():Boolean
   {
    return _useImmediate;
   }

   public function set useImmediate(value:Boolean):void
   {
    _useImmediate = value;
   }
   
   //Upone creation complete of this view
   protected function creationCompleteEvent(event:FlexEvent):void
   {
    //Add the listeners for DATA
    this.parent.addEventListener(DataEvent.DATA, displayEvent);
    this.parent.addEventListener(DataEvent.DATA, display2Event);
   }
   
   //When the parent view had capture a call for DataEvent.DATA
   private function displayEvent(event:Event):void{
    if(chkOption.selected){
     txtResult.text = "Event had been listened at " +
      "first field of " + this.parent.name + ".";
     //Stops the event from bubbling / moving upwards
     //Remove this line to see how you can listen for the 
     //same event in all the selected levels
     if(_useImmediate)
     {
      event.stopImmediatePropagation();
     }else{
      event.stopPropagation(); 
     }
    }
   }  
   
   //When the parent view had capture a call for DataEvent.DATA
   private function display2Event(event:Event):void{
    if(chkOption2.selected){
     txtResult2.text = "Event had been listened at " +
      "second field of " + this.parent.name + ".";
    }
   }  
  ]]>
 </mx:Script>
 <mx:VBox width="100%">
  <mx:HBox width="100%">
   <mx:Text id="txtResult" text=""/>
   <mx:Spacer width="100%"/>
   <mx:CheckBox id="chkOption" 
       label="Select me to check for Event 1"/>
  </mx:HBox>
  <mx:HBox width="100%">
   <mx:Text id="txtResult2" text=""/>
   <mx:Spacer width="100%"/>
   <mx:CheckBox id="chkOption2" 
       label="Select me to check for Event 2"/>
  </mx:HBox>
 </mx:VBox>
</mx:Canvas>
* Click here for the demo shown in this post.
^ Click here for the source files for the demo.