Java 在组件中心之间绘制一条线

标签 java swing user-interface paint

当用户单击一个标签、拖动并释放另一个标签时,我试图在两个 JLabel 的中心之间绘制一条线。无论窗口大小如何,它都应该有效。

但是线条不居中,我该如何修复它?

以下示例有效,但线条似乎被 JFrame 的边界偏移,因此它们不在中心。

我不想尝试从点计算中删除 JFrame 边框,因为真实的界面比给出的示例更复杂,并且 JFrame 中包含更多组件。

我认为点计算将与我正在使用的 JPanel 相关,因此我不会遇到 JFrame 边界问题,但情况似乎并非如此。

预先感谢您的帮助。

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


}

最佳答案

所以,基本问题是,组件的位置是相对于 panel 的,它被框架的装饰所偏移,但您正在使用框架现有的Graphics上下文来绘制线条,因此线条未对齐。

除了不使用 getGraphics 之外,您还可以通过使用框架的 glassPane 来实现预期结果,例如

enter image description here

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

    }

}

现在,只有当内容覆盖 contentPane 的整个可见区域时,这才有效,虽然您可能会乱七八糟地将位置信息从一个组件上下文转换为另一个组件上下文,但更简单的解决方案是使用JXLayer

在这种情况下我会避免覆盖 paint 的原因是 Swing 组件可以在不需要绘制父组件的情况下进行更新,这可能会清除父组件上次绘制的内容被画了...

看看How to Use Root Panes了解更多详情

关于Java 在组件中心之间绘制一条线,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28467411/

相关文章:

java - 识别一个字符串中的数字

java - 如何防止我的 JPanel 重新调整

eclipse - 如何在 Eclipse 中将默认搜索模式更改为 "File Search"?

java - 当 javax.xml.ws.soap.SOAPFaultException 发生时?

java - Swing:如何创建不使用布局的可滚动 JPanel 列表

java - gui 和 main() 方法

java - 从 JButton 打开 excel 工作表 - Java Swing

multithreading - 如何知道代码是在 ui 线程还是工作线程上运行 (MFC Visual C++)

python - 在 python 中从 CSV 获取特定时间间隔的数据

java - Eclipse: "println(__LINE__);"——是否存在这样的预处理器宏来以编程方式获取代码的行号?