每天逛逛TWaver论坛已经成为一种习惯,今天看到一个非常有意思的帖子: http://twaver.servasoft.com/forum/viewtopic.php?f=14&t=3129
当两个Group重叠时,Group中的Node会始终显示在两个Group之上,呈现结果如下图(引用了帖子中的图片):
这简直是无法忍受的,如果把这样的呈现效果拿给客户看,不被骂死才怪。我们要的是这种效果(引用了帖子中的图片):
帖子中给出的解决方案是:为每个Group生成一个layer,当选中Group中,将对应的layer置顶。想法很美好,现实却很残酷:帖子最后给出的代码虽然可以部分实现这种效果,但是在我看来仍然不够完美,甚至说有瑕疵。大家可以把代码copy下来,然后运行测试:先将所有的Group展开,然后点击group1,再点击group2_2并拖动与group1重合,你看到了什么?
group2_2虽然被置顶,但是它的parent:group2却仍然被group1和group1_1遮挡了。这是因为帖子中的解决方案只是处理了当前点击Group自身和的child Group(将child Group置顶),却没有处理parent Group,所以group2_2的parent:group2仍然显示在后面。下图是测试的结果(group1是group1_1的parent;group2是group2_2的parent;group1和group2是平级,同是group_root1的children):
可以很明显地看到,group2_2确实被置顶了,但是group2却仍然被遮挡,理想的状况下,当我们点击group2_2的时候,group2应该紧紧跟随在group2_2下面,并遮挡group1和group1_1,效果如下图:
这样就好看多了,当我们拖动group2_2的时候,group2应该仅仅跟随在group2_2下边,并且遮挡住group1和group1_1。
我把帖子中的代码改造了一下,最终实现了这种效果,独乐乐不如众乐乐,下面我把我的实现代码贴到此博客上与大家分享,如果有错误或可以优化的地方希望大家可以批评指正,感激不尽!
我们仍然沿用帖子中的设计思路:为每个Group生成一个layer,点击Group时将layer置顶,但是我们还要考虑Group的parent Group和child Group:将child Group置顶,parent Group紧随其后。首先Group可以嵌套多层,而且我们也不确定可能会嵌套几层,所以递归是不可避免的了。对于这种比较复杂的算法,直观的代码远远要比语言描述有力的多,直接贴上代码:
private function init():void { this.initBox(); network.elementBox = box; network.addInteractionListener(function(e:InteractionEvent):void { if(e.kind == InteractionEvent.CLICK_ELEMENT){ var g:Group = null; if(e.element is Group){ g = Group(e.element); }else if(e.element.parent is Group){ g = Group(e.element.parent); } var ele:Element=e.element as Element; var rootGroup:Group=g; var parentArr:ArrayCollection=new ArrayCollection; parentArr.addItem(g); while(ele.parent){ if(ele.parent is Group){ rootGroup=ele.parent as Group; parentArr.addItem(rootGroup); } ele=ele.parent as Element; } iterator(rootGroup,parentArr); } }); } private function iterator(parentGroup:Group,parentArr:ArrayCollection):void{ var nextParentGroup:Group=null; var layer:Layer = box.layerBox.getLayerByID(parentGroup.id) as Layer; if(layer){ box.layerBox.moveToBottom(layer); } for(var i=0;i<parentGroup.children.count;i++){ var ele:Element=parentGroup.getChildAt(i) as Element; if(ele is Group){ var g:Group=Group(ele); if(parentArr.contains(g)){ nextParentGroup=g; continue; } var layer:Layer = box.layerBox.getLayerByID(g.id) as Layer; if(layer){ box.layerBox.moveToBottom(layer); } iterator(Group(ele),parentArr); } } if(nextParentGroup) iterator(nextParentGroup,parentArr); }
尽管语言乏力,但是为了方便大家理解,我还是尽力描述一下:
当我点击一个Group的时候,获取这个Group的根Group,称之为rootGroup,将rootGroup传入iterator方法递归遍历,把所有的Group置顶一遍,但是越是靠后置顶的,就越靠前显示。所以为了让我们点击的Group能够靠前显示,我们需要先遍历无关Group(例子中的group1和group1_1),最后再遍历group2和group2_2(其中group2_2要最后遍历)。这个遍历先后顺序怎么控制呢,通过这段代码来控制:
if(parentArr.contains(g)){ nextParentGroup=g; continue; }
parentArr集合里包含了我们点击的Group的所有的parent Group,我们判断如果当前遍历到的group在parentArr集合中,就跳过循环,直到for结束后再遍历它。这是大致的思路,我觉得这些描述+代码应该可以让大家理解,不知道这样频繁的置顶对效率有没有影响,希望大家能提出优化方案。最后附上完整的代码 demo(见原文最下方)