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);
上面的代码将验证:
- 你实际上调用了你的循环代码
- 按钮的边界是正确的
我猜位置是错误的,按钮是一个叠放的。
我不明白这个逻辑:
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(…);
我在使用绝对布局向 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);
上面的代码将验证:
- 你实际上调用了你的循环代码
- 按钮的边界是正确的
我猜位置是错误的,按钮是一个叠放的。
我不明白这个逻辑:
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(…);