Posts Tagged ‘actionscript’

App Size: Flex vs Actionscript Project (Flex Framework Caching)

January 14, 2011

Following on from discussions at the ‘after’ part of last nights MMUG-Dublin meeting, I created a pure Actionscript 3 project and a similar Flex application project, both in Flex Builder 3. The aim was to compare final swf size. Both apps draw 3 circles on the screen.

Three circles, well done

Size Wise:

Flex 3 Application SWF (Release Build of Project): 169KB (but only 65KB with framework caching – see Optimization section below)

Pure Actionscript 3 Project SWF: 1KB

Optimization – Framework Caching

By default, the flex framework gets merged into your main swf file, adding around 100KB+. This can be avoided by going to Properties/Flex Build Path/Library Path/Framework Linkage/ and selecting Runtime Shared Library (RSL) from the dropdown. When you do a Build Release Project, your framework will be exported as a signed swz and an unsigned swf . These files should be placed in the same directory as your main swf file on the web server. In Flex 4, the framework RSL will automatically point to a public section of the Adobe site, where the swz’s will be available. Saves you having to host them.

The framework will only need to be downloaded by a particular client once, and will be cached by Flash Player, meaning every subsequent use of your app will not require downloading of the framework.swz (in the release will be named something like framework_3.2.0.3958.swz).

Read more about framework caching here: http://ted.onflash.org/2008/01/flex-3-framework-caching.php

Conclusion

If Actionscript will suffice e.g. for drawing three circles on the screen, go with a pure AS3 Project. If your developing data driven applications and user interfaces, use the Flex SDK (saves a tonne of time and effort, and whats 60kb these days – less than most peoples profile pics). Have a look at this Stackoverflow question for more opinions on each approaches pros and cons.

Code

Flex

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute"
	initialize="onInitialize()">
	<mx:Script>
		<![CDATA[
			import mx.core.UIComponent;

			private function onInitialize():void{
				drawCircle( 0x336699 );
	            drawCircle( 0x993333 );
	            drawCircle( 0x339933 );
			}

			private function drawCircle( color:uint ):void
	        {
	            var circle:Shape = new Shape();
	            var displayObject:UIComponent = new UIComponent();

	            circle.graphics.beginFill( color );
	            circle.graphics.lineStyle( 2, 0xCCCCCC );
	            circle.graphics.drawCircle( 30, 40, 30);
	            circle.graphics.endFill();
	            circle.x = (this.numChildren * 65) + 10;
	            circle.y = 0;

	            displayObject.addChild(circle);
	            this.addChild(displayObject);
	        }
		]]>
	</mx:Script>
</mx:Application>

Actionscript Project

package {
	import flash.display.Shape;
	import flash.display.Sprite;
	import flash.display.StageAlign;
	import flash.display.StageScaleMode;

	public class ActionScriptProject extends Sprite
	{
		public function ActionScriptProject()
		{
			stage.scaleMode = StageScaleMode.NO_SCALE;
            stage.align = StageAlign.TOP_LEFT;
            doDrawCircle( 0x336699 );
            doDrawCircle( 0x993333 );
            doDrawCircle( 0x339933 );
		}

		private function doDrawCircle( color:uint ):void
        {
            var child:Shape = new Shape();
            child.graphics.beginFill( color );
            child.graphics.lineStyle( 2, 0xCCCCCC );
            child.graphics.drawCircle( 30, 40, 30);
            child.graphics.endFill();
            child.x = (this.numChildren * 65) + 10;
            child.y = 0;
            addChild(child);
        }

	}
}

Module Optimization: Link report and load externs

January 12, 2011

When modules are released or built (using Clean or Project>Export Release Build..), the exported module swf’s will include any Flash/Flex framework classes and external swc/swf classes that are used by the modules.

This is not always optimal, as some of the files included in the Module’s swf’s may already be in used by the Main app, and included in the MainApp.swf. You can remove the overlapping classes from the module swf’s by doing the following:

  1. In your main app i.e. the one that will use the module, go to Project>Properties>Flex Compiler and add an Additional compiler arguments:

-link-report=link_report.xml

  1. Clean or Export Release Build the project.
  2. Copy the link_report.xml file from the bin-debug or bin-release folder and paste it to the root of the module project folder i.e. the same folder as the Module.mxml file(s).
  3. Repeat step 1 for the Modules project, and add the compiler arguments:

-load-externs=link_report.xml

  1. Clean or Export Release Build the project.

The released module swf’s will not include any external files that it shares with the main app. Reduced the size of the module I was optimizing in work by 15%!

