4、使用Graphics来创建图形
在SWT中,graphic context(GC)可以单独创建为一个对象,也可以通过PaintEvent获得。但是在Draw2d中,Figure
可以从2.1节中讲述的那些paint方法中获得Graphics(方法参数)。Graphics的绝大多数方法都和CG中的相同,最大
的不同是Draw2d允许Graphics对象利用translate()方法移动。
然而,Draw2d提供了更为强大的功能来创建和操纵几何图形(Shapes)。它有好几个包、很多类用来处理几何图形。
4.1、使用Graphics类
如前所述,Graphics类的方法和SWT的GC基本一样,所以,在这一小节里,我们将创建图C.2种列出的那些组件,即
如下三种Figure:
1)DecisionFigure——即程序流程图中的分支结构,一个输入,两个输出(是/否)。
2)ProcessFigure——流程图中的某个处理,一个输入,一个输出。
3)TerminatorFigure——表示一个程序流程的开始或者结束,只有一个输入(结束)或输出(开始)。
对于我们的以上三种Figure,他们的大小由一个叫做size的变量来控制,他们上面显示的文字由一个叫做message
的String来设置。这些限制都来自于Figure对象的外部,所以暂时你还看不到。
这些类的代码如下:(列表C.3至C.5)
列表C.3:DecisionFigure.java
package com.swtjface.AppC;
import org.eclipse.draw2d.*;
import org.eclipse.draw2d.geometry.*;
public class DecisionFigure extends ActivityFigure
{
FixedAnchor inAnchor, yesAnchor, noAnchor;
public DecisionFigure()
{
inAnchor = new FixedAnchor(this);
inAnchor.place = new Point(1, 0);
targetAnchors.put("in_dec",inAnchor);
noAnchor = new FixedAnchor(this);
noAnchor.place = new Point(2, 1);
sourceAnchors.put("no",noAnchor);
yesAnchor = new FixedAnchor(this);
yesAnchor.place = new Point(1, 2);
sourceAnchors.put("yes",yesAnchor);
}
public void paintFigure(Graphics g)
{
Rectangle r = bounds;
PointList pl = new PointList(4);
pl.addPoint(r.x + r.width/2, r.y);
pl.addPoint(r.x, r.y + r.height/2);
pl.addPoint(r.x + r.width/2, r.y + r.height-1);
pl.addPoint(r.x + r.width, r.y + r.height/2);
g.drawPolygon(pl);
g.drawText(message, r.x+r.width/4+5, r.y+3*r.height/8);
g.drawText("N", r.x+7*r.width/8, r.y+3*r.height/8);
g.drawText("Y", r.x+r.width/2-2, r.y+3*r.height/4);
}
}
由于分支结构不是规则图形(它是个棱形),无法使用既有图形,所以我们需要指定一系列点来描绘它;谢天谢地,
ProcessFigure是一个矩形(Rectangle),所以容易创建地多。
列表C.4:ProcessFigure.java
package com.swtjface.AppC;
import org.eclipse.draw2d.*;
import org.eclipse.draw2d.geometry.*;
public class ProcessFigure extends ActivityFigure
{
FixedAnchor inAnchor, outAnchor;
public ProcessFigure()
{
inAnchor = new FixedAnchor(this);
inAnchor.place = new Point(1, 0);
targetAnchors.put("in_proc", inAnchor);
outAnchor = new FixedAnchor(this);
outAnchor.place = new Point(1, 2);
sourceAnchors.put("out_proc", outAnchor);
}
public void paintFigure(Graphics g)
{
Rectangle r = bounds;
g.drawText(message, r.x + r.width/4, r.y + r.height/4);
g.drawRectangle(r.x, r.y, r.width-1, r.height-1);
}
}
TerminatorFigure的左右两条边是圆弧,所以比ProcessFigure要麻烦点,但代码却是不难理解的。
列表C.5:TerminatorFigure.java
package com.swtjface.AppC;
import org.eclipse.draw2d.*;
import org.eclipse.draw2d.geometry.*;
public class TerminatorFigure extends ActivityFigure
{
FixedAnchor inAnchor, outAnchor;
public TerminatorFigure()
{
inAnchor = new FixedAnchor(this);
inAnchor.place = new Point(1, 0);
targetAnchors.put("in_term",inAnchor);
outAnchor = new FixedAnchor(this);
outAnchor.place = new Point(1, 2);
sourceAnchors.put("out_term",outAnchor);
}
public void paintFigure(Graphics g)
{
Rectangle r = bounds;
g.drawArc(r.x + r.width/8, r.y, r.width/4, r.height-1, 90, 180);
g.drawLine(r.x + r.width/4, r.y, r.x + 3*r.width/4, r.y);
g.drawLine(r.x + r.width/4, r.y + r.height-1, r.x + 3*r.width/4,
r.y + r.height-1);
g.drawArc(r.x + 5*r.width/8, r.y, r.width/4, r.height-1, 270, 180);
g.drawText(message, r.x+3*r.width/8, r.y+r.height/8);
}
}
显然,这些类不会只负责将图形画出来就完事了,还有两个更复杂的方面:一是这些Figure需要连接到其他的Figure
上去,这需要使用ConnectionAnchors(叫做FixedAnchors)。第二,这些图形将来在讲述GEF的时候还要用到,很
多方法现在解释还为时过早(作者这里主要指拖拽、编辑上面的文字等功能)。
由于所有这三个类都继承了ActivityFigure,所以现在该它登场了。这个基类包含上述三个Figure的共性。如列表
C.6所示,他的大部分方法用来跟踪Connections以及Connections的anchors。
列表C.6:ActivityFigure.java
package com.swtjface.AppC;
import org.eclipse.draw2d.*;
import org.eclipse.draw2d.geometry.*;
import java.util.*;
abstract public class ActivityFigure
extends Figure
{
Rectangle r = new Rectangle();
Hashtable targetAnchors = new Hashtable();
Hashtable sourceAnchors = new Hashtable();
String message = new String();
public void setName(String msg)
{
message = msg;
repaint();
}
public ConnectionAnchor ConnectionAnchorAt(Point p)
{
ConnectionAnchor closest = null;
long min = Long.MAX_VALUE;
Hashtable conn = getSourceConnectionAnchors();
conn.putAll(getTargetConnectionAnchors());
Enumeration e = conn.elements();
while (e.hasMoreElements())
{
ConnectionAnchor c = (ConnectionAnchor) e.nextElement();
Point p2 = c.getLocation(null);
long d = p.getDistance2(p2);
if (d < min)
{
min = d;
closest = c;
}
}
return closest;
}
public ConnectionAnchor getSourceConnectionAnchor(String name)
{
return (ConnectionAnchor)sourceAnchors.get(name);
}
public ConnectionAnchor getTargetConnectionAnchor(String name)
{
return (ConnectionAnchor)targetAnchors.get(name);
}
public String getSourceAnchorName(ConnectionAnchor c)
{
Enumeration enum = sourceAnchors.keys();
String name;
while (enum.hasMoreElements())
{
name = (String)enum.nextElement();
if (sourceAnchors.get(name).equals(c))
return name;
}
return null;
}
public String getTargetAnchorName(ConnectionAnchor c)
{
Enumeration enum = targetAnchors.keys();
String name = null;
while (enum.hasMoreElements())
{
name = (String)enum.nextElement();
if (targetAnchors.get(name).equals(c))
return name;
}
return null;
}
public ConnectionAnchor getSourceConnectionAnchorAt(Point p)
{
ConnectionAnchor closest = null;
long min = Long.MAX_VALUE;
Enumeration e = getSourceConnectionAnchors().elements();
while (e.hasMoreElements())
{
ConnectionAnchor c = (ConnectionAnchor) e.nextElement();
Point p2 = c.getLocation(null);
long d = p.getDistance2(p2);
if (d < min)
{
min = d;
closest = c;
}
}
return closest;
}
public Hashtable getSourceConnectionAnchors()
{
return sourceAnchors;
}
public ConnectionAnchor getTargetConnectionAnchorAt(Point p)
{
ConnectionAnchor closest = null;
long min = Long.MAX_VALUE;
Enumeration e = getTargetConnectionAnchors().elements();
while (e.hasMoreElements())
{
ConnectionAnchor c = (ConnectionAnchor) e.nextElement();
Point p2 = c.getLocation(null);
long d = p.getDistance2(p2);
if (d < min)
{
min = d;
closest = c;
}
}
return closest;
}
public Hashtable getTargetConnectionAnchors()
{
return targetAnchors;
}
}
对于ConnectionAnchor和Connection,我们将稍后再讨论。现在让我们看一下Draw2d的几何包。
4.2、Draw2d中的几何和图论
你已经看过如何使用Point和Rectangle,但Draw2d还提供了其他更多的类来处理几何图形。例如更高精度的类
PrecisionPoint,PrecisionRectangle,以及PrecisionDimension。此外还有Ray对象,其作用就像数学中的向量
一样;以及Transform类,用来实现移动、旋转、缩放等功能。
包org.eclipse.draw2d.graph里面有一些用来创建和分析有向图的工具类,例如基本的Node和Edge,以及特有的
布局管理器DirectedGraphLayout。图论已远超本书范围,但如果你对此有兴趣,这个包应该是很有用的。
Draw2d的Figure之间并非是用Edge连接的,而是用Connection,下面来讨论这个类。