Learning Flex 4 – States

States are easier to work with in Flex 4 than they were with 3. While Flex 3 had the advantage that everything to do with your states was in one place, it made things more complicated by the additional syntax you had to deal with to add/remove items etc. With Flex 4, you can just add your state name to a property to specify a value specific to that state and specify if components should be included in/excluded from a state. The only downside to this is that your state specific code gets scattered but Flash Builder has an option to show you components by state so that helps there.

Starting from the beginning, you first need to declare what states you are going to have. This is done using the <s:states> tag. Within this, specify one or more <s:State> tags with the name property set to the name for your state. The first State is used as the default. In addition to the name, you may also specify a stateGroups property. This allows you to group states so you can refer to the group rather than all the individual states. This could be useful in the case where for most of the time, you want to do the same thing for several states but for specific components/properties you need a finer distinction.

Here’s an example (I like to use all caps for my state names to help them stand out a little more):

<s:States>
<s:State name="LOADING"/>
<s:State name="EMPLOYEE_VIEW" stateGroups="VIEW" />
<s:State name="MANAGER_VIEW" stateGroups="VIEW"/>
<s:State name="MANAGER_SELECT" />
</s:States>

The grouping is a bit artificial in this case but this app starts in the LOADING state while it’s gathering data from the back end. Then, if you’re a manager, the state becomes MANAGER_SELECT where you can select an employee to view data on, finally you get a MANAGER_VIEW of the data or the EMPLOYEE_VIEW if you logged in as an employee. You can see how there might be several components common to the MANAGER_VIEW and the EMPLOYEE_VIEW which you could use the VIEW stateGroup for but some components (maybe a change pay rate button for instance) would be specific to the MANAGER_VIEW.

You can include components in a state by using the property includeIn or exclude them using excludeFrom.  You can specify more than one state by using commas to separate them in the string (or specify a stateGroup). By adding the state to a group container, you can affect all the components within that group.

You can change properties (including event handlers) by suffixing the property name with the state name you want it to apply to eg – label.LOADING="retrieving data..."

If you don’t specify a state, the component/property is considered to be the same for all states. You can change the state by setting the currentState property (by setting it to the empty string, you return to the default or first state).

Custom components can declare their own states and you can use the currentState property of the instance of the custom component to change the state for that particular object.

When the state changes, the enterState event is fired by the State object being entered and by components entering that state. The exitState event is sent by the State and components being left. The object the currentState is being changed on fires currentStateChanging when it’s about to change and currentStateChange when it’s done.

You can use states within a custom item renderer to react to view states such as hovered, selected and change the presentation for these cases. This article has the details. Saturn Boy has also written a blog post looking at states with skinning.

Learning Flex – Lesson 26, The Basics of Memory Management and Performance

There are two basic ways to handle memory allocation. The first (used by C,C++) is to make the developer deal with it – you are responsible for allocating memory required and freeing it when you’re done. This allows the developer the freedom to decide when time consuming memory operations are done but the responsibility to keep track of everything or run into memory leaks (when unused memory isn’t released) or null pointer issues and the like when it’s released too soon.

The alternative chosen by Flash (and Java) is to use the runtime for garbage collection – i.e. let it decide how to allocate and free memory. The downside to this is that garbage collection can be time consuming and it’s nondeterministic – the runtime decides when it’s going to happen, not the developer.

There are several different algorithms for  garbage collection. The Flash player uses a version comprised of “reference counting” and “mark-and-sweep”. (For a discussion of various garbage collection strategies see this article).

For reference counting, the runtime keeps track of all the active references to an object. Each object keeps a reference internally to its children and each child has a reference to its parent. There are also references created explicitly by the developer as a way to access the object in question. If the reference count is zero, nothing can access that object any more so it can be removed and it’s memory reclaimed.

The second phase, mark-and-sweep is more costly and required to discover circular references – i.e. objects that reference each other but have no outside references. To perform the mark, the player starts at the root of the application and traverses through its references marking each as it goes. When it’s reached the leaf nodes of all those references, any objects not marked can be “swept” away and deallocated. Due to its cost, this operation is performed iteratively over several frames and is only run occasionally.

What does all this mean to a Flash/Flex developer? Well you should be sure to remove all references to an object when you no longer need it. Often this will be done for you with variables going out of scope etc but you should be proactive in removing longer lived references you no longer require.

Event Listener Leaks

Objects that want to be informed of an event can register as a listener using the addEventListener() method. This results in the creation of a reference to the object doing the listening. Therefore if an object is listening for events, it may not be available for garbage collection.

There is a method called removeEventListener() that can be called to remove the reference when it’s no longer required which would allow the listener object to be removed when it has no other references. Unfortunately, it’s not always easy to know when to call this.