Additional Compiler Arguments

More details on the compiler arguments and module optimization from:

http://livedocs.adobe.com/flex/3/html/help.html?content=modular_4.html

Caution: any time you change your main app, and remove or change dependencies or SDK versions, you will need to repeat the above process. The modules will not work with apps that they have not been optimized for.

If you receive an error message -load-externs “could not open …..”, make sure that the xml file is at the root of the module project.

Example multidimensional / 3D indexed array in Flex AS3

July 26, 2010

Scenario: I recently created a Flex component that extended the Canvas, and was split into a large number of ‘cells’ using constraint rows/columns. In each of these cells could be placed any number of UIComponents.

Problem: At any time I could tell what cell I was mousing over e.g.  (0,1), but I also needed to get an array of the components in that cell. It was not optimal iterating accross all the UIComponents on the canvas to check their constraint row/column id’s to find the relevant ones.

Solution: Created a 3D indexed array with references to each cell’s UIComponents. To retrieve the components from cell (3, 5) I would simply use: resultsArray = _cellComponents[rowNum][columnNum];

The image below represents a basic 3D array, with each cell containing just random objects: ints, strings and an image object. The sample app code that follows shows you how to create and populate a 3D array, and has a simple interface so you can test retrieving the stored objects.

3D array visual

Simple UI for 3D Array


<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" creationComplete="init()">
	<mx:Script>
		<![CDATA[
			import mx.controls.Image;
			import mx.controls.Button;
			import mx.events.IndexChangedEvent;
			
			private var _array3D:Array = new Array();
			private var _numRows:int = 2;
			private var _numColumns:int = 4;
			
			private function init():void{
							
				for(var i:int = 0; i < _numRows; i++){
					
					var rowArray:Array = new Array();
					_array3D.push(rowArray);
					
					// Create a 3d array (basically a 2d array of array objects)
					for(var j:int = 0; j < _numColumns; j++){
						
						var columnArray:Array = new Array();						
						_array3D[i].push(columnArray);									
					} 
				}
				
				// Populate a 3d array
				_array3D[0][0].push(1, 2, 3, 4);
				_array3D[0][3].push("A", "B", "C", "D");
				_array3D[1][0].push(5, 6, 7);
				_array3D[1][1].push(8);
				_array3D[1][2].push(9, 10);				
				// You can add any type of Object to the array.
				var image:Image = new Image();
				image.name = "arrayImage";			
				_array3D[1][3].push(image);
															
			}
			
			private function getValues(row:String, col:String):void{
				
				var r:int = parseInt(row);
				var c:int = parseInt(col);
				
				var cell:Array = _array3D[r][c];
				var output:String = "";
				
				if(cell != null){
					
					for(var i:int = 0; i <cell.length; i++){
						output = output + " " + cell[i].toString();
					}
					
					if(output == ""){
						outputText.text = "No data in cell"
					}
					else{
						outputText.text = output;
					}					
				}
				else{
					outputText.text = "Not a valid row/column";			
				}					
			}			
			
		]]>
	</mx:Script>

	<mx:Canvas id="myCanvas" x="65" y="49" width="308" height="99" backgroundColor="#FFFFFF">
		<mx:ApplicationControlBar x="0" y="0" width="100%">
			<mx:Label text="Row"/>
			<mx:TextInput id="row" width="56"/>
			<mx:Label text="Col."/>
			<mx:TextInput id="col" width="56"/>
			<mx:Button label="Get Values" click="getValues(row.text, col.text)"/>
		</mx:ApplicationControlBar>
		<mx:Text id="outputText" x="0" y="52" width="100%" textAlign="center"/>
	</mx:Canvas>
</mx:Application>

Flex: Stacked column chart – programmatically in actionscript

January 27, 2010

I was looking for ages for an example of a dynamically generated stacked column chart in Flex, but most of the examples were incorporating some MXML, or contained hard-coded series objects.  

The example below is based on Adobe Livedocs, but I show how to generate the series arrays programatically in as3, with no use of mxml code, and apply these series to a column chart (added to panelActionscript as seen in the code below). Also, this could be easily adapted to a barchart.

If youve got any suggestions, let me know.

Flex Stacked Chart

 

