在两个java图形之间绘制一条可移动的线

时间:2015-07-31 22:16:31

标签: java swing

How it looks

火柴人通过一条线连接到气泡。例如,当我移动Jimmy时,我想要将Jimmy连接到他销售的Fruit上的线路进行维护。当我拖动Fruit时也是如此。

但不知怎的,这不起作用。当我拖动火柴人或泡沫时,线条脱节。 enter image description here

以下是我的代码,如果有人想尝试运行它。我试图只包括相关内容。

示例课程

import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JPanel;

public class Example extends JPanel {

private static List<Person> persons;
private static List<Fruit> fruits;
private static List<LineTest> lines;
private Point mousePt;
private static Font setFont;
private Random randomGenerator;
private Person person;
private Fruit bubble;
private LineTest line;
private static final int W = 640;    
private static final int H = 480; 

public Example() {
    persons = new ArrayList<Person>();  // Stores the person's names & coords
    fruits = new ArrayList<Fruit>(); // Stores the person's name and what fruits he sells & coords
    lines = new ArrayList<LineTest>(); // Stores the person's name, fruits he sells & coords
    randomGenerator = new Random();
    setFont = new Font("Sans Serif", Font.BOLD, 12);

    String person1 = "Jimmy";
    String person2 = "Sally";

    person = new Person(person1, 50,50);
    addPerson(person);
    person = new Person(person2, 50,150);
    addPerson(person);

    String fruit1 = "Banana";
    String fruit2 = "Apple";
    String fruit3 = "Orange";
    String fruit4 = "Watermelon";
    String fruit5 = "Pineapple";
    String fruit6 = "Grapes";

    bubble = new Fruit(person1, fruit1, setFont, 150, 50);
    addFruit(bubble);
    bubble = new Fruit(person1, fruit2, setFont, 150, 100);
    addFruit(bubble);
    bubble = new Fruit(person1, fruit3, setFont, 150, 150);
    addFruit(bubble);
    bubble = new Fruit(person2, fruit4, setFont, 150, 200);
    addFruit(bubble);
    bubble = new Fruit(person2, fruit5, setFont, 150, 250);
    addFruit(bubble);
    bubble = new Fruit(person2, fruit6, setFont, 150, 300);
    addFruit(bubble);

    for (int i=0; i<persons.size();i++) {
        for (int j=0; j<fruits.size();j++) {
            // If the same person in the person's list can be found in the fruits list
            // draw a line between the Person and the Fruit
            if (persons.get(i).getPerson().equals((fruits.get(j).getPerson()))) {
                int personX = persons.get(i).getCoorX();
                int personY = persons.get(i).getCoorY();
                int fruitX = fruits.get(j).getCoorX();
                int fruitY = fruits.get(j).getCoorY();
                line = new LineTest(persons.get(i).getPerson(), fruits.get(j).getFruit(), personX, personY, fruitX, fruitY);
                addLine(line);
            }
        }
    } 

    this.setFont(setFont);    
    this.addMouseListener(new MouseAdapter() {    
        @Override    
        public void mousePressed(MouseEvent e) {    
            mousePt = e.getPoint();  
            for (Person p:persons) {  
                p.select(mousePt.x, mousePt.y);
            }  

            for (Fruit f:fruits) {
                f.select(mousePt.x, mousePt.y);
            }
        }  

        public void mouseReleased(MouseEvent e) {  
            for (Person p:persons) {
                p.unselect();
            }

            for(Fruit f:fruits) {
                f.unselect();
            }
        }  
    });    
    this.addMouseMotionListener(new MouseMotionAdapter() {    

        @Override    
        public void mouseDragged(MouseEvent e) {    
            mousePt = e.getPoint();  
            for (Person s:persons) {
                s.move(mousePt.x, mousePt.y);
                int personX = mousePt.x;
                int personY = mousePt.y;
                for(int k=0; k<lines.size(); k++) {
                    // If the same person in the person's list can be found in the fruits list
                    // move the point on the Person to a new coords
                    if(s.person.equals(lines.get(k).person)) {
                        lines.get(k).move(personX, personY);
                    }
                }
            }

            for(Fruit f:fruits) {
                f.move(mousePt.x, mousePt.y);
                int fruitX = mousePt.x;
                int fruitY = mousePt.y;
                for(int k=0; k<lines.size(); k++) {
                    if(f.person.equals(lines.get(k).person)) {
                        lines.get(k).move(fruitX, fruitY);
                    }
                }
            }
            repaint();    
        }    
    }); 
}

public void addPerson(Person person) {    
    persons.add(person);  
    repaint();    
} 

public void addFruit (Fruit fruit) {    
    fruits.add(fruit);    
    repaint();    
} 

public void addLine(LineTest line) {
    lines.add(line);
    repaint();
}

@Override    
public void paintComponent(Graphics g) {    
    super.paintComponent(g);  
    Graphics2D g2 = (Graphics2D)g.create();

    for (Person p:persons) {    
        p.paint(g2); 
    }   
    for (LineTest l:lines) {
        l.paint(g2);
    }
    for (Fruit f:fruits) {    
        f.paint(g2);
    } 
}    

@Override    
public Dimension getPreferredSize() {    
    return new Dimension(W, H);    
}    

public static void main(String[] args) {    
    EventQueue.invokeLater(new Runnable() {    
        @Override    
        public void run() {    
            JFrame f = new JFrame();    
            f.add(new Example());    
            f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);    
            f.pack();    
            f.setLocationRelativeTo(null);    
            f.setVisible(true);     
        }    
    });    
}    
}