An alternative when calling addEventListener() optional parameters may be added, the last of which is a Boolean indicating a weak reference (the default is false). What this means is that if set true, the reference created for the listener is considered weak and not counted as a reference for garbage collection. A listener with only weak references remaining would be eligible for garbage collection. Here’s a post with more on weak references and here’s an opinion on when they shouldn’t be used.

Flash/Flex Performance tuning

FlexBuilder (and now FlashBuilder) professional comes with a profiler tool that allows you to investigate the memory usage of your application and time operations to look for areas in need of tuning.

There are no free alternatives that I’m aware of but you can use flash.utils.getTimer() to time operations and System.totalMemory to keep track of memory use in your application. Grant Skinner also has a performance test harness you might find useful.

A great resource with lots of links to more detailed information on memory management and performance is this page on Jun Heider’s blog.

Learning Flex – Lesson 25, Debugging, Logging and Error Handling

Debugging

Perhaps the simplest form of debugging is adding a trace() statement to your code to print out when a function is entered or the value of a variable at a particular point.

In addition to the global trace() method, the <mx:TraceTarget/> tag allows you to see communication with a remote server. This can be particularly useful in determining what (if anything) your server is returning for your requests.

TraceTarget actually sets up the Flex logging API. The appearance of the server communication is down to the fact that the internal Flex components mx.rpc.*, mx.messaging.* and mx.data.* all use this API. We’ll come back to logging later.

A more interactive strategy for finding errors is to use a debugger. If you’re fortunate enough to have access to Flex Builder, you have a built-in graphical debugger that will allow you to set breakpoints, inspect variables etc.  If not, the Flex SDK comes with a command line debugger – fdb. This can be found in the bin directory and started by typing fdb and then run location of swf or just fdb location of swf directly. You can use help or tutorial at the fdb prompt to get information on the commands available. Basically you can set breakpoints (even conditional ones which the Flex Builder version doesn’t support), step through code and print or change the values of variables. It’s not quite as easy to use as the Flex Builder version but it will get the job done. There are also some 3rd party free tools available such as Demonster Debugger which can give you some of the features of a graphical debugger along with memory usage and frame rates but unfortunately, don’t support breakpoints.

Here’s Adobe’s info on the command line debugger and here’s a great chapter from the book Programming Flex 3 that OReilly have kindly put on their website that covers the command line debugger, the Flex Builder debugger and the logging framework.

Logging

The aforementioned OReilly chapter covers this topic really well. You can define different levels of logging (debug, warn etc) and filter on which you want to be logged. You can also determine what happens to your log data so for instance, you could arrange to log particularly worrying errors back to your server or perhaps to a local file that support staff could request users email them if problems occur.

Error Handling

Like most modern languages, ActionScript3 has the concept of exceptions and the associated try, catch, throw and finally commands.

Runtime exceptions occur during the operation of the application, often due to unexpected input or problems communicating with remote systems. In these situations, you can place the potentially troublesome code within a try-catch block like so:

try{
some dangerous code
}catch(e:Error){
do something about the error
}

Within the catch block, you can access properties and methods of the Error object: message; name; getStackTrace() – for debugger versions of the Flash player, returns the call stack for the error as a String and toString() which returns “Error” by default or the value contained in message if defined.

There are a number of error classes defined both by ECMAScript and ActionScript, more details on which can be found here.

You may define multiple catch blocks to deal with specific error classes. The rules are that the first catch block to satisfy the error will be executed and only one catch block may be run. For this reason, you should never use the base class Error before other catch blocks as it will always take the error.

The throw statement allows you to raise an exception which will bypass the normal operation of your application. You would normally do this if you have discovered a condition that prevents the normal flow of execution. You may either throw an existing error type or create your own by extending the Error class.

The finally statement should be placed after all catch blocks and is used to define code that should be executed regardless of whether an exception was raised or not (generally this is used for something like freeing resources requested in the try block).

Flash player 10.1 introduces the concept of a global error handler so you can trap errors that have otherwise not been processed. This allows you to have a catch-all point where you can at least log something and potentially inform the user before gracefully exiting.

According to the release notes (pdf), the current beta 2 of 10.1 has this functionality disabled but here’s a blog post from Christian Cantrell on how to do it using Air and here’s some more detail about the UncaughtErrorEvent.

Learning Flex – Lesson 24, Using Shared Objects

It’s often useful to be able to store data on the client side such as preferences or a saved cart similar to the way websites do using cookies.

Flex has the SharedObject class to achieve this which improves on cookies by being able to store complex data structures.

