JPopupMenu动态更改大小,如果里面有一个JPanel



我有一个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 重现:

  1. 右键单击以显示弹出菜单。
  2. 单击Press me按钮(位于窗口顶部)。
  3. 右键单击以再次显示弹出菜单(错误#1 - 弹出窗口仍然很小)
  4. 右键单击以再次显示弹出菜单(弹出菜单大小正常)
  5. 再次单击Press me按钮。
  6. 右键单击以显示弹出菜单。(错误#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(弹出窗口和项目之间)。它叫JPanelgetPreferredSize(),直接叫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);

相关内容

  • 没有找到相关文章

最新更新