java swing 动态添加自定义按钮

java swing dynamically adding custom button

我在使用绝对布局向 JPanel 动态添加组件时遇到问题(实际上不是添加而是保存以前添加的组件)。 我可以将一个自定义圆形按钮插入到框架上的 jpanel 中,但我在显示之前添加的对象时遇到问题。

大约 20 年前,我编写了一个应用程序来测试最短路径和类似算法。看起来像这样:我将标记节点添加到面板,然后使用标记线连接它们。然后我计算路径。 我已经好几年没用过 Swing 了。现在我想在 Swing 中重复同样的任务。当然,我没有旧应用程序的副本。 一开始我遇到了一个问题:将自定义组件动态添加到面板。该面板具有绝对布局。用户必须从菜单中选择“新节点”选项。 然后出现一个对话框——应用程序要求输入新节点的名称。放置名称后,单击鼠标时节点将添加到某个位置。但是我有一个问题,因为我无法显示之前添加的按钮——只显示新按钮。我试图将旧按钮连同它们的坐标作为元组存储在列表中——我做错了什么。

这看起来很简单:Core Java 书(MouseTest 第 10 章第 1 卷)中显示了类似的应用程序,唯一的区别是作者绘制了一个组件,其中包含通过鼠标单击创建的几何形状,但我我想绘制我的自定义按钮(准确地说是两种组件:自定义按钮和代表带有连接按钮的距离标签的线的组件)。每个对象都必须是一个单独的组件。 请帮助我解决问题。

public class RoutingGame extends JFrame {
private Set<String> labelNames;
private JMenuBar menuBar;
private JMenu fileMenu;

public RoutingGame() {
    super();
    labelNames = new HashSet<>();
    setBounds(300, 200, 800, 600);
    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

    JPanel npanel = new JPanel();
    getContentPane().add(npanel, BorderLayout.NORTH);
    npanel.setLayout(new FlowLayout(FlowLayout.LEADING));
    JPanel cpanel = new JPanel();
    getContentPane().add(cpanel, BorderLayout.CENTER);
    cpanel.setLayout(null);

    menuBar = new JMenuBar();
    fileMenu = new JMenu("File");
    JMenuItem nodeItem = new JMenuItem("New node");
    menuBar.add(fileMenu);
    fileMenu.add(nodeItem);
    setJMenuBar(menuBar);
    nodeItem.addActionListener(e -> {
        String s = "";
        while (s.isBlank()) {
            s = (String) JOptionPane.showInputDialog(
                    this,
                    "Choose a name for your new node:",
                    "Add node",
                    JOptionPane.PLAIN_MESSAGE,
                    null,
                    null,
                    "Node");
            }
        final String[] nodeName = {s};
        setCursor(Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR));
        cpanel.addMouseListener(new MouseAdapter() {
            public void mouseClicked(MouseEvent e) {
                RoundButton roundButton = new RoundButton(nodeName[0]);
                Insets insets = cpanel.getInsets();
                Dimension size = roundButton.getPreferredSize();

                roundButton.setBounds ( insets.left + e.getX()  - size.width / 2, insets.top + e.getY() - size.height / 2, size.width  , size.height );                    
                setCursor(Cursor.getDefaultCursor());
                cpanel.add( roundButton );
                cpanel.revalidate();
                cpanel.repaint();
                cpanel.removeMouseListener(this);
                }
            });
        });
    }
}
 public class RoundButton extends JButton {
    private static final int DEFAULT_WIDTH = 66;
    private static final int DEFAULT_HEIGHT = 66;
    private Shape shape;
    public RoundButton(String label) {
        super(label);
        setContentAreaFilled(false);
}
@Override
public Dimension getPreferredSize() {
    return new Dimension(DEFAULT_WIDTH, DEFAULT_HEIGHT);
}

protected void paintComponent(Graphics g) {
    int diameter = Math.min(getWidth(), getHeight());
    int radius = diameter / 2;

    if (! getModel().isArmed()) {
        g.setColor(WHITE);
    } else {
        g.setColor( new Color(230, 230, 230) );
    }
    g.fillOval(getWidth() / 2 - radius, getHeight() / 2 - radius, diameter, diameter);
    g.drawOval(getWidth() / 2 - radius, getHeight() / 2 - radius, diameter, diameter);
    super.paintComponent(g);
}

protected void paintBorder(Graphics g) {
    int diameter = Math.min(getWidth(), getHeight());
    int radius = diameter / 2;
    g.setColor(getForeground() );
    g.drawOval(getWidth() / 2 - radius, getHeight() / 2 - radius, diameter - 1, diameter - 1);
    }
}


public class RoutingGameRunner {
    public static void main(String[] args) {
        EventQueue.invokeLater( () -> {
            try {
                RoutingGame window = new RoutingGame();
                window.setResizable(false);
                window.setVisible(true);
            } catch (Exception e) {
                 e.printStackTrace();
            }
        });
    }
}

But I have a problem because I can’t display buttons added earlier

为什么要再次显示按钮?如果您将它们添加到面板,它们不会消失,除非您有删除它们的逻辑。

总之,你有调试过吗?

button.setBounds( xpos + mouseCoord[0]  - size.width / 2, ypos +  
    mouseCoord[1] - size.height / 2, size.width  , size.height );

上面的代码应该重组为:

int x = xpos + e.getX() - (size.width / 2);
int y = ypos + e.getY() - (size.height / 2);
button.setBounds(x, y, size.width, size.height);

因为现在您实际上可以添加一些调试代码来确保您的计算是正确的:

System.out.println(x + " : " + y + " : " + size);

上面的代码将验证:

  1. 你实际上调用了你的循环代码
  2. 按钮的边界是正确的

我猜位置是错误的,按钮是一个叠放的。

我不明白这个逻辑:

roundButton.setBounds ( insets.left + mouseCoord[0]  - size.width / 2, insets.top + mouseCoord[1] - size.height / 2, size.width  , size.height );
buttonTuples.add( new ButtonTuple(roundButton, mouseCoord[0], mouseCoord[1]));

您在一次位置添加了组件,但随后在 ButtonTuple 中保存了一个不同的位置 class。

为什么还需要最后两个参数?按钮的边界已经设置。

编辑:

除了上述所有建议,最大的问题是您需要创建一个新的 RoundButton 实例:

roundButton = new RoundButton();
roundButton.setBounds(…);