不要设置figure的大小,而是应该在父中用setConstraint方法设置子的大小,
就像在swt里我们使用layout来控制各个控件的摆放位置一样,在Draw2D里最好也把这个工作交给LayoutManager来做。除非是在自己实现的Layout里,一般程序里自己不要轻易使用setBounds()、setLocation()和setSize()这些方法控制图形的位置和大小,而应该在为每个图形设置了适当的LayoutManager后,通过setConstraint()和setPreferredSize()等方法告诉layoutmanager如何布局。
在之前没有理解LayoutManager,如是就以一种尊重原著的心态,在实现一个复杂的编辑器布局的时候,拼命的用draw2d默认提供的布局进行拼凑,最终结果是代码的复杂度和耦合度急剧上升。
其实如果换一种思路,就是一个容器里面除了要显示的子,不应该为了布局而额外出现一些IFigure,就算出现也应该是尽量少的出现。当默认的布局不够用的时候,应该尝试自己扩展一个有效的LayoutManager。
这里顺便提一下SWT的局限性:SWT里面容器组件的布局跟容器组件是绑定的,也就是说,如果一个组件里面需要多种布局。它就必须有多个子,然后每个子里面再选择布局。如果从设计上布局就是一个没有形状只有规则的子,这样就显得更合理。
LayoutManager接口:布局管理器的基础接口,其中layout方法最为重要,在此次处实现布局的规则。另外invalidate起到一个清空的作用。
AbstractLayout:抽象实现。里面增加了两个属性:preferredSize容器的首选大小,isObservingVisibility我的理解这个属性描述的是,是否完全可见。这个类提供了一个抽象方法供其子实现,calculatePreferredSize(IFigure container,int wHint, int hHint)计算容器的大小,另外在setConstraint里面对子元素的位置信息进行清空,位置变了需要重新计算。
setConstraint(IFigure figure, Object constraint):方法中第二个参数是根据布局的需要来传入的,不一定是Rectangle
对于一个真实有效的布局管理器来说,它必须做两件事情:
1.一个就是根据一定的规则,算出整个容器的大小。如果大小不可以改变,那么就不能改变。
2.在Layout方法里面定义出一套规则来摆放它的子节点。
XYLayout:自由布局,绝对定位的布局。XY中有一个Map constraints保存了子的尺寸。如下是XYLayout实现布局的代码:
public void layout(IFigure parent) { Iterator children = parent.getChildren().iterator(); Point offset = getOrigin(parent); IFigure f; while (children.hasNext()) { f = (IFigure) children.next(); Rectangle bounds = (Rectangle) getConstraint(f); if (bounds == null) continue; if (bounds.width == -1 || bounds.height == -1) { Dimension preferredSize = f.getPreferredSize(bounds.width, bounds.height); bounds = bounds.getCopy(); if (bounds.width == -1) bounds.width = preferredSize.width; if (bounds.height == -1) bounds.height = preferredSize.height; } bounds = bounds.getTranslated(offset); f.setBounds(bounds); } }
第一步它获取父的x,y坐标offset,然后计算每个子的矩形区域Rectangle,其中对超出边界的矩形做了一些处理。里面
这句话没看明白:bounds = bounds.getCopy();没看懂。getConstraint中放的都是设置的Rectangle,而
getPreferredSize里面放的是首选矩形。
注意:这里的绝对定位是相对父容器来说的,而不是整个界面来说。
RulerLayout:继承自XYLayout,它是gef主要用来实现ZoomManager功能的时候自己扩展的,在gef中还有RulerFigure,RulerEditPart之类的,这个布局管理器并不通用。
public void layout(IFigure container) { List children = container.getChildren(); Rectangle rulerSize = container.getClientArea(); for (int i = 0; i < children.size(); i++) { IFigure child = (IFigure) children.get(i); Dimension childSize = child.getPreferredSize(); int position = ((Integer) getConstraint(child)).intValue(); if (((RulerFigure) container).isHorizontal()) { childSize.height = rulerSize.height - 1; Rectangle.SINGLETON.setLocation(position - (childSize.width / 2), rulerSize.y); } else { childSize.width = rulerSize.width - 1; Rectangle.SINGLETON.setLocation(rulerSize.x, position - (childSize.height / 2)); } Rectangle.SINGLETON.setSize(childSize); child.setBounds(Rectangle.SINGLETON); } }
FreeformLayout:继承自XYLayout,它的布局方式跟XYLayout一样,只是重写了getOrigin(IFigure figure)方法,在获取Origin坐标的时候有所不同。
AbstractHintLayout:继承自AbstractLayout,它提供了一个calculateMinimumSize方法用于计算长度和宽度。AbstractHintLayout的子类都是尺寸敏感位置不敏感的相对布局。
还有好几种相对布局,暂时先不总结
BorderLayout:一个东南西北中的布局,里面有五个静态常量来描述位置。