刚开始学习Java的时候,许多书籍都用讲过是使用Java2D和AWT/Swing进行图形绘制,那时候认为java绘制出来的图形很丑,界面很灰,怎么看怎么不舒服,后来SWT可以根据操作系统的不同调用本地方法,使得界面看起来更舒服,之后是基于SWT的draw2D图形处理包,这个处理包使我们可以更加容易的绘制一些我们想要的图形。
那么Draw2D和GEF的关系是什么呢?GEF本身是一个更大的概念,官网给出大量的举例说明其是一个MVC图像处理框架,其中Model由我们自己根据业务来设计,它要能够提供某种模型改变通知的机制,用来把Model的变化告诉Control层;Control层由一些EditPart实现,EditPart是整个GEF的核心部件;而View层(大多数情况下)就是我们这里要说的Draw2D了,其作用是把Model以图形化的方式表现给使用者。
GEF框架用Draw2D作为自己的视图层,同样我们可以独立使用Draw2D。
如果对SWT有一定的了解,那么draw2D就是依托于SWT Canvas的一个轻量级图形组件。为什么说是轻量级呢?因为所有的图形组件可以用称为figures的java普通对象来表示,一个轻量级组件系统本身需要一个figure来与SWT Canvas关联,并且可以挂接大多数SWT事件监听器,并把这些监听器传送到事件转发器上,事件转发器的作用在于可以处理适当的figure动作。
figure是有父子关系的,并且有属于自己的边界,可以通过不同的布局来管理这些figure,通过更新管理机制可以对figure的形状大小等进行改变。
总结Draw2D具有以下特点:
有效地布局与呈现支持
多种图形对象和布局实现
图形边界
光标和工具箱支持
连接锚点, 连接路径, 连接装饰
多样化的,透明层
灵活的坐标系统
视窗预览
打印
轻量级图形组件LWS是连接SWT和Draw2D的桥梁,利用它,我们不仅可以轻松创建任意形状的图形(不仅仅限于矩形),同时能够节省系统资源(因为是轻量级组件)。一个典型的纯Draw2D应用程序代码具有类似下面的结构:
import org.eclipse.draw2d.ButtonGroup;
import org.eclipse.draw2d.ButtonModel;
import org.eclipse.draw2d.ChangeEvent;
import org.eclipse.draw2d.ChangeListener;
import org.eclipse.draw2d.CheckBox;
import org.eclipse.draw2d.Clickable;
import org.eclipse.draw2d.Figure;
import org.eclipse.draw2d.Label;
import org.eclipse.draw2d.LightweightSystem;
import org.eclipse.draw2d.ToggleModel;
import org.eclipse.draw2d.XYLayout;
import org.eclipse.draw2d.geometry.Rectangle;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
public class Draw2D_example {
public static void main(String args[]) {
final Label label = new Label("Press a button!");
Shell shell = new Shell();
LightweightSystem lws = new LightweightSystem(shell);
Figure parent = new Figure();
parent.setLayoutManager(new XYLayout());
lws.setContents(parent);
Clickable above = new CheckBox("I'm above!");
parent.add(above, new Rectangle(10, 10, 80, 20));
ButtonModel aModel = new ToggleModel();
aModel.addChangeListener(new ChangeListener() {
public void handleStateChanged(ChangeEvent e) {
label.setText("Above");
}
});
above.setModel(aModel);
Clickable below = new CheckBox("I'm below!");
parent.add(below, new Rectangle(10, 40, 80, 20));
ButtonModel bModel = new ToggleModel();
bModel.addChangeListener(new ChangeListener() {
public void handleStateChanged(ChangeEvent e) {
label.setText("Below");
}
});
below.setModel(bModel);
ButtonGroup bGroup = new ButtonGroup();
bGroup.add(aModel);
bGroup.add(bModel);
bGroup.setDefault(bModel);
parent.add(label, new Rectangle(10, 70, 80, 20));
shell.setSize(130, 120);
shell.open();
shell.setText("Example");
Display display = Display.getDefault();
while (!shell.isDisposed()) {
if (!display.readAndDispatch())
display.sleep();
}
}
Draw2D提供了很多缺省图形,最常见的有三类:1、形状(Shape),如矩形、三角形、椭圆形等等;2、控件(Widget),如标签、按钮、滚动条等等;3、层(Layer),它们用来为放置于其中的图形提供缩放、滚动等功能,在3.0版本的GEF中,还新增了GridLayer和GuideLayer用来实现”吸附到网格”功能。在以IFigure为根节点的类树下有相当多的类。
我们上例使用的类Clickable属于空间类,类定义:public class Clickable extends Figure可以看到其继承了Figure类,而Figure类实现了public class Figure implements IFigure接口。
每个图形都可以拥有一个边框(Border),Draw2D所提供的边框类型有GroupBoxBorder、TitleBarBorder、ImageBorder、ButtonBorder,以及可以组合两种边框的CompoundBorder等等,在Draw2D里还专门有一个Insets类用来表示边框在图形中所占的位置,它包含上下左右四个整型数值。
我们知道,一个图形可以包含很多个子图形,这些被包含的图形在显示的时候必须以某种方式被排列起来,负责这个任务的就是父图形的LayoutManager。同样的,Draw2D已经为我们提供了一系列可以直接使用的LayoutManager,如FlowLayout适合用于表格式的排列,如我们上述例子所用的XYLayout适合让用户在画布上用鼠标随意改变图形的位置,等等。如果没有适合我们应用的LayoutManager,可以自己定制。每个LayoutManager都包含某种算法,该算法将考虑与每个子图形关联的Constraint对象,计算得出子图形最终的位置和大小。
图形化应用程序的一个常见任务就是在两个图形之间做连接,想象一下UML类图中的各种连接线,或者程序流程图中表示数据流的线条,它们有着不同的外观,有些连接线还要显示名称,而且最好能不交叉。利用Draw2D中的Router、Anchor和Locator,可以实现多种连接样式,其中Router负责连接线的外观和操作方式,最简单的是设置Router为null(无Router),这样会使用直线连接,其他连接方式包括折线、具有控制点的折线等等(见图3),若想控制连接线不互相交叉也需要在Router中作文章。Anchor控制连接线端点在图形上的位置,即”锚点”的位置,最易于使用的是ChopBoxAnchor,它先假设图形中心为连接点,然后计算这条假想连线与图形边缘的交汇点作为实际的锚点,其他Anchor还有EllipseAnchor、LabelAnchor和XYAnchor等等;最后,Locator的作用是定位图形,例如希望在连接线中点处以一个标签显示此连线的名称/作用,就可以使用MidpointLocator来帮助定位这个标签,其他Locator还有ArrowLocator用于定位可旋转的修饰(Decoration,例如PolygonDecoration)、BendpointerLocator用于定位连接控制点、ConnectionEndpointLocator用于定位连接端点(通过指定uDistance和vDistance属性的值可以设置以端点为原点的坐标)。
下面举一个UML类图的例子,这个和例子是从网上找到的。
下图是一个简单的UML类图:
创建图形的第一步是确定使用什么组件来合成图形,以及它们是如何排列的。
例图由三个子图形组成。图形本身为UMLClassFigure,它的第一个子图形是Label,用来显示类名(本例为“Table”),另外两个 子图形是用来放置属性及方法的容器。我们将创建一个叫做CompartmentFigure的图形来达到容器的目的。UMLClassFigure和 CompartmentFigure都使用ToolbarLayout来放置子图形,下图是它在概念上的上层结构图:
CompartmentFigure用来放置方法和属性,这个类继承org.eclipse.draw2d.Figure类,并且使用 ToolbarLayout来管理子图形的布局,同时,CompartmentFigure使用了一个自定义的边框。这个边框只是简单的在它的上端画一条 1个象素粗细的线,作为两个CompartmentFigure的分隔。CompartmentFigure的代码如下:
public class CompartmentFigure extends Figure {
public CompartmentFigure() {
ToolbarLayout layout = new ToolbarLayout();
layout.setMinorAlignment(ToolbarLayout.ALIGN_TOPLEFT);
layout.setStretchMinorAxis(false);
layout.setSpacing(2);
setLayoutManager(layout);
setBorder(new CompartmentFigureBorder());
}
public class CompartmentFigureBorder extends AbstractBorder {
public Insets getInsets(IFigure figure) {
return new Insets(1,0,0,0);
}
public void paint(IFigure figure, Graphics graphics, Insets insets) {
graphics.drawLine(getPaintRectangle(figure, insets).getTopLeft(),
tempRect.getTopRight());
}
}
}
UMLClassFigure类在很多地方都和CompartmentFigure类很相似,它包含三个子图形——用来放置方法和属性的两个 CompartmentFigure和一个用来显示类名的Draw2D标签(Label)。它也使用了垂直导向的ToolbarLayout来管理子图形 的布局。UMLClassFirgure使用Draw2D的LineBorder在自己的边缘绘制矩形边框。
public class UMLClassFigure extends Figure {
public static Color classColor = new Color(null,255,255,206);
private CompartmentFigure attributeFigure = new CompartmentFigure();
private CompartmentFigure methodFigure = new CompartmentFigure();
public UMLClassFigure(Label name) {
ToolbarLayout layout = new ToolbarLayout();
setLayoutManager(layout);
setBorder(new LineBorder(ColorConstants.black,1));
setBackgroundColor(classColor);
setOpaque(true);
add(name);
add(attributeFigure);
add(methodFigure);
}
public CompartmentFigure getAttributesCompartment() {
return attributeFigure;
}
public CompartmentFigure getMethodsCompartment() {
return methodFigure;
}
}
Draw2D提供了一种成为连接(Connection)特殊的图形,用来连接两个图形。要在Draw2D中创建连接,首先要创建连接的两个端点 (endpoint)。这辆个端点成为源支撑点(source anchor)和目标支撑点(target anchor)。端点由实现了ConnectionAnchor接口的类来创建。在支撑点创建好后,便可通过调用 setSourceAnchor(ConnectionAnchor)方法和setTargetAnchor(ConnectionAnchor)方法来 将其设定为端点。本例使用了称为ChopboxAnchor的支撑点,这种支撑点将端点放置在图形的边缘,并且将方向指向图形的中心。
PolylineConnection c = new PolylineConnection();
ChopboxAnchor sourceAnchor = new ChopboxAnchor(classFigure);
ChopboxAnchor targetAnchor = new ChopboxAnchor(classFigure2);
c.setSourceAnchor(sourceAnchor);
c.setTargetAnchor(targetAnchor);
由PolyLineConnection连接的两个UMLClassFirgure
Draw2d同时也为端点提供了箭头等形状的可以具有一定意义的装饰。和UML一样,我们将创建一个实心的菱形装饰来表示组合关系。这将由 PolygonDecoration来实现。PolygonDecoration的缺省形状是实心箭头,然而可以通过创建新模板来实现任何形状。模板由 PointList定义,通过调用setTemplate(PointList)来指定。
以下代码演示了如何将PolygonDecoration加到连接上去:
PolygonDecoration decoration = new PolygonDecoration();
PointList decorationPointList = new PointList();
decorationPointList.addPoint(0,0);
decorationPointList.addPoint(-2,2);
decorationPointList.addPoint(-4,0);
decorationPointList.addPoint(-2,-2);
decoration.setTemplate(decorationPointList);
c.setSourceDecoration(decoration);
除了装饰之外,我们还可以给连接增加其他的Draw2d图形。这可以通过调用连接的add(IFigure figure, Object constratint)方法实现,其中figure为要增加的图形,constraint是一个实现了Locator接口的类,由Locator将图形 放置在连接上。我们将使用这种技术来为我们的类图加上标签。ConnectionEndpointLocator可以将图形放置在连接的源端点或目标端点 上,因而我们选用它来放置标签。我们还可以通过调用setUDistance(int)和setVDistance(int)方法来指定图形与端点的举例 (其中uDistance是连接的图形到将要加上去的图形的距离,而vDistance则是将要加上的图形到连接本身的举例)。
以下代码演示了如何使用ConnectionEndpointLocator给一个连接加上标签:
ConnectionEndpointLocator targetEndpointLocator =
new ConnectionEndpointLocator(c, true);
targetEndpointLocator.setVDistance(15);
Label targetMultiplicityLabel = new Label("1..*");
c.add(targetMultiplicityLabel, targetEndpointLocator);
ConnectionEndpointLocator sourceEndpointLocator =
new ConnectionEndpointLocator(c, false);
sourceEndpointLocator.setVDistance(15);
Label sourceMultiplicityLabel = new Label("1");
c.add(sourceMultiplicityLabel, sourceEndpointLocator);
ConnectionEndpointLocator relationshipLocator =
new ConnectionEndpointLocator(c,true);
relationshipLocator.setUDistance(30);
relationshipLocator.setVDistance(-20);
Label relationshipLabel = new Label("contains");
c.add(relationshipLabel,relationshipLocator);
测试类包含了一个main方法来创建一个含有Draw2d LightweightSystem(LWS,用来连接SWT和Draw2d)的SWT Shell。例子使用Draw2d的Figure类作为LWS的内容,并在Figure上增加了两个UMLClassFigure。然后在两个图形间创建 了一个带有菱形端点装饰和标签的连接。
测试类使用了几个图片{ ,,},请自行下载并放在Java工程的根文件夹下。
import org.eclipse.draw2d.*;
import org.eclipse.draw2d.geometry.PointList;
import org.eclipse.draw2d.geometry.Rectangle;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
/** * A test class to display a UMLFigure */
public class UMLClassFigureTest {
public static void main(String args[]){
Display d = new Display();
final Shell shell = new Shell(d);
shell.setSize(400, 400);
shell.setText("UMLClassFigure Test");
LightweightSystem lws = new LightweightSystem(shell);
Figure contents = new Figure();
XYLayout contentsLayout = new XYLayout();
contents.setLayoutManager(contentsLayout);
Font classFont = new Font(null, "Arial", 12, SWT.BOLD);
Label classLabel1 = new Label("Table", new Image(d,
UMLClassFigureTest.class.getResourceAsStream("class_obj.gif")));
classLabel1.setFont(classFont);
Label classLabel2 = new Label("Column", new Image(d,
UMLClassFigureTest.class.getResourceAsStream("class_obj.gif")));
classLabel2.setFont(classFont);
final UMLClassFigure classFigure = new UMLClassFigure(classLabel1);
final UMLClassFigure classFigure2 = new UMLClassFigure(classLabel2);
Label attribute1 = new Label("columns: Column[]", new Image(d,
UMLClassFigure.class.getResourceAsStream("field_private_obj.gif")));
Label attribute2 = new Label("rows: Row[]", new Image(d,
UMLClassFigure.class.getResourceAsStream("field_private_obj.gif")));
Label attribute3 = new Label("columnID: int", new Image(d,
UMLClassFigure.class.getResourceAsStream("field_private_obj.gif")));
Label attribute4 = new Label("items: List", new Image(d,
UMLClassFigure.class.getResourceAsStream("field_private_obj.gif")));
classFigure.getAttributesCompartment().add(attribute1);
classFigure.getAttributesCompartment().add(attribute2);
classFigure2.getAttributesCompartment().add(attribute3);
classFigure2.getAttributesCompartment().add(attribute4);
Label method1 = new Label("getColumns(): Column[]", new Image(d,
UMLClassFigure.class.getResourceAsStream("methpub_obj.gif")));
Label method2 = new Label("getRows(): Row[]", new Image(d,
UMLClassFigure.class.getResourceAsStream("methpub_obj.gif")));
Label method3 = new Label("getColumnID(): int", new Image(d,
UMLClassFigure.class.getResourceAsStream("methpub_obj.gif")));
Label method4 = new Label("getItems(): List", new Image(d,
UMLClassFigure.class.getResourceAsStream("methpub_obj.gif")));
classFigure.getMethodsCompartment().add(method1);
classFigure.getMethodsCompartment().add(method2);
classFigure2.getMethodsCompartment().add(method3);
classFigure2.getMethodsCompartment().add(method4);
contentsLayout.setConstraint(classFigure, new Rectangle(10,10,-1,-1));
contentsLayout.setConstraint(classFigure2, new Rectangle(200, 200, -1, -1));
/* Creating the connection */
PolylineConnection c = new PolylineConnection();
ChopboxAnchor sourceAnchor = new ChopboxAnchor(classFigure);
ChopboxAnchor targetAnchor = new ChopboxAnchor(classFigure2);
c.setSourceAnchor(sourceAnchor);
c.setTargetAnchor(targetAnchor);
/* Creating the decoration */
PolygonDecoration decoration = new PolygonDecoration();
PointList decorationPointList = new PointList();
decorationPointList.addPoint(0,0);
decorationPointList.addPoint(-2,2);
decorationPointList.addPoint(-4,0);
decorationPointList.addPoint(-2,-2);
decoration.setTemplate(decorationPointList);
c.setSourceDecoration(decoration);
/* Adding labels to the connection */
ConnectionEndpointLocator targetEndpointLocator =
new ConnectionEndpointLocator(c, true);
targetEndpointLocator.setVDistance(15);
Label targetMultiplicityLabel = new Label("1..*");
c.add(targetMultiplicityLabel, targetEndpointLocator);
ConnectionEndpointLocator sourceEndpointLocator =
new ConnectionEndpointLocator(c, false);
sourceEndpointLocator.setVDistance(15);
Label sourceMultiplicityLabel = new Label("1");
c.add(sourceMultiplicityLabel, sourceEndpointLocator);
ConnectionEndpointLocator relationshipLocator =
new ConnectionEndpointLocator(c,true);
relationshipLocator.setUDistance(10);
relationshipLocator.setVDistance(-20);
Label relationshipLabel = new Label("contains");
c.add(relationshipLabel,relationshipLocator);
contents.add(classFigure);
contents.add(classFigure2);
contents.add(c);
lws.setContents(contents);
shell.open();
while (!shell.isDisposed())
while (!d.readAndDispatch())
d.sleep();
}
}
参考实例:http://blog.csdn.net/pengpeng2395/article/details/2952230
网址:http://www.eclipse.org/gef/draw2d/index.php
文章:http://www.cnblogs.com/bjzhanghao/archive/2005/02/13/104045.html