人员类

import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.RenderingHints;

public class Person extends Rectangle {    
String person;
int x,y;  
int tx, ty;  
boolean isSelected = false;

public Person(String person, int x, int y) {  
    this.person = person;
    this.x = x;  
    this.y = y; 
    this.setBounds(x-10,y-10,40,90);

    isSelected = true;  
    move(x, y);  
    isSelected = false;  
}  

public void select(int x, int y){
    if(this.contains(x,y)) {
        isSelected=true; 
    } 
}  

public void unselect(){  
    isSelected = false;  
}

public void move(int x, int y) {  
    if(isSelected) {  
        LineTest.isPersonMoved = true;
        LineTest.isFruitMoved = false;
        tx = x;  
        ty= y;  
        this.translate(tx-this.x, ty-this.y); 
        this.x = tx;  
        this.y = ty;  
    }  
}  

public void paint(Graphics g) {  
    Graphics2D g2 = (Graphics2D)g.create();
    g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
    g2.drawOval(x, y, 20, 20); // head
    g2.drawLine(x+10,y+20,x+10,y+50); // body
    g2.drawLine(x+10,y+20,x+25,y+40); // right hand
    g2.drawLine(x+10,y+20,x-5,y+40); // left hand
    g2.drawLine(x+10,y+50,x-5,y+70); // left leg
    g2.drawLine(x+10,y+50,x+25,y+70); // right leg
    g2.drawString(person, tx-15, ty+85);
}  

public String getPerson() {
    return person;
}

public int getCoorX() {
    return x;
}

public int getCoorY() {
    return y;
}
} 

水果类

import java.awt.Color;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.RenderingHints;

public class Fruit extends Rectangle {

private static final long serialVersionUID = 1L;
String fruit, person;
Font _font;
int x, y, tx, ty;
public static int height, width, ovalWidth, ovalHeight;
boolean isSelected;
public static FontMetrics getMetrics;
public static Graphics2D g2;

public Fruit(String person, String fruit, Font font, int x, int y) {
    this.person = person;
    this.fruit = fruit;
    this._font = font;
    this.x = x;
    this.y = y;
    this.setBounds(x, y, ovalWidth, ovalHeight);

    isSelected = true;  
    move(x, y);  
    isSelected = false; 

}

public void select(int x, int y){
    if(this.contains(x,y)) {
        isSelected=true; 
    } 
} 

public void unselect(){  
    isSelected = false;  
}

public void move(int x, int y) {  
    if(isSelected) {  
        LineTest.isPersonMoved = false;
        LineTest.isFruitMoved = true;

        tx = x;  
        ty= y;  
        this.translate(tx-this.x, ty-this.y);
        this.x = tx;  
        this.y = ty;  
    }  
} 

public void paint(Graphics g) {
    g2 = (Graphics2D) g.create();
    g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
    getMetrics = g2.getFontMetrics(_font);
    height = getMetrics.getHeight();
    width = getMetrics.stringWidth(fruit);

    ovalWidth = width+25;
    ovalHeight = height+25;
    g2.setColor(Color.WHITE);
    g2.fillOval(x, y, ovalWidth, ovalHeight);
    g2.setColor(Color.BLACK);
    g2.drawOval(x, y, ovalWidth, ovalHeight);

    int centreX = x + ovalWidth/2;
    int centreY = y + ovalHeight/2;
    g2.drawString(fruit, (int) (centreX - width/2), (int) (centreY + height/4));

    this.setBounds(x, y, ovalWidth, ovalHeight);
}

public String getPerson() {
    return person;
}

public String getFruit() {
    return fruit;
}

public int getCoorX() {
    return x;
}

public int getCoorY() {
    return y;
}

}

LineTest类

import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RenderingHints;

/*
 * Draw line that connect between Person and Fruit
 */
public class LineTest {
int x1, y1, x2, y2, tx, ty;
String fruit, person;
public static boolean isPersonMoved, isFruitMoved;

public LineTest(String person, String fruit, int x1, int y1, int x2, int y2) {
    this.person = person;
    this.fruit = fruit;

    // Get x, y coordinates from person bound
    this.x1 = x1+35;
    this.y1 = y1+35;

    // Get x, y coordinates from fruit bound
    this.x2 = x2+30;
    this.y2 = y2+30;  
}

public void move(int x, int y) {
    if (isPersonMoved) {
        System.out.println("LineTest - isPersonMoved: " + isPersonMoved);
        tx = x;
        ty = y;
        this.x1 = tx+35;
        this.y1 = ty+35;
    } else if (isFruitMoved) {
        System.out.println("LineTest - isFruitMoved: " + isFruitMoved);
        tx = x;
        ty = y;
        this.x2 = tx+30;
        this.y2 = ty+30;
    }
}

public void paint(Graphics g) {
    Graphics2D g2d = (Graphics2D)g.create();
    g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
    g2d.drawLine(x1, y1, x2, y2);
}

public String getPerson() {
    return person;
}

public String getFruit() {
    return fruit;
}

public Point getFstCoor() {
    return new Point (x1, y1);
}

public Point getSndCoor() {
    return new Point(x2, y2);
}
}

2 个答案:

答案 0 :(得分:3)

基本思想是,您需要在要链接的对象之间生成某种关系。您可以隐式(Person包含Fruit)或非隐式的关系,在外部存储/管理关系,您所做的由您自己决定(我喜欢隐含的方法,因为它列出了你的意图)

你的代码有点奇怪,对不起,但确实如此,所以我做了一些修改。我确实考虑过使用Path2D很多,但这意味着所有代码都涂成了相同的颜色。重点是在对象之间定义某种共性,严格来说并不是必需的,因为它们之间的几乎所有工作都是相同的,为什么不呢......

public interface Paintable {
    public void paint(JComponent parent, Graphics2D g2d);
    public boolean contains(Point p);
    public void moveTo(Point2D p);
    public Rectangle2D getBounds();
}

然后我创建了一个类来管理关系......

public class Relationship {

    private Paintable parent;
    private Paintable child;

    public Relationship(Paintable parent, Paintable child) {
        this.parent = parent;
        this.child = child;
    }

    public Paintable getChild() {
        return child;
    }

    public Paintable getParent() {
        return parent;
    }

}

现在,这意味着你的水果可能属于一个人(或其他水果),所以如果这违反你的规则,你需要设计一个不同的关系算法(可能更隐含的东西)

现在,当您更新UI时,只需绘制关系和对象......