Properties assigned to a SharedObject are written to a file as soon as the swf is removed from Flash Player either by changing sites or closing down the browser. It’s also possible to manually force the write in the application.

Shared objects are stored on the end users machine in a location determined by the operating system (you can find out the details here ). They have an .sol extension and by default can each be up to 100kb in size (there’s no limit on their number). The user can use the flash player preferences to change this (right click in Flash window, select settings).

Shared objects cannot contain methods and are not deleted by clearing cookies. Like cookies, shared objects cannot be read from different domains. Note that when testing with Flex Builder, shared objects are limited to the same application as the testing environment opens a local file and does not establish a domain.

The static getLocal() method of the SharedObject class retrieves an existing SharedObject or creates a new one. If you need to write the object to file, you can use the static flush() method. Here’s an example of creating a SharedObject:

var mySO:SharedObject = SharedObject.getLocal("shoppingCart");

This creates a file called shoppingCart.sol on the users machine and the SharedObject mySO to refer to it.

To populate the object with data, you assign your values as properties of the data property of the object. For example, to store an email of myEmail@mail.com you would use:

mySO.data.email="myEmail@mail.com";

To store complex data objects, you must first create them so to store an array of products you would use:

mySO.data.products=new Array();
mySO.data.products=productsToSave;

When using the flush() method, you may specify a value for the size of the file in bytes. This can be used to create a file larger than initially required. The reason for this is that for the initial write of the file and any time it needs to grow in size, the user will be prompted for approval. by requesting a file size as large as you expect to get, you prevent extra requests for approval every time the shared object is written to.

Flash Player raises an exception when a flush() fails. It returns SharedObjectFlushStatus.FLUSHED
when it’s successful and SharedObjectFlushStatus.PENDING when the size of the file must increase and the user is being prompted for approval, after which a  netStatus event is dispatched with an information object detailing the success or failure of the request.

When working with a shared object, it’s best to check that the value you are about to use is present before attempting to use it. You can achieve this  as follows:

if (mySO.data.email != undefined){
//use the email address
}

To empty and remove the shared object, use it’s clear() method.

When using SharedObjects in a swf, make sure your display object is at least 215 pixels wide and at least 138 pixels high or the dialog prompt to increase storage cannot be displayed and the flush() call will fail.

Learning Flex – Lesson 23, Printing From Flex

Flex allows for printing from an application by using the FlexPrintJob class which is a wrapper for the flash.printing.PrintJob class. This allows you to specify one or more objects to be printed. The objects can be containers or custom components and you can specify a separate format that is not visible to the user.

The basic process is as follows:

  1. Instantiate a FlexPrintJob object
  2. call it’s start() method to begin the print process – this will cause the OS to display a print dialog and returns  true if they accept or false if they cancel out.
  3. Add the object(s) to be printed using the addObject() method
  4. Send the job to the printer using the send() method
  5. clean up any temporary objects no longer needed.

Between the start() and send() calls, a print job is spooled to the host operating system so only print specific work should be performed at this time.

It’s good practice to check the return of the start() method and abort the print attempt if it returns false.

If you try to hide the print version of a component by setting it’s visible property false, it will still occupy space in the layout. An alternative is to define the print layout in a custom component then define and instance and add it in your print process using the addchild() method of the application. You can then remove it using removeChild() as part of the print job cleanup. Your print code would then look something like this:

var printJob:FlexPrintJob = new FlexPrintJob();
if(printJob.start() != true){
  return;
}
var myPrintcontainer:CustomPrint = new CustomPrint();
addChild(myPrintContainer);
printJob.addObject(myPrintContainer);
printJob.send();
removeChild(myPrintContainer);

The output can be scaled by adding a parameter to the addObject() call. The options are static constants from the FlexPrintJobScaleType class:

  • MATCH_WIDTH – scales the printed output to match the page width. If the height exceeds the width, the output will span multiple pages (default)
  • MATCH_HEIGHT – scales the printed output to fill the page height. If the width exceeds the height, the output will span multiple pages.
  • SHOW_ALL – scales the printed output to fit on a single page, filling the smaller of width or height.
  • FILL_PAGE – scales the printed output to fill at least one page. It selects the larger of width or height.
  • NONE – the printed output matches the dimensions of the object on the screen.

To aid with printing data grids, there are print versions of data grid classes namely PrintDataGrid, PrintAdvancedDataGrid and PrintOLAPDataGrid. These have default appearances designed for printing and methods to support multiple page printing.

A PrintDataGrid will only print the rows that are visible by default. The property sizeToPage specifies that partially visible rows are not printed if it’s set true (default). To print rows below a scrollbar, you must use the nextPage() method and validNextPage boolean property.

