Data Visualization with Flex, Part III

In our first article, we went over the basics of data visualization, created a data set, and generated a display showing occurrences of keywords in the SitePoint forums. In the second part, we replaced the output with a spring graph—the better to illustrate relationships between keywords—and also added some font scaling to show the relative popularity of each keyword. In our final installment, we’ll add a few more pieces of information to the display and refactor our application in accordance with best practices. The instructions will build upon the finished code from the second tutorial, but you might want to download the Flex Builder project archive containing all the finished code.

Again this week we have an Article Quiz sponsored by Adobe to test what you learn in the tutorial. Be sure to check it out when you’re done here!

Adding Basic Tooltips

At the end of the second article we were left with a spring graph that looked like this:

Figure 1. Initial graph

Initial graph


This allows us to see direct relationships between keywords, and also see their relative popularity. It does leave a few things to be desired though. To start with, we’ve no way of seeing the actual occurrence counts of each keyword, and since we’re scaling the font size in fixed increments, there can be a considerable difference in counts between keywords that display at the same size.

Adding more text to the graph will probably make it a little messy, and the exact occurrence count is more of a secondary piece of data that people will drill down to if they’re interested in gleaning more information. With this in mind, a good way to proceed is to add a tooltip—this way the user can mouse over any keyword they’re interested in and see the info they want, without it clogging up the graph unnecessarily.

The easy way to do this is to add the count to the toolTip attribute on our item renderer. Let’s try doing this now:

Example 1.  (excerpt)

<sg:SpringGraph   id="mySpringGraph"  backgroundColor="#FFFFFF"   lineColor="#6666ff"   repulsionFactor="5"  right="10" left="10" bottom="10" top="10">  <sg:itemRenderer>    <mx:Component>      <mx:VBox         cornerRadius="5"         backgroundColor="0x444444"         paddingBottom="5"         paddingLeft="5"         paddingRight="5"         paddingTop="5"         toolTip="{data.data}">        <mx:Label           text="{data.id}"           fontSize="10"           color="0xFFFFFF"           textAlign="center" />      </mx:VBox>    </mx:Component>  </sg:itemRenderer></sg:SpringGraph>


If you look at the toolTip attribute that we added to the VBox in our inline item renderer component, we’re passing it the data key in our object. This might appear confusing: just remember that what’s being passed to the item renderer is always given the reference data in Flex, and it just so happens that our Item class has a property called data, which we’re using to store the keyword occurrence count. If our property in the Item class was called occurrence_count, the code would look like this:

toolTip="{data.occurrence_count}"

Save the above code—and when you mouse over a keyword in the resulting spring graph, you should see a tooltip like this:

Figure 2. Simple tooltips

Simple tooltips


This works, and proves that we can push our information through to the tooltip, but is neither attractive nor particularly helpful. Ideally we want prettier interface, and one big enough to contain some extra text like “Mentioned in 12345 Threads.”

Best Practice, Code Architecture, and Refactoring

Before we begin though, a note on best practice. In part II, we simply created our item renderer inline—it was easier and allowed us to explore the concept without a lot of code architecture getting in the way. However, in the real world, it’s much better to create an independent class for the item renderer. Doing so provides us with encapsulation and allows us to reuse the same tooltip in multiple places, should we wish to. In the context of this spring graph application, it may make little sense to you to separate these elements, especially if you lack experience working with larger projects. In the long run, however, I guarantee you’ll find life much easier if you follow these principles. Remember, when it’s a tiny demo application it seems like a lot of work, but once your codebase reaches several hundred thousand lines of code, it will be much simpler to debug, refactor, or modify any part of your application if you’ve followed this sort of best practice.

The whys and wherefores of architectural best practice are beyond the scope of this article, so for now I’ll just lay out a simple way to structure the code. If you’re new to this, I highly recommend you do some reading on Flex architecture and design patterns, and perhaps look into some of the frameworks like Mate and Swiz.

In order to separate out our item renderer, we need to create a new class. In your src directory you should have a subdirectory named com. Inside com, create a directory named sitepoint, then inside that create another directory named itemrenderers. This method of laying out folders is sometimes called reverse domain notation, and is just a handy way of separating code from different projects. As long as the paths are correct, Flex is unconcerned with where our code lives—we’re creating subdirectories purely to make it easier to manage as the codebase grows or as you combine code from different projects into one. In our case, you can see we already have a directory—adobe—inside our com directory; this is where the Adobe JSON serialization classes live. Already you can see how sticking to this domain notation automatically protects us from any naming clashes and keeps it all nice and clean.

Now inside the Flex Navigator pane, right-click the itemrenderers folder and choose New, then MXML Component. Call it KeywordRenderer.mxml and base it off VBox. You should end up with a new file whose contents look like this:

Example 2. src/com/sitepoint/itemrenderers/KeywordRenderer.mxml (excerpt)

<?xml version="1.0" encoding="utf-8"?><mx:VBox xmlns:mx="http://www.adobe.com/2006/mxml" width="400" height="300"></mx:VBox>


The code we need to complete the renderer is similar to the inline version we built in the last tutorial—the complete file should look like this:

Example 3. src/com/sitepoint/itemrenderers/KeywordRenderer.mxml

