Home | Account | Search  
Basic Flex TagCloud Component
On a recent Flex project, one of the requirements was to create tag cloud functionality. In a few quick searches, nothing related to building such a component turned up, so here's how I was able to whip one up.

tagcloud.png

Let's start with a simple XML snippet, a listing of books and their details:

<?xml version="1.0"?>
<catalog>
   <book id="bk101">
      <author>Gambardella, Matthew</author>
      <title>XML Developer's Guide</title>
      <genre>Computer</genre>
      <price>44.95</price>
      <publish_date>2000-10-01</publish_date>
      <description>An in-depth look at creating applications
      with XML.</description>
   </book>

    ... more book entries ...

</catalog>


We are going to display the contents of the author field. Based on the number of occurences in the XML file, the font size will be increased. The author mentioned the greatest number of times will be the largest and the ones mentioned once will be the smallest.

To start, let's look at the function buildTagCloud(xml:XML), which is called by the ResultEvent when the XML file is retrieved.

private function buildTagCloud(xml:XML):void
{
    var x:XMLList = xml.children();
    var a:ArrayCollection = new ArrayCollection();
    var s:String = "";
    var last:String = "";
    var n:int = 1;
    var max:int = 1;
   
    for(var i:Number = 0; i < x.length(); i++)
    {
        if(x[i].toString().indexOf(TagFieldName) > -1)
        {
            var node:XML = x[i];
            //trace(TagFieldName + "(" + i + ") = " + node.child(TagFieldName).toString());
            a.addItem(node.child(TagFieldName).toString());
        }
    }
   
    // sort alphabetically
    var sort:Sort = new Sort();
    a.sort = sort;
    a.refresh();
   
    max = getMaxOccur(a);
   
    // Figure out how many times each word appears
    for(i = 0; i < a.length; i++)
    {
        if(i > 0) // no comparison to do on the first one
        {
            s = a[i].toString();
           
            if(last == s)
            {
                n++;
            }
            else
            {
                // whatever term was last appears n times then reset n
                tagContainer.addChild(createTagLink(last, getFontHeight(n, max)));
                n = 1;
            }
        }
       
        last = a[i].toString();   
    }
}

The unique elements are added to an ArrayCollection, then sorted alphabetically. Next, we need to find the maximum number of times a single tag appears in the set of data. To accomplish that, a helper function called getMaxOccur(arr:ArrayCollection) is created.

private function getMaxOccur(arr:ArrayCollection):Number
{
    var max:int = 1;
    var n:int = 1;
    var last:String = "";
   
    for(var i:Number = 0; i < arr.length; i++)
    {
        if(i > 0)
        {
            if(last != arr[i].toString())
            {
                if(n > max) max = n;
                n = 1;
            }
            else
            {
                n++;
            }                   
        }   
       
        last = arr[i].toString();                   
    }
   
    return max;
}


Further into the buildTagCloud function, a second helper function is created to make a tag link, appropriately named createTagLink(label:String, size:Number).

private function createTagLink(label:String, size:Number):LinkButton
{
    tag = new LinkButton();
    tag.label = label;
    tag.setStyle("fontWeight","normal");
    tag.setStyle("fontFamily","Verdana");
    tag.setStyle("paddingRight",0);
    tag.setStyle("paddingLeft",0);
    tag.setStyle("fontSize", size);
    tag.setStyle("color", FontColor);
    tag.setStyle("textRollOverColor", HoverColor)
    tag.alpha = 0;
    tag.addEventListener(MouseEvent.CLICK, tagClickHandler);
    return tag;   
}


In addition, we need to determine the height of the font that will appear in the tag link. This can be derived from the number of occurrences for that particular tag along with the number of times a tag was found.

public function getFontHeight(occurences:int, maxoccur:int):int
{
    var interval:int = Math.round((MaxFontSize - MinFontSize) / 3);
    var ht:int = MinFontSize;
   
    if(occurences == maxoccur)
    {
        // largest size
        ht = MaxFontSize;
    }
    else if(occurences >= (maxoccur / 2))
    {
        ht = MaxFontSize - interval;
    }
    else if(occurences > 1)
    {
        ht = MinFontSize + interval;
    }
    else
    {
        // smallest/default size
        ht = MinFontSize;
    }
   
    return ht;
}


I know it probably isn't very clear what's going on here. The function has basically broken down the setup into three specific heights (small, medium, and large). I'm sure this could be expanded to support a finer range of heights pretty easily.

Finally, a couple classes from the awesome FlebLib library are leveraged to display the tag links. Namely -
flexlib.containers.FlowBox.as and flexlib.containers.utilityClasses.FlowLayout.as

While not a comprehensive component, feel free to download the source of this very basic TagCloud created in Flex.