While validNextPage is true, call nextPage() and then add the PrintDataGrid object to the print job.

Flex printing can look somewhat blurry on the page and does not always produce the results desired. For a great series of posts on an alternative, (AlivePDF) take a look at these posts from Kalen Gibbons.

Learning Flex – Lesson 22, Creating Transitions and Behaviors

Behaviors and transitions allow you to add animation and sound effects to your application. Behaviors are effects that can be applied directly on components, transitions are these effects applied to application states.

Some of the effects available are: fade, dissolve, move/resize, rotate, wipe, glow and play sound.

These effects can be used to draw the user’s attention to items changing on the screen or give cues as to which elements they can interact with.

Behaviors

When applied to components, behaviors are made up of a trigger and an effect.

The trigger is the action taking place on the component such as a button click or gaining focus. It is important to note that triggers are different to events. A component would have a mouseDownEffect trigger as well as a mouseDown event. When you use a trigger, you do not specify an event handler, instead you specify the effect to occur.

You can directly specify an effect class name for a trigger such as:

<mx:VBox showEffect="Fade"/>

but in this case, you are limited to one effect and cannot customize it.

You may define an effect tag and specify desired properties that can then be provided to a trigger using data binding on the effect id such as:

<mx:Fade id="myFade" duration="2000"/>
<mx:VBox showEffect="{myFade}"/>

You can apply multiple effects to the same trigger by using the <mx:Sequence> or <mx:Parallel> tags by nesting effects within them to occur in order (using <mx:Sequence>) or all together (using <mx:Parallel>). You can even nest these tags for more involved effects.

As effect triggers are implemented as CSS styles, you can specify them using an <mx:Style> tag:

<mx:Style>
List {showEffect : myFade;}
</mx:Style>