@Override
public void paintComponent(Graphics g) {
    super.paintComponent(g);
    Graphics2D g2 = (Graphics2D) g.create();

    for (Relationship relationship : relationships) {

        Point2D p1 = new Point2D.Double(relationship.getParent().getBounds().getCenterX(), relationship.getParent().getBounds().getCenterY());
        Point2D p2 = new Point2D.Double(relationship.getChild().getBounds().getCenterX(), relationship.getChild().getBounds().getCenterY());

        g2.draw(new Line2D.Double(p1, p2));

    }

    for (Person p : persons) {
        p.paint(this, g2);
    }
    for (Fruit f : fruits) {
        f.paint(this, g2);
    }

    g2.dispose();
}

Move me

import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;

public class Example extends JPanel {

    private List<Person> persons;
    private List<Fruit> fruits;
    private Point2D offset;
    private static Font baseFont;
    private Random randomGenerator;
    private Person person;
    private Fruit bubble;
    private static final int W = 640;
    private static final int H = 480;

    private Paintable selectedShape;

    private List<Relationship> relationships;

    public Example() {
        persons = new ArrayList<>();  // Stores the person's names & coords
        fruits = new ArrayList<>(); // Stores the person's name and what fruits he sells & coords
        relationships = new ArrayList<>(25);

        randomGenerator = new Random();
        baseFont = new Font("Sans Serif", Font.BOLD, 12);

        String person1 = "Jimmy";
        String person2 = "Sally";


        String fruit1 = "Banana";
        String fruit2 = "Apple";
        String fruit3 = "Orange";
        String fruit4 = "Watermelon";
        String fruit5 = "Pineapple";
        String fruit6 = "Grapes";

        Person person = new Person(person1, 50, 50);
        addPerson(person);

        Fruit bubble = new Fruit(fruit1, baseFont, 150, 50);
        addFruit(bubble);
        relate(person, bubble);
        bubble = new Fruit(fruit2, baseFont, 150, 100);
        addFruit(bubble);
        relate(person, bubble);
        bubble = new Fruit(fruit3, baseFont, 150, 150);
        addFruit(bubble);
        relate(person, bubble);

        person = new Person(person2, 50, 150);
        addPerson(person);

        bubble = new Fruit(fruit4, baseFont, 150, 200);
        addFruit(bubble);
        relate(person, bubble);
        bubble = new Fruit(fruit5, baseFont, 150, 250);
        addFruit(bubble);
        relate(person, bubble);
        bubble = new Fruit(fruit6, baseFont, 150, 300);
        addFruit(bubble);
        relate(person, bubble);

        this.setFont(baseFont);
        this.addMouseListener(new MouseAdapter() {
            @Override
            public void mousePressed(MouseEvent e) {
                for (Paintable p : getShapes()) {
                    if (p.contains(e.getPoint())) {
                        // Selected
                        selectedShape = p;
                        offset = new Point2D.Double(e.getX() - p.getBounds().getX(), e.getY() - p.getBounds().getY());
                        break;
                    }
                }
            }

            public void mouseReleased(MouseEvent e) {
                selectedShape = null;
            }
        });
        this.addMouseMotionListener(new MouseMotionAdapter() {

            @Override
            public void mouseDragged(MouseEvent e) {
                if (selectedShape != null) {

                    Point2D p = new Point2D.Double(e.getX() - offset.getX(), e.getY() - offset.getX());

                    selectedShape.moveTo(p);
                }
                repaint();
            }
        });
    }

    protected List<Paintable> getShapes() {
        ArrayList<Paintable> shapes = new ArrayList<>(fruits);
        shapes.addAll(persons);
        return shapes;
    }

    public void addPerson(Person person) {
        persons.add(person);
        repaint();
    }

    public void addFruit(Fruit fruit) {
        fruits.add(fruit);
        repaint();
    }

    @Override
    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        Graphics2D g2 = (Graphics2D) g.create();

        for (Relationship relationship : relationships) {

            Point2D p1 = new Point2D.Double(relationship.getParent().getBounds().getCenterX(), relationship.getParent().getBounds().getCenterY());
            Point2D p2 = new Point2D.Double(relationship.getChild().getBounds().getCenterX(), relationship.getChild().getBounds().getCenterY());

            g2.draw(new Line2D.Double(p1, p2));

        }

