我有这样一个需求
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之间是并列的关系,要画一个横向的箭头。请问上面的需求应该用什么技术实现?
       

解决方案 »

  1.   

    vml 标签可以 不过是ie上的
      

  2.   

    import javax.swing.*;
    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>    /**
      }
      

  3.   

       * 获取线段的斜率
         *
         * <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 ();
        }
      

  4.   

    楼上给出的这些内容如何在web页面使用啊?
    能够给一个能够在jsp页面使用的例子,而且,A1,A2,等是要和其他页面有链接的。
      

  5.   

    弄个图片不行吗?干嘛这么麻烦,直接用webdings就可以了吧。
      

  6.   

    顶三楼 我原以为只能用图片呢,没想到用java代码也能实现