在按钮摆动之间绘制可点击线

时间:2023-02-02 23:15:43

I have a gridbag of objects laid out in a specific pattern. Clicking on an object will trigger certain actions such as displaying information about that object or navigating to another screen.

我有一个以特定图案布置的物体网格。单击对象将触发某些操作,例如显示有关该对象的信息或导航到另一个屏幕。

Each object also has connections to other objects. These connections need to be clickable as clicking on them will display more information about that connection.

每个对象还具有与其他对象的连接。这些连接需要可点击,因为单击它们将显示有关该连接的更多信息。

The following example shows what I am aiming for. Each blue circle in the grid is a clickable object and each red line between them is a clickable connection.

以下示例显示了我的目标。网格中的每个蓝色圆圈都是可点击的对象,它们之间的每条红线都是可点击的连接。

在按钮摆动之间绘制可点击线

I have tried storing both the objects and connections as Jbuttons in a single grid bag layout and have had some success in that the connections are clickable.

我已经尝试将对象和连接作为Jbuttons存储在单个网格包布局中,并且已经取得了一些成功,因为连接是可点击的。

However the connections span multiple cells in a single direction and the Jbuttons expand to fill the layout cells. This causes the ends of the connections to pass through the center of the objects and finish at the border of the grid bag cell.

但是,连接跨越单个方向的多个单元格,Jbuttons展开以填充布局单元格。这导致连接的末端穿过物体的中心并在网格袋单元的边界处完成。

Here is a drawing of what I mean:

这是我的意思:

在按钮摆动之间绘制可点击线

Is there some simpler way to make this kind of interface / has anyone done this before?

是否有一些更简单的方法来制作这种界面/以前有人这样做过吗?

1 个解决方案

#1


Okay, very basic, but the idea is to paint the lines manually between the objects.

好的,非常基本,但想法是在对象之间手动绘制线条。

In this case, I created a simple Connection object which maintained the relationships between buttons. I then simply used the paintComponent method of the JPanel to paint the actual lines.

在这种情况下,我创建了一个简单的Connection对象,它维护了按钮之间的关系。然后我简单地使用JPanel的paintComponent方法来绘制实际的线条。

I'll leave you to devise a better path generation method ;)

我会让你设计一个更好的路径生成方法;)

