简体   繁体   English

Java在组件中心之间画一条线

[英]Java drawing a line between the centre of components

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. 当用户单击一个标签,在另一个标签上方拖动并释放时,我试图在两个JLabel的中心之间绘制一条线。 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. 以下示例正在运行,但是这些行似乎被JFrame的边界所偏移,因此它们不在中心。

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. 我不想尝试从点计算中删除JFrame边框,因为实际接口比给定的示例更复杂,并且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. 我以为积分计算是相对于我正在使用的JPanel的,因此我不会遇到JFrame边界问题,但事实并非如此。

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);
    }


}

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. 因此,基本问题是,组件的位置相对于panel ,相对于panel ,它被框架的装饰物偏移了,但是您使用的是框架的现有Graphics上下文来绘制线条,因此线条未对齐。

Apart from NOT using getGraphics , EVER, you could achieve the expected results by using the frame's glassPane , for example 除了不使用getGraphics glassPane ,例如,使用框架的glassPane可以达到预期的结果

在此处输入图片说明

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 . 现在,这仅在内容覆盖contentPane的整个可见区域时才有效,尽管您可能会把位置信息从一个组件上下文转换为另一个组件上下文,但更简单的解决方案是使用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... 在这种情况下,我避免覆盖paint的原因是可以在不对父部件进行paint的情况下更新Swing组件,这可能会抹去上次油漆时父油漆的内容...

Take a look at How to Use Root Panes for more details 看看如何使用根窗格了解更多详细信息

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM