Java drawing a line between the centre of components

烈酒焚心 提交于 2019-11-28 12:26:37

问题


I am trying to have a line drawn between the centre of two JLabels when the user clicks on one label, drags and releases on top of another. Which should work no matter what size the window is.

But the lines are not centre, how can I fix it?

The following example is working, but the lines seem to be offset by the boundaries of the JFrame, so they are not centre.

I do not want to try to remove the JFrame border from the point calculation, since the real interface is more complex than the example given, and has many more components included in the JFrame.

I thought the point calculation would be relative to the JPanel I am using so I would not run into the JFrame boundary issues, but this does not seem to be the case.

Thank you in advance for any help.

import java.awt.Color;
import java.awt.Component;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.Point;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;

import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;

public class test extends JFrame implements MouseListener {

    private static JPanel panel = new JPanel();
    private static test window = new test();

    public test() { 
        panel.setLayout(new GridLayout(2, 2));

        JLabel l1 = new JLabel();
        JLabel l2 = new JLabel();
        JLabel l3 = new JLabel();
        JLabel l4 = new JLabel();

        l1.setOpaque(true);
        l2.setOpaque(true);
        l3.setOpaque(true);
        l4.setOpaque(true);

        l1.setBackground(Color.RED);
        l2.setBackground(Color.BLUE);
        l3.setBackground(Color.GREEN);
        l4.setBackground(Color.ORANGE);

        l1.setName("l1");
        l2.setName("l2");
        l3.setName("l3");
        l4.setName("l4");

        panel.add(l1);
        panel.add(l2);
        panel.add(l3);
        panel.add(l4);

        panel.addMouseListener(this);

        this.add(panel);    
    }

    public static void drawArcs(int x1, int y1, int x2, int y2) {
        Graphics g = window.getGraphics();
        Graphics2D g2 = (Graphics2D) g;
        g2.drawLine(x1,  y1,  x2,  y2);
    }

    private static int x1 = 0;
    private static int y1 = 0;
    public void mousePressed(MouseEvent e) {
        Component square1 = panel.getComponentAt(new Point(e.getX(), e.getY()));
        System.out.println( square1.getName() );    
        x1 = square1.getX() + square1.getWidth() / 2;
        y1 = square1.getY() + square1.getHeight() / 2;
    }

    public void mouseReleased(MouseEvent e) {
        Component square2 = panel.getComponentAt(new Point(e.getX(), e.getY()));
        System.out.println( square2.getName() );    
        int x2 = square2.getX() + square2.getWidth() / 2;
        int y2 = square2.getY() + square2.getHeight() / 2;
        drawArcs(x1, y1, x2, y2);
    }

    @Override
    public void mouseClicked(MouseEvent arg0) {}
    @Override
    public void mouseEntered(MouseEvent arg0) {}
    @Override
    public void mouseExited(MouseEvent arg0) {}

    public static void main(String[] args) {
        window.setVisible(true);
        window.setSize(400, 400);
        window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }


}

回答1:


So, the basic problem is, the location of your components are relative to panel, which is offset by the frame's decorations, but you are using the frame's existing Graphics context to paint the line, so the lines are misaligned.

Apart from NOT using getGraphics, EVER, you could achieve the expected results by using the frame's glassPane, for example

import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.geom.Line2D;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class Test extends JFrame implements MouseListener {

    private JPanel panel = new JPanel();

    public Test() {
        ConnectTheDots dots = new ConnectTheDots();
        setGlassPane(dots);
        dots.setVisible(true);
        panel.setLayout(new GridLayout(2, 2));

        panel.add(createLabel(Color.RED));
        panel.add(createLabel(Color.BLUE));
        panel.add(createLabel(Color.GREEN));
        panel.add(createLabel(Color.ORANGE));

        panel.addMouseListener(this);

        this.add(panel);
    }

    private Component pressComponent;
    private Component releaseComponent;

    public void mousePressed(MouseEvent e) {
        pressComponent = panel.getComponentAt(new Point(e.getX(), e.getY()));
    }

    public void mouseReleased(MouseEvent e) {
        releaseComponent = panel.getComponentAt(new Point(e.getX(), e.getY()));
        joinTheDots();
    }

    @Override
    public void mouseClicked(MouseEvent arg0) {
    }

    @Override
    public void mouseEntered(MouseEvent arg0) {
    }

    @Override
    public void mouseExited(MouseEvent arg0) {
    }

    protected void joinTheDots() {

        Rectangle bounds = pressComponent.getBounds();
        Point startPoint = centerOf(bounds);
        bounds = releaseComponent.getBounds();
        Point endPoint = centerOf(bounds);

        ((ConnectTheDots) getGlassPane()).drawLine(startPoint, endPoint);

    }

    protected Point centerOf(Rectangle bounds) {

        return new Point(
                        bounds.x + (bounds.width / 2),
                        bounds.y + (bounds.height / 2));

    }

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    ex.printStackTrace();
                }

                Test frame = new Test();
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    protected JLabel createLabel(Color background) {
        JLabel label = new JLabel() {
            @Override
            public Dimension getPreferredSize() {
                return new Dimension(100, 100);
            }
        };
        label.setOpaque(true);
        label.setBackground(background);
        return label;
    }

    public class ConnectTheDots extends JPanel {

        private Point startPoint;
        private Point endPoint;

        public ConnectTheDots() {
            setOpaque(false);
        }

        public void drawLine(Point startPoint, Point endPoint) {
            this.startPoint = startPoint;
            this.endPoint = endPoint;
            repaint();
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            if (startPoint != null && endPoint != null) {
                Graphics2D g2d = (Graphics2D) g.create();
                Line2D line = new Line2D.Double(startPoint, endPoint);
                g2d.setColor(Color.BLACK);
                g2d.draw(line);
                g2d.dispose();
            }
        }

    }

}

Now, this will only work if the content covers the entire visible area of the contentPane, while you could mess around with converting the location information from one component context to another, a simpler solution would be to use JXLayer.

The reason I would avoid overriding paint in this case, is Swing components can be updated without requiring the parent component to be painted, this could wipe out what ever the parent component painted the last time it was painted...

Take a look at How to Use Root Panes for more details



来源:https://stackoverflow.com/questions/28467411/java-drawing-a-line-between-the-centre-of-components

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!