java swing动态添加自定义按钮



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

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

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

无论如何,你做过调试吗?

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

最新更新