我有这样一个需求
web页面上有字符串 A1 ,A2, A3分别表示三个模块
点击A1,A2,A3会分别进入到A1 ,A2, A3的详细表示画面。
A1 ,A2, A3之间有调用关系,这种调用关系希望能够通过线和箭头标识出来。例如
A1 A3
A2
A4A1调用A2,A2调用A4,所以A1与A2之间要画一个箭头,A2与A4之间要画一个箭头, A1与A3之间是并列的关系,要画一个横向的箭头。请问上面的需求应该用什么技术实现?
web页面上有字符串 A1 ,A2, A3分别表示三个模块
点击A1,A2,A3会分别进入到A1 ,A2, A3的详细表示画面。
A1 ,A2, A3之间有调用关系,这种调用关系希望能够通过线和箭头标识出来。例如
A1 A3
A2
A4A1调用A2,A2调用A4,所以A1与A2之间要画一个箭头,A2与A4之间要画一个箭头, A1与A3之间是并列的关系,要画一个横向的箭头。请问上面的需求应该用什么技术实现?
import java.awt.*;<p>/**
* 在两个Swing控件之间画箭头
* @author <a:href = "mailto:[email protected]">seth yang</a>
*/
public class Arrow {
/** 源控件,即箭头的出发控件 */
private JComponent source;
/** 目标控件,即箭头的指向控件 */
private JComponent target;
/** 箭头中间的文本,通常是该箭头标识的关系的名称 */
private String name = "";
/** 源控件关联出去的字段 */
private String sourceField = "";
/** 目标控件的关联字段 */
private String targetField = "";
/** 箭头扇形的开始角度 */
private int startAngle = 0;<p> /** 箭头扇形的半径 */
private static final int ARROW_HEIGHT = 20;
/** 箭头扇形的角度 */
private static final int ARROW_ANGLE = 30;<p> public Arrow (JComponent source, JComponent target) {
this (source.getName () + "_" + target.getName (), source, target);
}
/**
* 构造函数
*
* 创建指定名称,源控件和目标控件的箭头
* @param name 箭头的名称
* @param source 箭头的出发控件
* @param target 箭头的指向控件
*/
public Arrow (String name, JComponent source, JComponent target) {
this (name, source, target, source.getName (), target.getName ());
}<p> /**
* 构造函数
*
* 创建指定名称,源控件和目标控件的箭头
* @param name 箭头的名称
* @param source 箭头的出发控件
* @param target 箭头的指向控件
* @param sourceField 箭头的出发字段
* @param targetField 箭头的指向字段
*/
public Arrow (String name, JComponent source, JComponent target, String sourceField, String targetField) {
setName (name);
setSource (source);
setTarget (target);
this.setSourceField (sourceField);
this.setTargetField (targetField);
}<p> private Point getCenter (JComponent c) {
int x = c.getLocation ().x;
int y = c.getLocation ().y;
int w = c.getSize ().width;
int h = c.getSize ().height;<p> return new Point (x + w / 2, y + h / 2);
}<p> public void draw (Graphics g) {
g.setColor (Color.black);
Point ps = getClosestPoint (getTarget (), getSource ());
Point pt = getClosestPoint (getSource (), getTarget ());
// 画线 g.drawLine (ps.x, ps.y, pt.x, pt.y);<p> // 画箭头 // 简单起见,在这里从线段终点填充一个30度的扇形 if (ps.x != pt.x && ps.y != pt.y) {
double k = getK (ps, pt);
if (ps.x > pt.x)
startAngle = 360 - (int) (Math.atan (k) * 180 / Math.PI) - 15;
else
startAngle = 180 - (int) (Math.atan (k) * 180 / Math.PI) - 15;
}
// 圆心 Point pc = new Point (pt.x - ARROW_HEIGHT, pt.y - ARROW_HEIGHT);
g.fillArc (pc.x, pc.y, 2 * ARROW_HEIGHT, 2 * ARROW_HEIGHT, startAngle, ARROW_ANGLE);<p> FontMetrics fm = g.getFontMetrics ();
int ascent = fm.getAscent ();
int descent = fm.getDescent ();<p> // 在线条中心点处显示名称 int mx = (ps.x + pt.x) / 2;
int my = (ps.y + pt.y) / 2;
g.drawString (name, mx, my + ascent);<p> if (sourceField == null) sourceField = "";
if (targetField == null) targetField = "";<p> if (ps.y < pt.y) {// 源在目标上方,目标文字应该在更上面一些,源文字应该更下面一些 // 在箭头处显示目标 if (ps.x > pt.x) // 目标在源的左方 g.drawString (getTargetField (), pt.x - fm.stringWidth (getTargetField ()), pt.y - ascent - descent);
else
g.drawString (getTargetField (), pt.x, pt.y - ascent - descent);
// 在线段起点处显示源 g.drawString (getSourceField (), ps.x, ps.y + ascent);
} else {
// 在箭头处显示目标 if (ps.x > pt.x) // 目标在源的左方 g.drawString (getTargetField (), pt.x - fm.stringWidth (getTargetField ()), pt.y + ascent + descent);
else
g.drawString (getTargetField (), pt.x, pt.y + ascent + descent);
// 在线段起点处显示源 g.drawString (getSourceField (), ps.x, ps.y - descent);
}
}<p> /**
* @param source 源控件
* @param target 目的控件
*/
private Point getClosestPoint (JComponent source, JComponent target) {
Point ps = getCenter (source);
Point pt = getCenter (target);<p> if (ps.x == pt.x) { // 垂直线 if (ps.y < pt.y) {// 源在目标上方 this.startAngle = 90 - ARROW_ANGLE / 2;
return new Point (ps.x, target.getLocation ().y);
}
startAngle = 270 - ARROW_ANGLE / 2;
return new Point (ps.x, target.getLocation ().y + target.getSize ().height);
}<p> if (Math.abs (ps.y - pt.y) < 15) { // 水平线 if (ps.x < pt.x) {// 源在目标左边 startAngle = 180 - ARROW_ANGLE / 2;
return new Point (target.getLocation ().x, ps.y);
}
startAngle = - ARROW_ANGLE / 2;
return new Point (target.getLocation ().x + target.getSize ().width, ps.y);
}<p> double k0 = getK (ps, pt);
// 直线方程: // y = kx + b // x = (y - b) / k double b = getB (ps, pt);
int xt = target.getLocation ().x;
int yt = target.getLocation ().y;
int w = target.getWidth ();
int h = target.getHeight ();<p> class DPoint extends Point {
public static final int D_H = 0;
public static final int D_V = 1;
private int d;<p> public DPoint (int x, int y, int d) {
super (x, y);
this.d = d;
}<p> public int getD () {
return d;
}
}<p> // 取于水平边相交的点 DPoint[] pts = new DPoint[4];
// 取上边的点 DPoint pq = new DPoint ((int) ((yt - b) / k0), yt, DPoint.D_H);
pts[0] = pq;<p> // 取下边的点 pq = new DPoint ((int) ((yt + h - b) / k0), yt + h, DPoint.D_H);
pts[1] = pq;
// 取左边的点 pq = new DPoint (xt, (int) (k0 * xt + b), DPoint.D_V);
pts[2] = pq;
// 取右边的点 pq = new DPoint (xt + w, (int) (k0 * (xt + w) + b), DPoint.D_V);
pts[3] = pq;<p> DPoint p1 = null;
DPoint p2 = null;
for (int i = 0; i < pts.length; i++) {
DPoint p = pts[i];
if (p.x < target.getLocation ().x || p.x > target.getLocation ().x + target.getSize ().width ||
p.y < target.getLocation ().y || p.y > target.getLocation ().y + target.getSize ().height)
continue;
if (p1 == null)
p1 = p;
else
p2 = p;
}<p> if (p1.getD () == DPoint.D_H) { // 水平线 if (Math.abs (p1.y - ps.y) < Math.abs (p2.y - ps.y))
return p1;
return p2;
} else { // 垂直线 if (Math.abs (p1.x - ps.x) < Math.abs (p2.x - ps.x))
return p1;
return p2;
}
}<p> /**
}
*
* <i>注意,该方法可能抛出异常,当p1.x = p2.x时将抛出被0除的异常</i>
* @param p1 线段的端点
* @param p2 线段的另一个端点
* @return 线段的斜率
*/
private double getK (Point p1, Point p2) {
return ((double) p2.y - p1.y) / (p2.x - p1.x);
}<p> /**
* 划去直线方程中的b值
* @param ps 线段的端点
* @param pt 线段的另一个端点
* @return 直线方程中的b值
*/
private double getB (Point ps, Point pt) {
double x1 = ps.x;
double y1 = ps.y;
double x2 = pt.x;
double y2 = pt.y;<p> return ((double) y1 * x2 - y2 * x1) / (x2 - x1);
}<p> public JComponent getSource () {
return source;
}<p> public JComponent getTarget () {
return target;
}<p> public String getName () {
return name;
}<p> public void setName (String name) {
this.name = name;
}<p> public void setSource (JComponent source) {
this.source = source;
}<p> public void setTarget (JComponent target) {
this.target = target;
}<p> public String getSourceField () {
return sourceField;
}<p> public void setSourceField (String sourceField) {
this.sourceField = sourceField;
}<p> public String getTargetField () {
return targetField;
}<p> public void setTargetField (String targetField) {
this.targetField = targetField;
}
}<p>测试类
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;<p>public class Test extends JPanel {
private int prevx, prevy;
private Arrow arrow;
private MouseListener ml = new MouseAdapter () {
public void mousePressed (MouseEvent e) {
prevx = e.getX ();
prevy = e.getY ();
}
};
private MouseMotionListener mml = new MouseMotionAdapter () {
public void mouseDragged (MouseEvent e) {
Component c = e.getComponent ();
c.setLocation (c.getLocation ().x + e.getX () - prevx, c.getLocation ().y + e.getY () - prevy);
repaint ();
}
};
public Test() throws Exception {
JLabel source = new JLabel ("source");
source.setSize (50,50);
source.setLocation (0, 0);
source.setBorder (BorderFactory.createEtchedBorder ());
source.setHorizontalAlignment (JLabel.CENTER);
source.addMouseListener (ml);
source.addMouseMotionListener (mml);<p> JLabel target = new JLabel ("target");
target.setSize (50, 50);
target.setLocation (100, 100);
target.setBorder (BorderFactory.createEtchedBorder ());
target.setHorizontalAlignment (JLabel.CENTER);
target.addMouseListener (ml);
target.addMouseMotionListener (mml);<p> setLayout (null);
add (source, null);
add (target, null);<p> arrow = new Arrow ("arrow", source, target, "source_field", "target_field");
}<p> public void paint (Graphics g) {
super.paint (g);
arrow.draw (g);
}<p> public static void main(String[] args) throws Exception {
JFrame f = new JFrame ("test");
f.getContentPane ().setLayout (new BorderLayout ());
f.getContentPane ().add (new Test (), BorderLayout.CENTER);
f.setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE);
f.setSize (800, 600);
f.show ();
}
能够给一个能够在jsp页面使用的例子,而且,A1,A2,等是要和其他页面有链接的。