<?xml version="1.0" encoding="utf-8"?><mx:VBox   xmlns:mx="http://www.adobe.com/2006/mxml"  cornerRadius="5"  borderThickness="1"  borderStyle="solid"  backgroundColor="0x444444"   paddingBottom="5"   paddingLeft="5"   paddingRight="5"   paddingTop="5">  <mx:Label     text="{data.id}"     fontSize="{parentDocument.getFontSize(data.data)}"     color="0xFFFFFF"     textAlign="center" /></mx:VBox>


Now that this is complete, we go back to our main .mxml file and make a couple of changes. Remove the inline component—everything inside and including the <sg:itemRenderer> tags. It should end up looking like this:

Example 4. src/SitePoint_DataViz_Tutorial_Part3.mxml (excerpt)

<sg:SpringGraph   id="mySpringGraph"  backgroundColor="#FFFFFF"   lineColor="#6666ff"   repulsionFactor="5"  right="10" left="10" bottom="10" top="10" />


Note that I’ve removed the end tag and added a trailing slash to the start tag, because we no longer need to nest anything inside it. We’ll also need to move the font stepping code (which we wrote in Part II) of the series to inside the item renderer class. This will prevent us from having to use the parentDocument call to the font function. It’s always a good idea to keep your components from being dependent on the rest of your code, so they can be reused without modification.

As our font method relies on data calculated in the main application, we’ll need to pass it in as a series of parameters. We can just copy and paste the getFontSize method into our new item renderer, and then add declarations for the public variables like so:

Example 5. src/com/sitepoint/itemrenderers/KeywordRenderer.mxml (excerpt)

public var dataMin:Number;public var dataMax:Number;public var fontMin:Number;public var fontMax:Number;public var fontLift:Number;


To allow us to set these parameters, we’ll need to assign the itemRenderer in our main application file. Let’s add that to the setup method, just before the line where we assign the dataProvider-:

Example 6. src/SitePoint_DataViz_Tutorial_Part3.mxml (excerpt)

var factory:ClassFactory = new ClassFactory(KeywordRenderer)factory.properties = {fontMin:fontMin, fontMax:fontMax, fontLift:fontLift, dataMin:dataMin, dataMax:dataMax};mySpringGraph.itemRenderer = factory; mySpringGraph.dataProvider = myGraph;


Here we’re creating a ClassFactory and setting the parameters we need in an array (factory.properties), then assigning the factory as the item renderer for our spring graph. We need to do this in our script, since we’re unable to set parameters if we simply assign it as an attribute in our MXML tag.

Your final KeywordRenderer.mxml should look like this:

Example 7. src/com/sitepoint/itemrenderers/KeywordRenderer.mxml (excerpt)

<?xml version="1.0" encoding="utf-8"?><mx:VBox   xmlns:mx="http://www.adobe.com/2006/mxml"  cornerRadius="5"  borderThickness="1"  borderStyle="solid"  backgroundColor="0x444444"   paddingBottom="5"   paddingLeft="5"   paddingRight="5"   paddingTop="5"   buttonMode="true"  mouseChildren="false"  useHandCursor="true"  click="this.searchForums()"  toolTip=" "  toolTipCreate="createCustomToolTip('Mentioned in '+formatter.format(data.data)+' threads', event)">  <mx:Script>    <![CDATA[      import mx.events.ToolTipEvent;      import com.sitepoint.tooltips.KeywordToolTip;            private var sitePointSearchURL:String = "http://www.sitepoint.com/forums/search.php?q=";      public var dataMin:Number;      public var dataMax:Number;      public var fontMin:Number;      public var fontMax:Number;      public var fontLift:Number;          private function createCustomToolTip(title:String, event:ToolTipEvent) : void {        var ktt:KeywordToolTip = new KeywordToolTip();        ktt.message = title;        event.toolTip = ktt;      }          private function getFontSize(data:Number) : Number {        var fontSize:Number = (data - dataMin) / fontLift;        if (fontSize < fontMin) {          return fontMin;        } else if (fontSize > fontMax) {          return fontMax;        } else {          return fontSize;          }      }            private function searchForums() : void {        navigateToURL(new URLRequest(sitePointSearchURL+data.id), "_self");      }    ]]>  </mx:Script>  <mx:NumberFormatter id="formatter" />  <mx:Label     text="{data.id}"     fontSize="{getFontSize(data.data)}"     color="0xFFFFFF"     textAlign="center" /></mx:VBox>


If you compile and reload your application, nothing will have changed … perfect! That’s exactly what you want! When refactoring, the goal is to keep the behavior of the application the same, while providing the warm fuzzy feeling of knowing you’ve architected your code properly for the future.

note: Performance Considerations

When creating custom item renderers (especially for components like data grids) you need to take performance into account. For our simple purposes, the above item renderer is perfectly sufficient. If, however, you’re creating a renderer for a heavy data grid, for example, you’d be better off creating a pure ActionScript item renderer. This has no reliance on a container, as elements like Canvas and VBox can take a toll on performance when dozens or hundreds are created in rendering a grid.

Free book: Jump Start HTML5 Basics

Grab a free copy of one our latest ebooks! Packed with hints and tips on HTML5's most powerful new features.

No Reader comments

Comments on this post are closed.