在按钮摆动之间绘制可点击线

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.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.Rectangle;
import java.awt.geom.Line2D;
import java.awt.geom.Path2D;
import java.awt.geom.Point2D;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class Test {

    public static void main(String[] args) {
        new Test();
    }

    public Test() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    ex.printStackTrace();
                }

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class TestPane extends JPanel {

        private JButton[] buttons = new JButton[]{
            new JButton("1"),
            new JButton("2"),
            new JButton("3"),
            new JButton("4"),};

        private List<Connection> connections;

        public TestPane() {
            setLayout(new GridBagLayout());
            GridBagConstraints gbc = new GridBagConstraints();
            gbc.gridx = 0;
            gbc.gridy = 0;
            gbc.insets = new Insets(10, 10, 10, 10);
            add(buttons[0], gbc);

            gbc.gridx = 2;
            gbc.gridy = 0;
            add(buttons[1], gbc);

            gbc.gridx = 1;
            gbc.gridy = 1;
            add(buttons[2], gbc);

            gbc.gridx = 2;
            gbc.gridy = 2;
            add(buttons[3], gbc);

            connections = new ArrayList<Connection>(25);
            connections.add(new Connection(buttons[0], buttons[1]));
            connections.add(new Connection(buttons[0], buttons[2]));
            connections.add(new Connection(buttons[0], buttons[3]));

            connections.add(new Connection(buttons[1], buttons[3]));
            connections.add(new Connection(buttons[2], buttons[3]));
        }

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

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D) g.create();
            g2d.setColor(Color.RED);
            for (Connection connection : connections) {
                JButton source = connection.getSource();
                JButton dest = connection.getDestination();

                if (source.getX() == dest.getX()) {
                    // Same column...
                    g2d.drawLine(source.getX() + source.getWidth() / 2, source.getY(),
                                    dest.getX() + source.getWidth() / 2, dest.getY());
                } else if (source.getY() == dest.getY()) {
                    // Same row...
                    g2d.drawLine(source.getX(), source.getY() + source.getHeight() / 2,
                                    dest.getX(), dest.getY() + dest.getHeight() / 2);
                } else {

                    Path2D path = new Path2D.Double();
                    path.moveTo(horizontalCenter(source), verticalCenter(source));
                    path.curveTo(horizontalCenter(source), verticalCenter(dest), 
                                    horizontalCenter(source), verticalCenter(dest), 
                                    horizontalCenter(dest), verticalCenter(dest));
                    g2d.draw(path);

                }
            }
            g2d.dispose();
        }

        protected double horizontalCenter(JComponent bounds) {

            return bounds.getX() + bounds.getWidth() / 2d;

        }

        protected double verticalCenter(JComponent bounds) {

            return bounds.getY() + bounds.getHeight() / 2d;

        }

        protected boolean hasIntersection(Line2D line, JComponent... exclude) {
            List<JComponent> toExclude = Arrays.asList(exclude);
            boolean intersects = false;
            for (Component comp : getComponents()) {
                if (!toExclude.contains(comp)) {
                    if (line.intersects(comp.getBounds())) {
                        System.out.println(line.getP1() + "-" + line.getP2() + " intersets with " + ((JButton)comp).getText() + "; " + comp.getBounds());
                        intersects = true;
                        break;
                    }
                }
            }
            return intersects;
        }

        protected Line2D lineDownTo(JComponent from, JComponent to) {
            return new Line2D.Double(horizontalCenter(from), from.getY(), horizontalCenter(from), verticalCenter(to));      
        }

        protected Line2D lineAcrossTo(JComponent from, JComponent to) {
            return new Line2D.Double(from.getX(), verticalCenter(from), horizontalCenter(to), verticalCenter(from));        
        }

        protected Point2D centerOf(Rectangle bounds) {
            return new Point2D.Double(bounds.getX() + bounds.getWidth() / 2, bounds.getY() + bounds.getHeight() / 2);
        }

        protected boolean canGoDownTo(Point2D startPoint, Point2D endPoint, JComponent to, JComponent from) {
            Point2D targetPoint = new Point2D.Double(startPoint.getX(), endPoint.getY());
            return !hasIntersection(new Line2D.Double(startPoint, targetPoint), to, from);
        }

        public class Connection {

            private final JButton source;
            private final JButton destination;

            public Connection(JButton source, JButton destination) {
                this.source = source;
                this.destination = destination;
            }

            public JButton getSource() {
                return source;
            }

            public JButton getDestination() {
                return destination;
            }

        }

    }

}

#1


Okay, very basic, but the idea is to paint the lines manually between the objects.

好的,非常基本,但想法是在对象之间手动绘制线条。

In this case, I created a simple Connection object which maintained the relationships between buttons. I then simply used the paintComponent method of the JPanel to paint the actual lines.

在这种情况下,我创建了一个简单的Connection对象,它维护了按钮之间的关系。然后我简单地使用JPanel的paintComponent方法来绘制实际的线条。

I'll leave you to devise a better path generation method ;)

我会让你设计一个更好的路径生成方法;)

