Slick Flex Tree control

Actionscript, Flex Add comments

Following on from the recent post about automatically closing the open branches of the Tree control when you open a new branch; this is a slicker implementation that does the same thing but allows you to keep the animation. Currently the Flex control falls over if you try and animate multiple branches opening or closing.

Get Adobe Flash player

The main thing to point out here is that this example also works if you click the Tree disclosure icon (the little triangle) as well as the item. Most of the examples out there in the blogosphere that show you how to click on items to open branches forget that the disclosure button will open/close the branch and ignore the itemClick handler you’ve setup.

I listen for the itemOpening and itemClick events and pass on to a method called handleSelectedItem. This method then checks the depth of the selected item like so.

  1. var d:int = TreeListData(TreeItemRenderer(tree.itemToItemRenderer(selectedItem)).listData).depth;

This looks crazy but you need to grab the itemRenderer’s listData property to find the depth of the object. It’s quite convoluted but you can grab the renderer with the Tree’s itemToItemRenderer method, cast it to the default TreeItemRenderer class and cast the listData property to the default TreeListData class to get the depth property off that … phew!

We need the depth because I only want to work with the root level branches which is level 1 in the case of a Tree. I then cycle through the Tree’s dataprovider which gives me the items in my XMLListCollection. These will correspond to the root level items as they are XML nodes with all of the children nodes (branches) in them. I can then look for siblings to the selected item using the getRoot method and close any open siblings.

  1. if(tree.isItemOpen(i) && i!=selectedItem && idepth==1 && isSibling)
  2.                   {
  3.                    tree.addEventListener(TreeEvent.ITEM_CLOSE,itemClosed);
  4.                    tree.expandItem(i,false,true,true); // CLOSE SIBLING
  5.                    openInstantly = false;
  6.                   }

I create an eventListener to listen for when the open root level branch is closed and because there will ever only be one branch closing I can use animation. The openInstantly Boolean prevents the selectedItem from opening … yet. When the closed item has finished I run a short Timer delay to make sure the animation listeners are all cleared and then open the selected item.

