我在以绝对布局将组件动态添加到JPanel时遇到问题(实际上不是添加,而是保存以前添加的组件)。我可以在框架的jpanel中插入一个自定义圆圈按钮,但是在显示之前添加的对象时遇到问题。
[差不多20年前,我编写了一个应用程序来测试最短路径和类似算法。看起来是这样的:我在面板上添加了标记节点,然后使用标记线将它们连接起来。然后,我计算了路径。我已经好几年没有使用Swing了。现在,我想在Swing中重复相同的任务。当然,我没有旧应用程序的副本。一开始我遇到了一个问题:将自定义组件动态添加到面板中。面板具有绝对布局。用户必须从菜单中选择“新节点”选项。然后出现一个对话框–应用程序要求输入新节点的名称。命名后,单击鼠标将节点添加到一个位置。但是我有一个问题,因为我无法显示之前添加的按钮-仅显示新的按钮。我试图将旧按钮及其坐标作为元组存储在列表中–我做错了。
这似乎很容易:Core Java书籍(MouseTest第10章第1卷)中显示了一个类似的应用程序,唯一的区别是作者绘制了一个包含鼠标单击创建的几何形状的组件,但我想绘制自己的自定义按钮(恰好有两种组件:自定义按钮和代表带有连接该按钮的距离标签的线的组件)。每个对象必须是一个单独的组件。请帮助我解决我的问题。
public class RoutingGame extends JFrame{
private List<ButtonTuple> buttonTuples;
private JMenuBar menuBar;
private JMenu fileMenu;
private JMenu opsMenu;
public RoutingGame() {
super();
buttonTuples = new ArrayList<>();
setBounds(100, 100, 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");
opsMenu = new JMenu("Options");
JMenuItem newItem = new JMenuItem("New file");
JMenuItem openItem = new JMenuItem("Open file");
JMenuItem nodeItem = new JMenuItem("New node");
JMenuItem closeItem = new JMenuItem("Close file");
JMenuItem exitItem = new JMenuItem("Exit");
JMenuItem dspItem = new JMenuItem("Display options");
menuBar.add(fileMenu);
menuBar.add(opsMenu);
fileMenu.add(newItem);
fileMenu.add(openItem);
fileMenu.add(nodeItem);
fileMenu.add(closeItem);
fileMenu.add(exitItem);
opsMenu.add(dspItem);
setJMenuBar(menuBar);
exitItem.addActionListener(e -> System.exit(0));
nodeItem.addActionListener(e -> {
String s = (String)JOptionPane.showInputDialog(
this,
"Choose a name for your new node:",
"Add node",
JOptionPane.PLAIN_MESSAGE,
null,
null,
"Node");
RoundButton roundButton = new RoundButton(s);
Insets insets = cpanel.getInsets();
Dimension size = roundButton.getPreferredSize();
final int[] mouseCoord = {-10,-10};
setCursor(Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR));
cpanel.addMouseListener(new MouseAdapter() {
public void mouseClicked(MouseEvent e) {
mouseCoord[0] = e.getX();
mouseCoord[1] = e.getY();
for(var tuple : buttonTuples){// this loop doesn't work
var button = tuple.getRoundButton();
var xpos = tuple.getXPos();
var ypos = tuple.getYPos();
button.setBounds( xpos + insets.left - size.width / 2, ypos + insets.top - size.height / 2, size.width , size.height );
}
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]));
setCursor(Cursor.getDefaultCursor());
cpanel.add( roundButton );
cpanel.revalidate();
cpanel.repaint();
}
});
});
}
}
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 ButtonTuple {
private RoundButton roundButton;
private int xPos;
private int yPos;
// + getters, setters and constructor
}
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);
上面的代码将验证:
我猜位置不对,按钮被一个接一个地放置。
我不理解此逻辑:
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(…);
我在我的代码中发现了一个错误:内循环中的setBounds应该看起来像那样,但是不能再继续工作了:
button.setBounds( xpos + insets.left - size.width / 2, ypos + insets.top - size.height / 2, size.width , size.height );