【问题标题】:java swing dynamically adding custom buttonjava swing动态添加自定义按钮
【发布时间】:2020-07-05 12:46:16
【问题描述】:

我在使用绝对布局将组件动态添加到 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();
            }
        });
    }
}

【问题讨论】:

  • 在提问时发布正确的minimal reproducible example。您的问题是关于显示组件。因此,首先让您的代码使用标准的 JDK JButton。一旦成功,您就可以使用自定义 JButton 进行测试。限制一次测试的自定义代码。简化逻辑将更容易找到问题。

标签: java swing dynamic jbutton add


【解决方案1】:

但是我有一个问题,因为我无法显示之前添加的按钮

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

无论如何,你有没有做过调试?

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 类中保存不同的位置。

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

编辑:

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

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

【讨论】:

  • 现在一切都如我所愿。谢谢大家的耐心。我更新了我的代码。我的错误是在错误的位置创建按钮。当然,重新绘制按钮没有任何意义。再次感谢!
猜你喜欢
  • 1970-01-01
  • 2011-03-31
  • 2016-04-30
  • 1970-01-01
  • 2011-05-19
  • 2021-06-04
  • 2016-04-24
  • 2018-09-22
  • 2018-03-19
相关资源
最近更新 更多