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.
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.
-
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.
-
if(tree.isItemOpen(i) && i!=selectedItem && idepth==1 && isSibling)
-
{
-
tree.addEventListener(TreeEvent.ITEM_CLOSE,itemClosed);
-
tree.expandItem(i,false,true,true); // CLOSE SIBLING
-
openInstantly = false;
-
}
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:
-
<?xml version="1.0" encoding="utf-8"?>
-
<mx:Application
-
-
xmlns:mx="http://www.adobe.com/2006/mxml"
-
layout="absolute"
-
width="200"
-
height="300"
-
>
-
-
<mx:Script>
-
-
<![CDATA[
-
-
import mx.controls.treeClasses.TreeItemRenderer;
-
import org.osflash.thunderbolt.Logger;
-
import com.criticalmass.gucci.homepage.renderers.CustomTreeItemRenderer;
-
import mx.controls.listClasses.IListItemRenderer;
-
import mx.controls.treeClasses.TreeListData;
-
-
import mx.collections.XMLListCollection;
-
import mx.events.TreeEvent;
-
import mx.controls.Tree;
-
import mx.events.ListEvent;
-
-
private var dpx:XML = <nav>
-
-
<node label="Womens">
-
<node label="Flora by Gucci" url="assets/swf/movies/flora.swf" />
-
<node label="Gucci by Gucci" url="assets/swf/movies/gucci.swf" />
-
<node label="Classics" url="assets/swf/movies/classics.swf" >
-
<node label="Classic 1" url="http://gucci.com/class/classic1.html" />
-
<node label="Classic 2" url="http://gucci.com/class/classic1.html" />
-
<node label="Classic 3" url="http://gucci.com/class/classic1.html" />
-
</node>
-
</node>
-
-
<node label="Mens">
-
<node label="Pour Homme" url="assets/swf/movies/ph.swf" >
-
<node label="Pour Homme 1" url="http://gucci.com/ph/classic1.html" />
-
<node label="Pour Homme 2" url="http://gucci.com/ph/classic2.html" />
-
</node>
-
</node>
-
-
<node label="Boys">
-
<node label="Pour Homme" url="assets/swf/movies/ph.swf" >
-
<node label="Pour Homme 1" url="http://gucci.com/ph/classic1.html" />
-
<node label="Pour Homme 2" url="http://gucci.com/ph/classic2.html" />
-
</node>
-
</node>
-
-
</nav>;
-
-
private var dp:XMLListCollection = new XMLListCollection(dpx.children());
-
-
private var selectedItem:Object;
-
-
private var timeDelay:Timer;
-
-
//-----------------------------------------------------------------------------------
-
private function treeItemOpening(e:TreeEvent):void
-
{
-
if(!tree.isItemOpen(e.item))
-
{
-
selectedItem = e.item;
-
handleSelectedItem();
-
}
-
}
-
//-----------------------------------------------------------------------------------
-
private function treeItemClick(e:ListEvent):void
-
{
-
selectedItem = Tree(e.currentTarget).selectedItem;
-
handleSelectedItem();
-
}
-
-
//-----------------------------------------------------------------------------------
-
private function handleSelectedItem():void
-
{
-
-
var openItems:Array = tree.openItems as Array;
-
var openInstantly:Boolean = true;
-
-
var d:int = TreeListData(TreeItemRenderer(tree.itemToItemRenderer(selectedItem)).listData).depth;
-
-
if(tree.dataDescriptor.isBranch(selectedItem))
-
{
-
for each (var i:Object in tree.dataProvider)
-
{
-
var idepth:int = TreeListData(TreeItemRenderer(tree.itemToItemRenderer(i)).listData).depth;
-
var isSibling:Boolean = XML(getRoot(i)).@label != XML(getRoot(selectedItem)).@label;
-
if(tree.isItemOpen(i) && i!=selectedItem && idepth==1 && isSibling)
-
{
-
tree.addEventListener(TreeEvent.ITEM_CLOSE,itemClosed);
-
tree.expandItem(i,false,true,true); // CLOSE SIBLING
-
openInstantly = false;
-
}
-
}
-
-
if(openInstantly) tree.expandItem(selectedItem,!tree.isItemOpen(selectedItem),true,false);
-
}
-
else
-
{
-
// Clicked on an item -- DO STUFF!
-
}
-
}
-
-
//-----------------------------------------------------------------------------------
-
/*
-
Function for accessing the Root item of the Tree
-
*/
-
private function getRoot(childObj:Object):Object
-
{
-
var parentObj:Object = tree.getParentItem(childObj);
-
if(parentObj != null) return getRoot(parentObj);
-
else return childObj;
-
}
-
-
//-----------------------------------------------------------------------------------
-
/*
-
Item closed handler
-
*/
-
private function itemClosed(e:TreeEvent):void
-
{
-
tree.removeEventListener(TreeEvent.ITEM_CLOSE,itemClosed);
-
-
timeDelay = new Timer(200,1);
-
timeDelay.addEventListener(TimerEvent.TIMER_COMPLETE,openSelectedItem);
-
timeDelay.start();
-
}
-
//-----------------------------------------------------------------------------------
-
private function openSelectedItem(e:TimerEvent):void
-
{
-
timeDelay.removeEventListener(TimerEvent.TIMER_COMPLETE,openSelectedItem);
-
if(selectedItem) tree.expandItem(selectedItem,true,true,false);
-
}
-
]]>
-
</mx:Script>
-
-
<mx:Tree
-
-
id="tree"
-
width="200"
-
height="300"
-
dataProvider="{dp}"
-
labelField="@label"
-
itemClick="treeItemClick(event)"
-
itemOpening="treeItemOpening(event)"
-
/>
-
-
</mx:Application>


July 12th, 2009 at 4:41 pm
What a good example. You have just made my day.
August 31st, 2009 at 11:13 am
Brillant example, god bless you….. Amazing …. amazing …. thanks .. for posting it.
December 28th, 2009 at 11:43 am
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
January 12th, 2010 at 8:22 pm
Hello, Great example, but I’m having the same problem as Karthik. If there is a solution, I’d be interesed.
Thanks, Jon
January 12th, 2010 at 8:57 pm
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?
January 12th, 2010 at 9:00 pm
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.