在按钮摆动之间绘制可点击线

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.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.Rectangle;
import java.awt.geom.Line2D;
import java.awt.geom.Path2D;
import java.awt.geom.Point2D;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class Test {

    public static void main(String[] args) {
        new Test();
    }

    public Test() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    ex.printStackTrace();
                }

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class TestPane extends JPanel {

        private JButton[] buttons = new JButton[]{
            new JButton("1"),
            new JButton("2"),
            new JButton("3"),
            new JButton("4"),};

        private List<Connection> connections;

        public TestPane() {
            setLayout(new GridBagLayout());
            GridBagConstraints gbc = new GridBagConstraints();
            gbc.gridx = 0;
            gbc.gridy = 0;
            gbc.insets = new Insets(10, 10, 10, 10);
            add(buttons[0], gbc);

            gbc.gridx = 2;
            gbc.gridy = 0;
            add(buttons[1], gbc);

            gbc.gridx = 1;
            gbc.gridy = 1;
            add(buttons[2], gbc);

            gbc.gridx = 2;
            gbc.gridy = 2;
            add(buttons[3], gbc);

            connections = new ArrayList<Connection>(25);
            connections.add(new Connection(buttons[0], buttons[1]));
            connections.add(new Connection(buttons[0], buttons[2]));
            connections.add(new Connection(buttons[0], buttons[3]));

            connections.add(new Connection(buttons[1], buttons[3]));
            connections.add(new Connection(buttons[2], buttons[3]));
        }

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

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D) g.create();
            g2d.setColor(Color.RED);
            for (Connection connection : connections) {
                JButton source = connection.getSource();
                JButton dest = connection.getDestination();

                if (source.getX() == dest.getX()) {
                    // Same column...
                    g2d.drawLine(source.getX() + source.getWidth() / 2, source.getY(),
                                    dest.getX() + source.getWidth() / 2, dest.getY());
                } else if (source.getY() == dest.getY()) {
                    // Same row...
                    g2d.drawLine(source.getX(), source.getY() + source.getHeight() / 2,
                                    dest.getX(), dest.getY() + dest.getHeight() / 2);
                } else {

                    Path2D path = new Path2D.Double();
                    path.moveTo(horizontalCenter(source), verticalCenter(source));
                    path.curveTo(horizontalCenter(source), verticalCenter(dest), 
                                    horizontalCenter(source), verticalCenter(dest), 
                                    horizontalCenter(dest), verticalCenter(dest));
                    g2d.draw(path);

                }
            }
            g2d.dispose();
        }

        protected double horizontalCenter(JComponent bounds) {

            return bounds.getX() + bounds.getWidth() / 2d;

        }

        protected double verticalCenter(JComponent bounds) {

            return bounds.getY() + bounds.getHeight() / 2d;

        }

        protected boolean hasIntersection(Line2D line, JComponent... exclude) {
            List<JComponent> toExclude = Arrays.asList(exclude);
            boolean intersects = false;
            for (Component comp : getComponents()) {
                if (!toExclude.contains(comp)) {
                    if (line.intersects(comp.getBounds())) {
                        System.out.println(line.getP1() + "-" + line.getP2() + " intersets with " + ((JButton)comp).getText() + "; " + comp.getBounds());
                        intersects = true;
                        break;
                    }
                }
            }
            return intersects;
        }

        protected Line2D lineDownTo(JComponent from, JComponent to) {
            return new Line2D.Double(horizontalCenter(from), from.getY(), horizontalCenter(from), verticalCenter(to));      
        }

        protected Line2D lineAcrossTo(JComponent from, JComponent to) {
            return new Line2D.Double(from.getX(), verticalCenter(from), horizontalCenter(to), verticalCenter(from));        
        }

        protected Point2D centerOf(Rectangle bounds) {
            return new Point2D.Double(bounds.getX() + bounds.getWidth() / 2, bounds.getY() + bounds.getHeight() / 2);
        }

        protected boolean canGoDownTo(Point2D startPoint, Point2D endPoint, JComponent to, JComponent from) {
            Point2D targetPoint = new Point2D.Double(startPoint.getX(), endPoint.getY());
            return !hasIntersection(new Line2D.Double(startPoint, targetPoint), to, from);
        }

        public class Connection {

            private final JButton source;
            private final JButton destination;

            public Connection(JButton source, JButton destination) {
                this.source = source;
                this.destination = destination;
            }

            public JButton getSource() {
                return source;
            }

            public JButton getDestination() {
                return destination;
            }

        }

    }

}