看看这变态的自动布局

自动布局就是对数据元素中的节点进行定位,使节点分散在到一个比较好看的显示效果,自动布局的实质就是一种布局的算法的实现。比如TWaver默认提供圆形布局、交错布局、树形布局和弹簧布局,此外标签云、bus总线等也属于布局的应用,但需求总是变化无穷,数据结构不同,要求的呈现效果也不同,自动布局的定制在所难免,本文将介绍一种比较少见的布局效果。

 

默认的自动布局

首先我们来看看默认的自动布局的使用

var autoLayouter:AutoLayouter = new AutoLayouter(network);

autoLayouter.doLayout(Consts.LAYOUT_SYMMETRY);

代码分两步,首先定义一个自动布局器关联了network组件,然后调用自动布局,进行交错布局。
可以看出,TWaver中自动布局都是通过AutoLayouter类来实现,而布局类型包括:Consts.LAYOUT_***
布局的效果如下,实际程序中可以看到布局过程的动画效果:

看看这变态的自动布局

自动布局的实质

自动布局是自动对数据容器中所有网元进行重新定位,使节点分散开到好的显示效果,其实质是一种布局算法的实现,所以要定制自动布局就是要实现一种布局算法,以确定所有网元节点的位置。
TWaver
并没有自动布局的接口,通常一个自动布局器会实现一个叫“doLayout()”的函数,以保持命名上的统一。

自动布局“接口函数”
CustomLayouter#public function getLayoutResult():Dictionary
CustomLayouter#public function doLayout():void

getLayoutResult
getLayoutResult()
函数是布局算法的核心,其返回的是节点位置的计算结果,是一个Dictionary类型,< , >类型是。

doLayout
doLayout()
函数包含两步,先调用getLayoutResult()得到布局位置信息,然后将这些信息,设置到网元节点上,类似下面的代码:

public function doLayout(box:ElementBox, root:IData = null):void{

  var result:Dictionary = getLayoutResult(box, root);

  for(var element:* in result){

    var location:Point = result[element];

    (element as Node).location = location;

  }

}

 

从上面的示例代码可以看出,实际使用中会传入一些参数,这些根据实际情况而定。此外这里也可以使用动画的平移效果,起一个Timer,动画的将节点从原始位置移动到新的位置。

开始定制

下面开始布局算法,这里我们将布出下面的效果,整体是一个树结构,从左到右四层,最后一层使用Bus布局:

看看这变态的自动布局

因为只是一个示例,布局实现不可能尽善尽美,所以只提供些实现的思路,算是抛砖引玉,作为参考:

整理数据结构

首先理清数据的结构,本例是一个树状布局,具有固定的四层结构,故本例将每一层的节点找出来,如第一层一个节点,第二层四个节点,第三层十二个节点,第四层可能右八十多个节点。
本例中使用了dataBox的广度遍历,每遍历完一层,就对这层上的节点作布局,这样一次遍历完,布局也做完,也就是说“整理数据结构”和“分别对每层布局”是同时进行的,但这并不是很好的示范,通常还是老老实实先整理数据的结构,甚至做一些模型的封装了表示这些模型,然后再做布局。

 

看看这变态的自动布局

使用dataBox的广度遍历,每遍历完一层,就对这层上的节点作布局:

 

public function getLayoutResult(box:ElementBox, root:IData = null):Dictionary{

	var result:Dictionary = new Dictionary();



	function _calculateLocations(queue:Array, layer:int):void{

		var sum:int = queue.length;

		if(sum == 0){

			return;

		}

		queue.forEach(function(node:IData, index:int, a:*):void{

			var subIndex:int = index;

			var parentPoint:Point = null;

			var subSum:int = sum;

			if(node.parent){

				subIndex = node.parent.children.getItemIndex(node);

				parentPoint = result[node.parent];

				subSum = node.parent.childrenCount;

			}

			var location:Point = calculateLocation(node, layer, index , sum, subIndex, subSum, parentPoint);

			result[node] = location;

		});

	};



	var parent:IData = null;

	var layer:int = 0;

	var queue:Array = new Array();

	box.forEachByBreadthFirst(function(node:IData):void{

		if(!(node is Node)){

			return;

		}

		if(node.parent != parent){

			parent = node.parent;

			var l:int = 0;

			var p:IData = node.parent;

			while(p){

				l++;

				p = p.parent;

			}

			if(l != layer){

				_calculateLocations(queue, layer);

				layer++;

				queue = new Array();

			}

		}

		queue.push(node);

	}, root);



	_calculateLocations(queue, layer);

	return result;

}

 

分别对每层布局

有了上面这些信息,就很方便对各层作不同的布局:第一层到第四层,从左到右排列,每层节点从上到下排列,最后一层水平排列,本例中实现布局的函数是calculateLocation(),传入的参数包含了该节点布局相关的信息(所在层,在同层的位置,该层总共有多少节点,在其父节点的位置,其父节点所有的孩子数,父节点的布局位置)

 

public function calculateLocation(node:IData, layer:int, index:int, sum:int, subIndex:int, subSum:int, parentPoint:Point = null):Point

看看这变态的自动布局

var maxHeight:int = 700;

var startX:int = 10;

var startY:int = 10;

public function calculateLocation(node:IData, layer:int, index:int, sum:int, subIndex:int, subSum:int, parentPoint:Point = null):Point{

	var x:int = startX;

	var y:int = startY;

	var hGap:int = getHorizontalGap(layer);

	if(layer < 3){

		x = parentPoint ? parentPoint.x + hGap : startX;

		y = startY + getVerticalGap(layer, sum) * (index + 0.5);

	}else if(layer == 3 && parentPoint){

		x = parentPoint.x + hGap + subIndex/2 * 50;

		y = parentPoint.y + ((subIndex % 2 == 0 ) ? 15 : -15);

	}

	return new Point(x, y);

}



public function getHorizontalGap(layer:int):int{

	return layer == 3 ? 90 : 150;

}



public function getVerticalGap(layer:Number, sum:Number):Number{

	return Math.max(maxHeight/(sum + 0.5), 75);

}

连线样式的定制

本例中连线样式的配置也是关键,对于第四层Bus布局的呈现主要通过正交连线样式来体现,所以在数据添加时,对第四层的连线设置如下:

var link:Link = new Link(parent, node);

link.setStyle(Styles.LINK_TYPE, Consts.LINK_TYPE_HORIZONTAL_VERTICAL);

link.setStyle(Styles.LINK_CORNER, Consts.LINK_CORNER_NONE);

box.add(link);

查看完整源代码,请看这里

你可能感兴趣的:(布局)