        for (Person p : persons) {
            p.paint(this, g2);
        }
        for (Fruit f : fruits) {
            f.paint(this, g2);
        }

        g2.dispose();
    }

    @Override
    public Dimension getPreferredSize() {
        return new Dimension(W, H);
    }

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                JFrame f = new JFrame();
                f.add(new Example());
                f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                f.pack();
                f.setLocationRelativeTo(null);
                f.setVisible(true);
            }
        });
    }

    protected void relate(Person person, Fruit bubble) {
        relationships.add(new Relationship(person, bubble));
    }

    public class Relationship {

        private Paintable parent;
        private Paintable child;

        public Relationship(Paintable parent, Paintable child) {
            this.parent = parent;
            this.child = child;
        }

        public Paintable getChild() {
            return child;
        }

        public Paintable getParent() {
            return parent;
        }

    }

    public interface Paintable {

        public void paint(JComponent parent, Graphics2D g2d);

        public boolean contains(Point p);

        public void moveTo(Point2D p);

        public Rectangle2D getBounds();

    }

    public class Fruit implements Paintable {

        private static final long serialVersionUID = 1L;
        String fruit;
        Font font;

        private Ellipse2D bounds;

        public Fruit(String fruit, Font font, int x, int y) {
            this.fruit = fruit;
            this.font = font;
            bounds = new Ellipse2D.Double(x, y, 40, 90);
        }

        public String getFruit() {
            return fruit;
        }

        @Override
        public boolean contains(Point p) {
            return bounds.contains(p);
        }

        @Override
        public void moveTo(Point2D p) {
            bounds = new Ellipse2D.Double(p.getX(), p.getY(), 40, 90);
        }

        @Override
        public void paint(JComponent parent, Graphics2D g) {
            Graphics2D g2 = (Graphics2D) g.create();
            g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
            g2.setFont(font);
            FontMetrics fm = g2.getFontMetrics();
            int height = fm.getHeight();
            int width = fm.stringWidth(fruit);

            g2.setColor(Color.WHITE);
            g2.fill(bounds);
            g2.setColor(Color.BLACK);
            g2.draw(bounds);

            double centreX = bounds.getX() + bounds.getWidth() / 2d;
            double centreY = bounds.getY() + bounds.getHeight() / 2d;
            g2.drawString(fruit, (int) (centreX - width / 2), (int) (centreY + height / 4));

            g2.dispose();
        }

        @Override
        public Rectangle2D getBounds() {
            return bounds.getBounds2D();
        }

    }

    public class Person implements Paintable {

        String person;
        private Rectangle2D bounds;

        public Person(String person, int x, int y) {
            this.person = person;
            bounds = new Rectangle2D.Double(x, y, 40, 90);
        }

        @Override
        public void paint(JComponent parent, Graphics2D g) {
            Graphics2D g2 = (Graphics2D) g.create();
            g2.translate(bounds.getX(), bounds.getY());
            g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
            g2.drawOval(0, 0, 20, 20); // head
            g2.drawLine(10, 20, 10, 50); // bodbounds.getY()
            g2.drawLine(10, 20, 25, 40); // right hand
            g2.drawLine(10, 20, 0 - 5, 40); // left hand
            g2.drawLine(10, 50, 0 - 5, 70); // left leg
            g2.drawLine(10, 50, 25, 70); // right leg
            g2.drawString(person, 0 - 15, 85);
            g2.dispose();
        }

        public String getPerson() {
            return person;
        }

        @Override
        public boolean contains(Point p) {
            return bounds.contains(p);
        }

        @Override
        public void moveTo(Point2D p) {
            bounds = new Rectangle2D.Double(p.getX(), p.getY(), 40, 90);
        }

        @Override
        public Rectangle2D getBounds() {
            return bounds.getBounds2D();
        }
    }

}

我鼓励你看一下Path2D,至少对于简笔画来说,它会让生活变得更好

答案 1 :(得分:0)

保持棍子男人的位置以及果实在其物体内的位置。关联在一起的每个对象。当repaint()开始时,获取棒人的位置信息和水果的位置信息,并在这两点之间画一条线。如果移动完成,那么也应该重新划线。