which will make all lists have the same show effect (unless you override it in a specific list configuration, or:

<mx:Style>
.fadeStyle { showEffect : myFade;}
</mx:Style>

which will display this effect for all components with the styleName property set to fadeStyle.

It’s also possible to play effects at other times by calling the effect’s play() method (you can optionally specify an array of components to perform the effect on and a boolean value which when set to true, will play the effect backwards. There’s also an end() method to end the effect (this is often called immediately before play() to stop any outstanding effects).

Effects can be used for changes in data provided by a dataProvider in a List or TileList. More information about this can be found here.

More control over the animation of an effect may be provided by using an easing function. All effect classes that are children of the TweenEffect or MaskEffect classes support the easingFunction property. Some components have easing function style properties that can be used directly.  More information on easing functions can be found here.

You can play a sound effect by using the <mx:SoundEffect> tag and specifying the url of an MP3 file in the source property. Alternatively, if you’ve already embedded  the sound file, you may use data binding to refer to it’s class object here.  There are also several properties to determine the duration, start point in the file, volume start and end values etc (you can find out the full details here) .

Transitions

Transitions are effects for application states. They must be declared as children of an <mx:transitions> tag (this is a property of the application). Each <mx:Transition> has a fromState – the view state you’re moving from, toState – the view state you’re moving to and the effect object you want to play when the transition occurs. You can specify a target or targets (array of target objects) for the effect either in the effect tag directly or at a higher level within an <mx:Sequence> or <mx:Parallel> tag. An example would be:

<mx:transitions>
<mx:Transition fromState="START" toState="*">
<mx:Dissolve duration="2000"  target="{myContainer}"/>
</mx:Transition>
</mx:transitions>

Once the transitions are declared, they are triggered by changing the application’s currentState property.

Learning Flex – Lesson 21, Deploying Flex Applications

Adobe Integrated Runtime (AIR) allows you to write Flex applications that can be run from the desktop. AIR gives you access to features that are not available when running from the browser such as interaction with the local file system, an embedded database for client side storage and with the new AIR 2 beta , raw microphone data access and multitouch support amongst other things.

The basics of working with AIR are very similar to Flex applications. In Flex Builder or FlashDevelop, choose a new AIR project and instead of a root <mx:Application> tag, use <mx:WindowedApplication>.

In defining your application, the application id is important as this is used to register the app with the operating system so it needs to be unique. This value (along with other configuration data) is stored in the project’s application.xml file. Flex Builder allows you to specify the application id on project creation but FlashDevelop will default it to the project name so you’ll probably want to go in and change that. Further information on the application.xml file can be found here.

Because of the additional access they have, AIR apps need to be signed by a security certificate before they can be released.

You can create a self-signed cert or buy one from a certificate authority such as VeriSign (currently $499 per year) or Thawte (currently $299 per year). Note these are different to standard SSL certificates.

The downside to a self-signed cert is that the install screen will show a big red “?” along with the text “are you sure you want to install this application to your computer?” instead of the happy green “!” that you get with a valid paid certificate. You can find out more about how to get and install a certificate in this article.

FlashDevelop provides two batch files and an AIR_readme.txt file when it creates an AIR project to help you release your application. You can find out more information on packaging an AIR file (including potential error messages) in this article.

Learning Flex – Lesson 20, Creating Modular Applications

As your application gets larger, it could result in a pretty bulky swf that will take a long time to download and potentially cause users to get code for things they never use. There are also issues around re-using your code in different applications – if you just copy the source, it’s going to be a pain to maintain when you have bug fixes or enhancements for it.

Fortunately, there are ways to break up your code in Flex to reduce some of these issues.

Modules

Flex modules allow you to create a subset of code that can be loaded/removed at run time.  Say you’ve got an application that has features that only certain users have access to. You could provide the core of the app as your swf and then download the “special” functionality after a user of the correct type has logged in and asked to access that area. This saves everyone having to download code they may never  be able to access.

First of all you need to define your Module. The <mx:Module> tag defines the root of a module and you may have more than one of them in an application. It has a layout property that can be set to absolute, horizontal or vertical. More than likely, you’ll convert a current UI container class into a module so you can replace it’s tags with the Module ones.

FlexBuilder has functionality to aid with modules but if you’re using FlashDevelop, you’re going to have to do things a little differently. This post explains how to do that. Basically, you can specify compiler properties in an annotated comment (remember to do this within the <mx:Script> block if you’re Module is defined in MXML). The only addition I would make is to use the -source-path option to define the root of your source code so the compiler can find everything it needs to compile the module if you’ve got multiple folders within your source code.

The ModuleLoader class is a type of container that knows how to load and unload modules. As it’s a container, it can be added directly to the UI. It has a url attribute which specifies where to find the module swf at run time and can be given width and height attributes along with an id. The ready event is fired when the module is loaded and the object data of the module is available through the child property. The unloadModule() method can be used to remove the module.

A sample moduleLoader tag might look like this:

<mx:moduleLoader id="myModule" url="modules/myModule.swf" width="100%" height="100%" ready="handleModuleReady(event)"/>

In which case to set a property within my module I would cast the child to the module class to access the property like so:  MyModule(myModule.child).property=value;

You can also specify an error event handler to deal with problems trying to load your module. If you see an error something like this:

Error #1053: Illegal override of FlexModuleFactory in mx.core.FlexModuleFactory

Make sure you’re using the same  version of the Flex sdk for your module and your application (particularly if you’re using the FlashDevelop method covered above and you’ve got a project specific compiler specified – remember compiling the module with CTRL-F8 uses the default compiler).

Runtime Shared Libraries (RSL)

If you have common code that gets used between several of your applications, you can separate this out into a library and then either compile that in to each application or use it as an RSL. These can be cached on the client so it does not need to be downloaded every time the application is used.

It’s important to consider the size implications of using libraries – if you create ones that try to fit in too much, the overall size of your application at run time will go up as it will be including code from the shared library that it never uses.

Adobe has developed  special RSLs of the framework, data visualization and  data services classes so that they can be cached by the Flash player and any swf can use them, thus removing the need to compile in extra code and reduce the size of all apps. Thse special RSLs are digitally signed and have a .swz extension to ensure that only Adobe certified code is used.  They can be found in your SDK’s frameworks\rsls directory. Only Flash Player 9.0.115 and later can use the signed swz versions but there are swf versions available too.

To specify you wish to use  RSLs, you use the compiler -runtime-shared-library-path option. This is followed by the library file (swc) to compile against, the runtime location of the primary RSL (the swz), a policy file if needed (if this is your own unsigned RSL and you’re using it for a different domain) and the failover RSL (swf) runtime location for older Flash Players.

Here’s an example from my project:

-runtime-shared-library-path=C:\flex3sdk\frameworks\libs\framework.swc,framework_3.4.0.9271.swz,,framework_3.4.0.9271.swf

(I’m putting the framework RSLs in the same place as my application swf).

Creating a Flex Library

FlexBuilder has built in support for creating a Flex library (swc) file but for FlashDevelop you’ll need to use the command line compc compiler (there is a plugin for FlashDevelop but I couldn’t get it to work for me).

The best way to do this if you have several items is via a n xml configuration file.

You can then go to the bin directory under your SDK and run the command:

compc -load-config+=config-file.xml the += means in addition to the default config which makes sure you get access to relevant libraries.  Here’s an example configuration file:

<?xml version="1.0"?>
<flex-config xmlns="http://www.adobe.com/2006/flex-config">
  <output>C:\tools\flex\FlexGrocerLibrary\.\bin\FlexGrocerLibrary.swc</output>
  <use-network>true</use-network>
  <target-player>10</target-player>
  <warnings>true</warnings>
  <benchmark>false</benchmark>
  <compiler>
    <accessible>false</accessible>
    <allow-source-path-overlap>false</allow-source-path-overlap>
    <optimize>true</optimize>
    <strict>true</strict>
    <es>false</es>
    <show-actionscript-warnings>true</show-actionscript-warnings>
    <show-binding-warnings>true</show-binding-warnings>
    <show-unused-type-selector-warnings>true</show-unused-type-selector-warnings>
    <use-resource-bundle-metadata>true</use-resource-bundle-metadata>
    <verbose-stacktraces>false</verbose-stacktraces>
    <source-path>
      <path-element>c:\tools\flex\flexgrocerlibrary\src</path-element>
    </source-path>
    <external-library-path append="true">
      <path-element>C:\tools\flex_sdk_3.4\frameworks\libs\framework.swc</path-element>
      <path-element>C:\tools\flex_sdk_3.4\frameworks\libs\datavisualization.swc</path-element>
    </external-library-path>
  </compiler>
  <include-classes>
    <class>events.CategoryEvent</class>
    <class>events.ObjectDataEvent</class>
    <class>utils.Util</class>
    <class>valueObjects.Category</class>
    <class>views.MaxRestorePanel</class>
    <class>views.dashboard.ChartPod</class>
    <class>views.dashboard.ComparisonChart</class>
    <class>views.dashboard.SalesChart</class>
    <class>views.dashboard.TypeChart</class>
  </include-classes>
 <include-file>
    <name>downArrow.gif</name>
    <path>C:\tools\flex\FlexGrocerLibrary\src\assets\downArrow.gif</path>
</include-file>
<include-file>
    <name>upArrow.gif</name>
    <path>C:\tools\flex\FlexGrocerLibrary\src\assets\upArrow.gif</path>
</include-file>
 </flex-config>

Note the external library path entries for the framework libraries I’m referencing – the append attribute is to make sure these get added to any other libraries required. Make sure you specify these as external libraries rather than just libraries or they’ll get compiled in rather than just referenced.

Note also you can specify classes and asset files for inclusion.

Once this is run, you’ll have a swc file you can include as a library to your apps. To make it an RSL takes a little more work.  First of all, you need to extract the swf file from the swc using winzip or something similar, then you need to optimize it to remove debug information etc (you don’t need to do this if you’re building in your swc file as the compiler takes care of stripping that out when you generate a release build). You can find out how to optimize your RSL swf here.

Learning Flex – Lesson 19, part II More Chart Customization

Chart Legends

The <mx:legend> tag uses it’s dataProvider property to determine what to display. This should be the id of the chart you want the legend for. It has style attributes to determine labelPlacement and direction of layout. It uses the displayName property of a series to determine what to place in the label.

Axis Renderers

You can customize the layout of an axis by using an <mx:AxisRenderer>; tag. This has properties such as canDropLabels, canStagger, title etc. The axis property is used to define which axis the renderer is to be used for. AxisRenderer tags are children of either an enclosing <mx:horizontalAxisRenderers>; or <mx:verticalAxisRenderers> tag.

Vertical axis titles default to rendering top to bottom. To get the more normal bottom to top, you can use an AxisRenderer with the property verticalAxisTitleAlignment set to vertical (thanks to McQuillen Interactive for that gem).

Data Tips

All charts support a boolean property showDataTips that when set to true, will show a tool tip type box when the user mouses over an element. By default, this shows the x and y value but you can specify a function for the property dataTipFunction to customize what’s displayed. This function should accept an object of type mx.charts.HitData and return String. The HitData object has a property item typed to Object which contains the dataProvider item the user has moused over on the chart.

Click Events

If a user clicks in a chart, a chartClick event will be dispatched if no data point is found under the mouse or itemClick if there is an item within the radius specified by mouseSensitivity (default 3 pixels) for the chart. The chartClick event provides the chart the user clicked on, the itemClick event provides a property hitData of type HitData that contains the chart dataProvider item the user clicked on the chart.

Data Selection

You can enable data point selection by setting the property selectionMode on the chart to none, single or multiple. You can select multiple points either by dragging a box around a number of points with the mouse or clicking them while holding down the ctrl/command key.

The selectability of individual chart series can be turned on or off by using the boolean selectable property on the series in question.

The change property on a chart can be used to define a call back function that will be called when the selection in a chart changes. The event fired contains a hitSet property which is an Array of HitData objects corresponding to the chart points selected.

It’s also possible to programatically  select chart items by setting the selectedIndex property for a single point or selectedIndices Array property of a series for multiple points.

Chart Animations

Three built in animations are provided for charts. They are subclasses of the mx.charts.effects.SeriesEffects class and are specified for a series showDataEffect or hideDataEffect attributes.

  • seriesInterpolate – this effect moves the graphics that represent the existing data in a series to the new points. It creates a smooth animation between the two sets of data. This effect is only valid for showDataEffect.
  • seriesSlide – this effect slides the chart data into and out of the chart boundaries. If you use seriesSlide with the hideDataEffect trigger, the series slides from the current position to off screen in the specified direction. For showDataEffect, the effect is reversed, from the specified direction into the chart.
  • seriesZoom – this effect implodes and explodes the chart data into and out of the focal point specified.

Customizing Chart Appearance

You can set styles using CSS for the chart as a whole, individual series in a chart or the axes. Many style elements may also be set inline as attributes of the relevant MXML tag. For instance, you may use the <mx:stroke> tag to specify the style of a chart’s item renderer and <mx:lineStroke> to define a line chart’s lines or <mx:areaStroke> for an area chart.

Here’s a more customized version of the line chart I had in the previous post:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">
  <mx:Script>
    <![CDATA[
      import mx.collections.ArrayCollection;
      import mx.charts.LinearAxis;

      [Bindable]
      public var notorious:ArrayCollection = new ArrayCollection(
      [ { money: 1000, problems: 1 },
      {money: 5000, problems: 2 },
      {money: 20000, problems: 4 },
      {money:100000, problems: 6 },
      {money:150000, problems:10 } ]);

      private function renderMoney(value:Number, previousValue:Number,axis:LinearAxis):String{
        return dollars.format(value);
      }
    ]]>
  </mx:Script>
  <mx:CurrencyFormatter currencySymbol="$" id="dollars" useThousandsSeparator="true"/>
    <mx:Style>
      LineChart {
         fontFamily:TimesRoman;
         fontSize:14;
     fontWeight:bold;
      }
    </mx:Style>

  <mx:Panel title="Notorious Line chart">
    <mx:LineChart id="myChart"
           dataProvider="{notorious}"
           showDataTips="true">
      <mx:series>
        <mx:LineSeries yField="money" xField="problems">
          <mx:lineStroke>
            <mx:Stroke color="0x33FF33" weight="7" />
          </mx:lineStroke>
        </mx:LineSeries>
      </mx:series>
    <mx:verticalAxis>
            <mx:LinearAxis id="vert" title="Money" labelFunction="renderMoney" />
        </mx:verticalAxis>
    <mx:horizontalAxis>
            <mx:LinearAxis title="Problems"/>
        </mx:horizontalAxis>
    <mx:verticalAxisRenderers>
      <mx:AxisRenderer axis="{vert}" verticalAxisTitleAlignment="vertical"/>
    </mx:verticalAxisRenderers>
        <mx:backgroundElements>
           <mx:Image
                source="@Embed('01-large-cash-money.gif')"
                alpha=".4"
           />
        </mx:backgroundElements>
    </mx:LineChart>
  </mx:Panel>
</mx:Application>

here’s a picture of the “improved” chart:

customized "Notorious" graph

(cash money image courtesy of   http://mindmillion.com/MONEY/)

For other chart components, you should take a look at Kap IT Lab or ILOG Elixir from IBM for charts that are free for non commercial use or the open source project Axiis if you want to construct your own data visualizations.

Learning Flex – Lesson 19, Visualizing Data

The Flex charting components are part of the Data Visualization component library, a separate  download found on the Flex SDK download page. Prior to Flex 3.4, components created with this library would show a trial watermark if used without a Flex Builder professional key. Version 3.4 no longer has this watermark but it’s my understanding that Adobe still expects you to have a professional license to use these components “for real”.

Flex supports the following chart types: area, bar, bubble, candlestick, column, high low open close (for stocks), line, pie and plot.

All these chart types have a similar construction – each chart type defines what kind of chart to display, has attributes for the data to render (dataProvider), whether tool tips are shown etc. Child tags then define axes, one or more series and styling elements.

Each chart must have a series defined before data will be rendered and each type of chart has it’s own series class that can be used (eg <mx:PieChart> has <mx:PieSeries>). Some charts allow you to mix different series types within the same chart. The series will define which field of the dataProvider is to be used for the chart.

The Axis class is required for any of the Cartesian charts to specify what data should be rendered for the horizontal and vertical axes. Subclasses are used to specify numeric (LinearAxis) or String based (CategoryAxis) data.

An AxisRenderer optionally defines how the axis will be shown (including labels, tick marks and title). CSS, text properties, label rotation and spacing (to allow labels to be dropped for readability) are items that can be specified by an AxisRenderer.

Other optional elements are backgroundElements for such things as background images and annotationElements for items like grid lines (both are subclasses of ChartElement).

Pie Charts

A PieSeries has a labelPosition style attribute which is used to define how labels are rendered for the chart. The available values are:

  • none – default, no labels
  • outside – draw labels around the boundary of the pie
  • callout – draw labels in two vertical columns on either side of the pie. The pie will shrink to accommodate them.
  • inside – labels are drawn approximately 70% along each wedge, labels will be shrunk to avoid interference. Labels shrunk below the calloutPointSize property are removed and larger wedge labels have priority if they overlap.
  • insideWithCallout – labels are drawn inside the pie but if they shrink below a legible size, they are converted to callouts.

The label will default to the data being rendered in the pie but this can be changed by using the property labelField to define an element in the dataProvider to use or labelFunction to specify a rendering function. This function should have the signature -

(data:Object, field:String, index:Number, percentValue:Number):String

The data is the dataProvider item being rendered, field is the name of the field in that item that’s being rendered, index is the index into the dataProvider for this wedge and percentValue is the percentage of the whole this wedge represents.

You may also specify a nameField property which defines the field of the dataProvider to use as the name of the wedge in tool tip objects and legends.

You can generate a doughnut or wheel chart (a hollow center) by specifying the innerRadius property on the PieChart. This is a number between 0 and 1 as a fraction of the entire pie’s radius.

Pie charts can be “exploded” so wedges are drawn partially outside the pie. To explode all wedges uniformly, set the explodeRadius property on the PieSeries to a value between 0 and 1 representing the fraction of pie radius to use. To explode wedges by different amounts, define an array of values between 0 and 1 and pass this to the PieSeries perWedgeExplodeRadius property.

Here’s the code for a simple pie chart:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">
  <mx:Script>
    <![CDATA[
      import mx.collections.ArrayCollection;
      [Bindable]
      public var snowSports:ArrayCollection = new ArrayCollection(
      [ { sport: "Ski", percent: 48 },
      {sport: "Snowboard", percent: 46 },
      {sport: "Snowshoe", percent: 4 },
      {sport: "Other", percent: 2 }]);
    ]]>
  </mx:Script>
  <mx:Panel title="Pie Chart">
    <mx:PieChart id="myChart"
           dataProvider="{snowSports}"
           showDataTips="true">
      <mx:series>
        <mx:PieSeries field="percent"
                labelField="sport"
                labelPosition="callout"/>
      </mx:series>
    </mx:PieChart>
  </mx:Panel>

</mx:Application>

here’s a picture of the chart created:

Pie Chart Example

Line Charts

For a LineChart or other Cartesian graph, you specify the yField property of your series type to define which field of the dataProvider is to be used for for the y axis plot and an xField property to determine the x axis location. If you omit the xField, Flex arranges the data points in the order of the data in the dataProvider.

The axis labeling is defined using <mx:horizontalAxis> and <mx:verticalAxis> tags. Flex supports 4 types of axes:

  • CategoryAxis – map data of discrete values such as cities, teams etc. A CategoryAxis can optimize it’s rendering if it’s data is more static
  • LinearAxis – map numeric data to the axis. This can be used to provide valid numeric ranges, numbers to skip etc
  • LogAxis – map logrithmic data to an axis (labels for powers of 10)
  • DateTimeAxis – map time based values to an axis and format the labels. You may also disable particular dates from being shown (only show workdays or weekends for instance).

It is possible to define multiple axes for a chart. To do this, you create axes for each series being displayed.

Here’s the code for a simple line chart:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">
  <mx:Script>
    <![CDATA[
      import mx.collections.ArrayCollection;
      [Bindable]
      public var notorious:ArrayCollection = new ArrayCollection(
      [ { money: 1000, problems: 1 },
      {money: 5000, problems: 2 },
      {money: 20000, problems: 4 },
      {money:100000, problems: 6 },
      {money:150000, problems:10}]);
    ]]>
  </mx:Script>
  <mx:Panel title="Notorious Line chart">
    <mx:LineChart id="myChart"
           dataProvider="{notorious}"
           showDataTips="true">
      <mx:series>
        <mx:LineSeries yField="money"
                xField="problems"/>
      </mx:series>
    </mx:LineChart>
  </mx:Panel>
</mx:Application>

here’s a picture of the chart created:

Notorious BIG Chart

I’ll continue with charts in part 2 shortly.

Follow

Get every new post delivered to your Inbox.