我有一个JPopupMenu
,我想根据它的inner components' size
动态更改它的内部大小。在我的SSCCE中,我描述了这个问题。
SSCCE:
public class PopupTest2 {
public static void main(String[] a) {
final JFrame frame = new JFrame();
frame.setSize(500, 500);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
final JPanel panel = new JPanel(new BorderLayout());
panel.setBorder(BorderFactory.createLineBorder(Color.RED));
final JPopupMenu menu = new JPopupMenu();
// critical step
JPanel itemPanel = new JPanel();
itemPanel.setLayout(new BoxLayout(itemPanel, BoxLayout.Y_AXIS));
final JMenuItem[] items = new JMenuItem[10];
for (int i = 0; i < 10; i++) {
JMenuItem item = new JMenuItem("Item #"+String.valueOf(i));
itemPanel.add(item);
items[i] = item;
}
menu.add(itemPanel);
JToggleButton button = new JToggleButton("Press me");
button.addActionListener(new ActionListener() {
boolean pressed = false;
@Override
public void actionPerformed(ActionEvent e) {
pressed = !pressed;
if (pressed) {
for (JMenuItem item : items) {
item.setText(item.getText()+" changed");
}
} else {
for (JMenuItem item : items) {
item.setText(item.getText().substring(0, item.getText().length() - 8));
}
}
}
});
panel.add(button, BorderLayout.NORTH);
panel.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
if (e.getButton() == MouseEvent.BUTTON3) {
menu.show(panel, (int) (e.getX() - menu.getPreferredSize().getWidth()), e.getY());
}
}
});
frame.setContentPane(panel);
frame.setUndecorated(true);
frame.setBackground(new Color(50, 50, 50, 200));
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
frame.setVisible(true);
}
});
}
}
步骤 2 重现:
- 右键单击以显示弹出菜单。
- 单击
Press me
按钮(位于窗口顶部)。 - 右键单击以再次显示弹出菜单(错误#1 - 弹出窗口仍然很小)
- 右键单击以再次显示弹出菜单(弹出菜单大小正常)
- 再次单击
Press me
按钮。 - 右键单击以显示弹出菜单。(错误#2 - 弹出窗口仍然很大)
如何在显示前强制JPopupMenu
更改其大小?如果我直接将项目添加到popupMenu
,为什么它会起作用?
这是执行此操作的一种方法:
public static void main(String[] a) {
final JFrame frame = new JFrame();
frame.setSize(500, 500);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
final JPanel panel = new JPanel(new BorderLayout());
panel.setBorder(BorderFactory.createLineBorder(Color.RED));
final JPopupMenu menu = new JPopupMenu();
// critical step
final JPanel itemPanel = new JPanel();
itemPanel.setLayout(new BoxLayout(itemPanel, BoxLayout.Y_AXIS));
final JMenuItem[] items = new JMenuItem[10];
for (int i = 0; i < 10; i++) {
JMenuItem item = new JMenuItem("Item #"+String.valueOf(i));
itemPanel.add(item);
items[i] = item;
}
menu.add(itemPanel);
JToggleButton button = new JToggleButton("Press me");
button.addActionListener(new ActionListener() {
boolean pressed = false;
@Override
public void actionPerformed(ActionEvent e) {
pressed = !pressed;
if (pressed) {
for (JMenuItem item : items) {
item.setText(item.getText()+" changed");
item.setMaximumSize(new Dimension(70, 50));
item.setPreferredSize(new Dimension(70, 50));
item.setMinimumSize(new Dimension(70, 50));
itemPanel.invalidate();
}
} else {
for (JMenuItem item : items) {
item.setText(item.getText().substring(0, item.getText().length() - 8));
item.setMaximumSize(new Dimension(130, 50));
item.setPreferredSize(new Dimension(130, 50));
item.setMinimumSize(new Dimension(130, 50));
itemPanel.invalidate();
}
}
}
});
panel.add(button, BorderLayout.NORTH);
panel.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
if (e.getButton() == MouseEvent.BUTTON3) {
menu.show(panel, (int) (e.getX() - menu.getPreferredSize().getWidth()), e.getY());
}
}
});
frame.setContentPane(panel);
frame.setUndecorated(true);
frame.setBackground(new Color(50, 50, 50, 200));
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
frame.setVisible(true);
}
});
}
谢谢大家,但是经过连续调试,我已经找到了解决方案。问题出在JPanel
中使用的layout
(弹出窗口和项目之间)。它叫JPanel
的getPreferredSize()
,直接叫menuItem.getPrefferedSize()
,叫BasicMenuItemUI.getPrefferedSize()
。最后一种方法使用 MainMenuLayoutHelper
类来获取首选大小。此类将有关大小的数据存储在静态对象Properties
。
默认的JPopupMenu
布局 - DefaultMenuLayout
- 每次调用其preferredLayoutSize()
方法时都会清除此静态数据,调用MenuItemLayoutHelper.clearUsedClientProperties(popupMenu)
。我们将做同样的事情 - 使用我们的 JPanel
参数调用此方法,并进一步调用revalidate()
:
public class PopupTest2 {
public static void main(String[] a) {
final JFrame frame = new JFrame();
frame.setSize(500, 500);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
final JPanel panel = new JPanel(new BorderLayout());
final JPopupMenu menu = new JPopupMenu();
final JPanel itemPanel = new JPanel();
itemPanel.setLayout(new BoxLayout(itemPanel, BoxLayout.Y_AXIS));
final JMenuItem[] items = new JMenuItem[1];
for (int i = 0; i < 1; i++) {
JMenuItem item = new JMenuItem("Item #"+String.valueOf(i));
itemPanel.add(item);
items[i] = item;
}
menu.updateUI();
menu.add(itemPanel);
JToggleButton button = new JToggleButton("Press me");
button.addActionListener(new ActionListener() {
boolean pressed = false;
@Override
public void actionPerformed(ActionEvent e) {
pressed = !pressed;
if (pressed) {
for (JMenuItem item : items) {
item.setText(item.getText()+" changed");
}
} else {
for (JMenuItem item : items) {
item.setText(item.getText().substring(0, item.getText().length() - 8));
}
}
}
});
panel.add(button, BorderLayout.NORTH);
panel.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
if (e.getButton() == MouseEvent.BUTTON3) {
MenuItemLayoutHelper.clearUsedClientProperties(itemPanel);
itemPanel.revalidate();
menu.show(panel, (int) (e.getX() - menu.getPreferredSize().getWidth()), e.getY());
}
}
});
frame.setContentPane(panel);
frame.setUndecorated(true);
frame.setBackground(new Color(50, 50, 50, 200));
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
frame.setVisible(true);
}
});
}
}
PS:亲爱的读者,这不是编写代码的最佳方式。我这样做是因为我真的需要它(我有这样的要求)。如果你可以在没有JPanel
的情况下构建你的JPopupMenu
,请不要在这个答案中使用代码,请。
你必须添加
menu.updateUI();
添加菜单项后。
final JMenuItem[] items = new JMenuItem[10];
for (int i = 0; i < 10; i++) {
JMenuItem item = new JMenuItem("Item #"+String.valueOf(i));
itemPanel.add(item);
item.setUI(new MyUI());
items[i] = item;
}
menu.updateUI(); <<<<<<--------
menu.add(itemPanel);