在开发中,一般使用的树控件都是放在左边的,然后当展开树时,是向右方展开的。以前在做Eclipse的SWT/JFace应用时,见过向左展开的树,就是一个属性的设置,但是其中好像有些bug,记得是在向右展开的树的节点的文本有问题,应当是节点标签文本如果是以括号开始或者结尾时 ,加入设置的文本是“(ABC)”,那么显示在树节点上的文本顺序将会变成“)ABC(”,总之是把前后括号的顺序改变了,但是一般的字符是没有问题的。

最近在做Flex开发,也需要一个向左展开的树,这样我就可以在一个面板上先后放上一个向右展开的树和一个向左展开的树,这样我就可以很方便的在这两棵树之间进行连线,进行数据的映射了,下面就把Flex中向左展开树的实现贴出来,其实很简单,只要自己实现一个TreeItemRender就可以了,我的实现方式不是继承自flex中已有的TreeItemRender,而是把flex中自有的TreeItemRender整个的copy来,然后修改一些方法的定义(至于为什么不用继承的方式,是因为flex中自有的TreeItemRender有些私有变量我无法取得到)。下面就是我copy Flex中自有的TreeItemRender,然后只修改了updateDisplayList这一个方法,这个方法修改的原理就是把关于x轴坐标的设置修改,即 原来的x轴坐标的计算是从x=0开始,现在改成x=width开始。两个方法我都贴出来对比下

Flex自带的TreeItemRenderer的updateDisplayList方法:

override protected function updateDisplayList(unscaledWidth:Number,
                                                                                                    unscaledHeight:Number):void
        {
                super.updateDisplayList(unscaledWidth, unscaledHeight);

                var startx:Number = _data ? _listData.indent : 0;
                
                if (disclosureIcon)
                {
                        disclosureIcon.x = startx;

                        startx = disclosureIcon.x + disclosureIcon.width;
                        
                        disclosureIcon.setActualSize(disclosureIcon.width,
                                                                                 disclosureIcon.height);
                        
                        disclosureIcon.visible = _data ?
                                                                         _listData.hasChildren :
                                                                         false;
                }
                
                if (icon)
                {
                        icon.x = startx;
                        startx = icon.x + icon.measuredWidth;
                        icon.setActualSize(icon.measuredWidth, icon.measuredHeight);
                }
                
                label.x = startx;
                label.setActualSize(unscaledWidth - startx, measuredHeight);

                var verticalAlign:String = getStyle("verticalAlign");
                if (verticalAlign == "top")
                {
                        label.y = 0;
                        if (icon)
                                icon.y = 0;
                        if (disclosureIcon)
                                disclosureIcon.y = 0;
                }
                else if (verticalAlign == "bottom")
                {
                        label.y = unscaledHeight - label.height + 2; // 2 for gutter
                        if (icon)
                                icon.y = unscaledHeight - icon.height;
                        if (disclosureIcon)
                                disclosureIcon.y = unscaledHeight - disclosureIcon.height;
                }
                else
                {
                        label.y = (unscaledHeight - label.height) / 2;
                        if (icon)
                                icon.y = (unscaledHeight - icon.height) / 2;
                        if (disclosureIcon)
                                disclosureIcon.y = (unscaledHeight - disclosureIcon.height) / 2;
                }

                var labelColor:Number;

                if (data && parent)
                {
                        if (!enabled)
                                labelColor = getStyle("disabledColor");

                        else if (listOwner.isItemHighlighted(listData.uid))
                                labelColor = getStyle("textRollOverColor");

                        else if (listOwner.isItemSelected(listData.uid))
                                labelColor = getStyle("textSelectedColor");

                        else
                                labelColor = getStyle("color");

                             label.setColor(labelColor);
                }
                
                if (_data != null)
                {                        
                        if (listOwner.showDataTips)
                        {
                                if (label.textWidth > label.width ||
                                        listOwner.dataTipFunction != null)
                                {
                                        toolTip = listOwner.itemToDataTip(_data);
                                }
                                else
                                {
                                        toolTip = null;
                                }
                        }
                        else
                        {
                                toolTip = null;
                        }
                }
        }



下面是我的TreeItemRendererEx中的updateDisplayList方法的内容:

override protected function updateDisplayList(unscaledWidth:Number,
                             unscaledHeight:Number):void
    {
      super.updateDisplayList(unscaledWidth, unscaledHeight);
      
      var startx:Number = _data ?this.width- _listData.indent : this.width;
      
      if (disclosureIcon)
      {
        startx =    startx-disclosureIcon.width;
        disclosureIcon.x = startx;
        
        disclosureIcon.setActualSize(disclosureIcon.width,
          disclosureIcon.height);
        
        disclosureIcon.visible = _data ?
          _listData.hasChildren :
          false;
      }
      
      if (icon)
      {
        startx = startx - icon.width;
        icon.x = startx;
        icon.setActualSize(icon.measuredWidth, icon.measuredHeight);
      }
      startx = startx-label.textWidth -5;//图标和label文本之间设置一定的间隔
      label.x = startx;
      label.setActualSize(startx, measuredHeight);
      
      var verticalAlign:String = getStyle("verticalAlign");
      if (verticalAlign == "top")
      {
        label.y = 0;
        if (icon)
          icon.y = 0;
        if (disclosureIcon)
          disclosureIcon.y = 0;
      }
      else if (verticalAlign == "bottom")
      {
        label.y = unscaledHeight - label.height + 2; // 2 for gutter
        if (icon)
          icon.y = unscaledHeight - icon.height;
        if (disclosureIcon)
          disclosureIcon.y = unscaledHeight - disclosureIcon.height;
      }
      else
      {
        label.y = (unscaledHeight - label.height) / 2;
        if (icon)
          icon.y = (unscaledHeight - icon.height) / 2;
        if (disclosureIcon)
          disclosureIcon.y = (unscaledHeight - disclosureIcon.height) / 2;
      }
      
      var labelColor:Number;
      
      if (data && parent)
      {
        if (!enabled)
          labelColor = getStyle("disabledColor");
          
        else if (listOwner.isItemHighlighted(listData.uid))
          labelColor = getStyle("textRollOverColor");
          
        else if (listOwner.isItemSelected(listData.uid))
          labelColor = getStyle("textSelectedColor");
          
        else
          labelColor = getStyle("color");
        
        label.setColor(labelColor);
      }
      
      if (_data != null)
      {                        
        if (listOwner.showDataTips)
        {
          if (label.textWidth > label.width ||
            listOwner.dataTipFunction != null)
          {
            toolTip = listOwner.itemToDataTip(_data);
          }
          else
          {
            toolTip = null;
          }
        }
        else
        {
          toolTip = null;
        }
      }
    }
    


OK,就这么简单,下面是一个测试应用的效果,其中的IFactory使用的是我在我的上一篇博客中实现的Factory方式

<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
            xmlns:s="library://ns.adobe.com/flex/spark"
            xmlns:mx="library://ns.adobe.com/flex/mx" minWidth="955" minHeight="600">
  <fx:Declarations>
    <!-- 将非可视元素(例如服务、值对象)放在此处 -->
    <fx:XML id="dp" xmlns="">
      <root>
        <folder label="One">
          <folder label="One.A">
            <item label="One.A.1" />
            <item label="One.A.2" />
            <item label="One.A.3" />
            <item label="One.A.4" />
            <item label="One.A.5" />
          </folder>
          <item label="One.1" />
          <item label="One.2" />
        </folder>
        <folder label="Two">
          <item label="Two.1" />
          <folder label="Two.A">
            <item label="Two.A.1" />
            <item label="Two.A.2" />
          </folder>
        </folder>
      </root>
    </fx:XML>
  </fx:Declarations>
  <fx:Script>
    <![CDATA[
      import mx.collections.ICollectionView;
      import mx.events.ListEvent;
      
      import test.test.MyFactory;
      import test.test.TreeItemRendererEx;
      
      
      private function tree_labelFunc(item:XML):String {
        return item[treeL.labelField]
      }
      
      private function myTreeItemRenderer():TreeItemRendererEx{
        return new TreeItemRendererEx();
      }
    ]]>
  </fx:Script>
  <mx:Canvas width="100%" height="100%" id="container">
    <mx:Tree id="treeL" dataProvider="{dp}" showRoot="false" labelField="@label" labelFunction="tree_labelFunc" width="300" height="100%" rowCount="6" x="{container.x+10}" y="{container.y+10}"/>
    <mx:Tree id="treeR" dataProvider="{dp}" showRoot="false" labelField="@label" labelFunction="tree_labelFunc" width="300" height="100%" rowCount="6" x="{container.width - treeR.width-10}" y="{container.y+10}" itemRenderer="{new MyFactory(myTreeItemRenderer)}">
    </mx:Tree>
  </mx:Canvas>
</s:Application>

下面是运行效果的截图:

左右树





















本人新博客地址