Here’s the Application class:

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <mx:Application
  3.  
  4.  xmlns:mx="http://www.adobe.com/2006/mxml"
  5.  layout="absolute"
  6.  width="200"
  7.  height="300"
  8.  >
  9.  
  10.  <mx:Script>
  11.  
  12.   <![CDATA[
  13.  
  14.   import mx.controls.treeClasses.TreeItemRenderer;
  15.   import org.osflash.thunderbolt.Logger;
  16.   import com.criticalmass.gucci.homepage.renderers.CustomTreeItemRenderer;
  17.   import mx.controls.listClasses.IListItemRenderer;
  18.   import mx.controls.treeClasses.TreeListData;
  19.  
  20.   import mx.collections.XMLListCollection;
  21.   import mx.events.TreeEvent;
  22.   import mx.controls.Tree;
  23.   import mx.events.ListEvent;
  24.  
  25.   private var dpx:XML = <nav>
  26.    
  27.    <node label="Womens">
  28.     <node label="Flora by Gucci" url="assets/swf/movies/flora.swf" />
  29.     <node label="Gucci by Gucci" url="assets/swf/movies/gucci.swf" />
  30.     <node label="Classics" url="assets/swf/movies/classics.swf" >
  31.      <node label="Classic 1" url="http://gucci.com/class/classic1.html" />
  32.      <node label="Classic 2" url="http://gucci.com/class/classic1.html" />
  33.      <node label="Classic 3" url="http://gucci.com/class/classic1.html" />
  34.     </node>
  35.    </node>
  36.    
  37.    <node label="Mens">
  38.     <node label="Pour Homme" url="assets/swf/movies/ph.swf" >
  39.      <node label="Pour Homme 1" url="http://gucci.com/ph/classic1.html" />
  40.      <node label="Pour Homme 2" url="http://gucci.com/ph/classic2.html" />
  41.     </node>
  42.    </node>
  43.    
  44.    <node label="Boys">
  45.     <node label="Pour Homme" url="assets/swf/movies/ph.swf" >
  46.      <node label="Pour Homme 1" url="http://gucci.com/ph/classic1.html" />
  47.      <node label="Pour Homme 2" url="http://gucci.com/ph/classic2.html" />
  48.     </node>
  49.    </node>
  50.    
  51.   </nav>;
  52.  
  53.   private var dp:XMLListCollection = new XMLListCollection(dpx.children());
  54.  
  55.   private var selectedItem:Object;
  56.  
  57.   private var timeDelay:Timer;
  58.  
  59.   //-----------------------------------------------------------------------------------
  60.   private function treeItemOpening(e:TreeEvent):void
  61.   {
  62.    if(!tree.isItemOpen(e.item))
  63.    {
  64.     selectedItem = e.item;
  65.     handleSelectedItem();
  66.    }
  67.   }
  68.   //-----------------------------------------------------------------------------------
  69.   private function treeItemClick(e:ListEvent):void
  70.   {
  71.                selectedItem = Tree(e.currentTarget).selectedItem;
  72.    handleSelectedItem();
  73.            }
  74.            
  75.            //-----------------------------------------------------------------------------------
  76.   private function handleSelectedItem():void
  77.   {
  78.    
  79.    var openItems:Array = tree.openItems as Array;
  80.             var openInstantly:Boolean = true;
  81.    
  82.                var d:int = TreeListData(TreeItemRenderer(tree.itemToItemRenderer(selectedItem)).listData).depth;
  83.                
  84.                if(tree.dataDescriptor.isBranch(selectedItem))
  85.                {
  86.                 for each (var i:Object in tree.dataProvider)
  87.                 {
  88.                  var idepth:int = TreeListData(TreeItemRenderer(tree.itemToItemRenderer(i)).listData).depth;
  89.      var isSibling:Boolean = XML(getRoot(i)).@label != XML(getRoot(selectedItem)).@label;        
  90.                  if(tree.isItemOpen(i) && i!=selectedItem && idepth==1 && isSibling)
  91.                  {
  92.                   tree.addEventListener(TreeEvent.ITEM_CLOSE,itemClosed);
  93.                   tree.expandItem(i,false,true,true); // CLOSE SIBLING
  94.                   openInstantly = false;
  95.                  }
  96.                 }
  97.                
  98.                 if(openInstantly) tree.expandItem(selectedItem,!tree.isItemOpen(selectedItem),true,false);
  99.                }
  100.                else
  101.                {
  102.                 // Clicked on an item -- DO STUFF!
  103.                }
  104.   }
  105.  
  106.   //-----------------------------------------------------------------------------------
  107.            /*
  108.             Function for accessing the Root item of the Tree
  109.            */
  110.            private function getRoot(childObj:Object):Object
  111.            {
  112.             var parentObj:Object = tree.getParentItem(childObj);
  113.             if(parentObj != null) return getRoot(parentObj);
  114.             else return childObj;
  115.            }
  116.  
  117.   //-----------------------------------------------------------------------------------
  118.   /*
  119.    Item closed handler
  120.   */
  121.   private function itemClosed(e:TreeEvent):void
  122.   {
  123.    tree.removeEventListener(TreeEvent.ITEM_CLOSE,itemClosed);
  124.    
  125.    timeDelay = new Timer(200,1);
  126.    timeDelay.addEventListener(TimerEvent.TIMER_COMPLETE,openSelectedItem);
  127.    timeDelay.start();
  128.   }
  129.   //-----------------------------------------------------------------------------------
  130.   private function openSelectedItem(e:TimerEvent):void
  131.   {
  132.    timeDelay.removeEventListener(TimerEvent.TIMER_COMPLETE,openSelectedItem);
  133.    if(selectedItem) tree.expandItem(selectedItem,true,true,false);
  134.   }
  135.  ]]>
  136.  </mx:Script>
  137.  
  138.  <mx:Tree
  139.  
  140.   id="tree"
  141.   width="200"
  142.   height="300"
  143.   dataProvider="{dp}"
  144.   labelField="@label"
  145.   itemClick="treeItemClick(event)"
  146.   itemOpening="treeItemOpening(event)"
  147.   />
  148.  
  149. </mx:Application>
Digg!

6 Responses to “Slick Flex Tree control”

  1. Arnold Kaswa Says:

    What a good example. You have just made my day.

  2. Abhay Bhandari Says:

    Brillant example, god bless you….. Amazing …. amazing …. thanks .. for posting it.

  3. KarthiK Says:

    Hi, this is a great example, I appreciate your work. I am getting some problems with this code when there is vertical scroll bar coming. If we give more data in dataprovider, lets say we give 20 nodes xml and vertical scroll bar is appearing with Tree control. The problem is when I clicked an element which will be visible once you scroll and it gives “cannot access property with null reference”. Then I started debug and found that the problem is in this line “var idepth:int = TreeListData(TreeItemRenderer(this.itemToItemRenderer(i)).listData).depth;” It gives depth to the items which are visible, it doesn’t give depth and gives errors for the tree items which are not visible. Can you guide me to fix this? I really wonder if you can give me a solution.

    Thanks
    KARTHIK

  4. Jon Says:

    Hello, Great example, but I’m having the same problem as Karthik. If there is a solution, I’d be interesed.
    Thanks, Jon

  5. Lee Probert Says:

    Hi guys. So the items outside the viewport are not registered as having a depth? Is that the issue? I will need to dig the code out and take a look at this. Seems bizarre though ay?

  6. Jon Says:

    Found one potential solution:
    I removed the idepth variable altogether, and replaced the if statement with the following:
    if (tree.isItemOpen(i) && i != selectedItem && isSibling)

    The above fixes the issue with minimizing the root nodes, however, the animation is not perfect when clicking on a node while the vertical scroll bar exists.

Leave a Reply

WP Theme & Icons by N.Design Studio
Entries RSS Comments RSS Log in