<?xml version="1.0" encoding="utf-8"?>
<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml"
creationComplete="initApp()" width="622" height="660">
  <mx:Script><![CDATA[

  	 import mx.charts.CategoryAxis;
  	 import mx.charts.series.ColumnSeries;
  	 import mx.charts.ColumnChart;
  	 import mx.charts.series.ColumnSet;
     import mx.charts.Legend;
     import mx.charts.BarChart;
     import mx.charts.series.BarSet;
     import mx.charts.series.BarSeries;
     import mx.collections.ArrayCollection;

     [Bindable]
     private var yearlyData:ArrayCollection = new ArrayCollection([
        {month:"January", revenue:120, costs:45,
            overhead:102, oneTime:23},
        {month:"February", revenue:108, costs:42,
            overhead:87, oneTime:47},
        {month:"March", revenue:150, costs:82,
            overhead:32, oneTime:21},
        {month:"April", revenue:170, costs:44,
            overhead:68},
        {month:"May", revenue:250, costs:57,
            overhead:77, oneTime:17},
        {month:"June", revenue:200, costs:33,
            overhead:51, oneTime:30},
        {month:"July", revenue:145, costs:80,
            overhead:62, oneTime:18},
        {month:"August", revenue:166, costs:87,
            overhead:48},
        {month:"September", revenue:103, costs:56,
            overhead:42},
        {month:"October", revenue:140, costs:91,
            overhead:45, oneTime:60},
        {month:"November", revenue:100, costs:42,
            overhead:33, oneTime:67},
        {month:"December", revenue:182, costs:56,
            overhead:25, oneTime:48},
        {month:"May", revenue:120, costs:57,
            overhead:30}
     ]);

     private function initApp():void {

     	// Create a column chart object
        var stackedColumnChart:ColumnChart = new ColumnChart();
        stackedColumnChart.dataProvider = yearlyData;
        stackedColumnChart.showDataTips = true;
 stackedColumnChart.width	= 460;

 // Set the horizontal axix category
        var xAxis:CategoryAxis = new CategoryAxis();
        xAxis.categoryField = "month";
        stackedColumnChart.horizontalAxis = xAxis;

 // ColumnSet.series is an array which contains
 // an array of ColumnSeries objects.
        var columnSet:ColumnSet = new ColumnSet();
        columnSet.type = "stacked";

        // Each item in seriesDetails becomes a different segment of
        // a column in the stacked chart i.e. each item represents a series.
        var seriesDetails:ArrayCollection = new ArrayCollection([
        	{YField:"costs", DisplayName:"Recurring Costs"},
        	{YField:"overhead", DisplayName:"Overhead Costs"},
        	{YField:"oneTime", DisplayName:"One-Time Costs"}
        	]);

        // Create a ColumnSeries, and an array to be
        // populated with dynamically generated columnSeries objects
        var columnSeries:ColumnSeries;
    	var seriesArray:Array = new Array();

    	// Generate an array of ColumnSeries objects
        // which are then be applied to the ColumnSet series.
        for(var i:int = 0; i < seriesDetails.length; i++){
        	columnSeries = new ColumnSeries();
        	columnSeries.yField = seriesDetails[i].YField;
        	columnSeries.displayName = seriesDetails[i].DisplayName;
        	seriesArray.push(columnSeries);
        }
        columnSet.series = seriesArray;

 // c.series could take an array of column sets
 // for displaying mixed charts i.e. c.series = [columnSet1, columnSet2];
 stackedColumnChart.series = [columnSet];

        var legend:Legend = new Legend();
        legend.direction = "horizontal";
        legend.dataProvider = stackedColumnChart;

        panelActionscript.addChild(stackedColumnChart);
        panelActionscript.addChild(legend);

     }

  ]]></mx:Script>
  <mx:VBox height="100%">
    <mx:Panel title="Stacked chart (dynamically created in ActionScript)"
    				id="panelActionscript" width="490">
    </mx:Panel>
  </mx:VBox>
  <mx:Panel title="Same chart created solely in MXML" id="panelMxml" width="490">
    <mx:ColumnChart id="mxmlColumnChart" dataProvider="{yearlyData}"
    				showDataTips="true" width="470">
      <mx:horizontalAxis>
           <mx:CategoryAxis categoryField="month"/>
       </mx:horizontalAxis>
       <mx:series>
           <mx:ColumnSet type="stacked">
               <mx:ColumnSeries
                   yField="costs"
                   displayName="Recurring Costs"/>
               <mx:ColumnSeries
                   yField="overhead"
                   displayName="Overhead Costs"/>
               <mx:ColumnSeries
                   yField="oneTime"
                   displayName="One-Time Costs"/>
           </mx:ColumnSet>
       </mx:series>
    </mx:ColumnChart>
    <mx:Legend dataProvider="{mxmlColumnChart}" direction="horizontal"/>
  </mx:Panel>
</mx:WindowedApplication>