使用某个左下角坐标进行JPopupMenu显示

假设我想以这样的方式在Java中创建一个按钮,这样当您单击它时,就会出现JPopupMenu 。 它出现的相关代码是menu.show(button, button.getWidth()/2, button.getHeight()/2); ,这使得JPopupMenu的左上角显示在按钮的中心,如下所示:

当前Swing JPopupMenu

但是,我希望它能让左下角位于按钮的中央,就像iTunes一样(左下角有一个按钮,大小与+按钮相同)左边):

在此处输入图像描述

我试图通过获取JPopupMenu的高度并将其添加到弹出菜单显示的y坐标来实现这一点,但我发现JPopupMenu在可见之前的高度为0,而不是帮助我,因为我试图告诉计算机在哪里可以看到它。 此外,不可能在偏移中进行硬编码,因为弹出窗口中的项目数量不一定相同。

我怎样才能使我的JPopupMenu具有未知的高度,以便它的左下角坐标与给定的坐标相匹配?

基本上,这会创建一个弹出菜单,并使用JComponent#setComponentPopupMenu将其注册到组件。 这意味着我不再需要监视鼠标事件或决定何时显示弹出窗口。

然后我重写JComponent#getPopupLocation并计算我希望弹出窗口出现的位置。

基本上,我得到了JComponent#getComponentPopupMenu ,得到它的首选大小并计算一个合适的偏移量,以便左下角现在出现在组件的中心……

在此处输入图像描述

 import java.awt.BorderLayout; import java.awt.Dimension; import java.awt.EventQueue; import java.awt.Graphics; import java.awt.Point; import java.awt.event.MouseEvent; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JMenuItem; import javax.swing.JPanel; import javax.swing.JPopupMenu; import javax.swing.UIManager; import javax.swing.UnsupportedLookAndFeelException; public class TestPopup02 { public static void main(String[] args) { new TestPopup02(); } public TestPopup02() { EventQueue.invokeLater(new Runnable() { @Override public void run() { try { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) { } JFrame frame = new JFrame("Testing"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setLayout(new BorderLayout()); frame.add(new TestPane()); frame.pack(); frame.setLocationRelativeTo(null); frame.setVisible(true); } }); } public class TestPane extends JPanel { public TestPane() { JPopupMenu menu = new JPopupMenu(); menu.add(new JMenuItem("Edit Playlist")); menu.addSeparator(); menu.add(new JMenuItem("Check for Available Downloads...")); menu.addSeparator(); menu.add(new JMenuItem("Export...")); menu.add(new JMenuItem("Burn Playlist to Disc")); menu.add(new JMenuItem("Copy To Play Order")); menu.addSeparator(); menu.add(new JMenuItem("Delete")); setComponentPopupMenu(menu); } @Override public Point getPopupLocation(MouseEvent event) { // Get the registered popup menu... JPopupMenu popup = getComponentPopupMenu(); // Get the super point, just in case... Point pos = super.getPopupLocation(event); if (popup != null) { // Create a new "point" location pos = new Point(); // get the preferred size of the menu... Dimension size = popup.getPreferredSize(); // Adjust the x position so that the left side of the popup // appears at the center of the component pos.x = (getWidth() / 2); // Adjust the y position so that the y postion (top corner) // is positioned so that the bottom of the popup // appears in the center pos.y = (getHeight() / 2) - size.height; } return pos; } @Override public Dimension getPreferredSize() { return new Dimension(400, 200); } @Override protected void paintComponent(Graphics g) { super.paintComponent(g); // Simply draws a cross in the center of the window, so you // know where the center is... int width = getWidth() - 1; int height = getHeight() - 1; g.drawLine(width / 2, 0, width / 2, height); g.drawLine(0, height / 2, width, height / 2); } } } 

使用Mac输出更新

在此处输入图像描述

按钮示例

您不可能找到满足您需求的解决方案。 任何开发人员可以开发的最大技能之一是能够在需要时采取创意并将其塑造。

前面的示例包含您需要的所有内容,您只需要能够实现从概念到解决方案的跨越。

getPopupLocation是组件弹出API的一部分,所以重写方法或调用它可能不是你需要的(除非你有一个专用的任务按钮,这可能不是一件坏事),所以你需要根据您的需求调整解决方案……

在此处输入图像描述

 import java.awt.BorderLayout; import java.awt.Dimension; import java.awt.EventQueue; import java.awt.Graphics; import java.awt.GridBagLayout; import java.awt.Point; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseEvent; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JMenuItem; import javax.swing.JPanel; import javax.swing.JPopupMenu; import javax.swing.UIManager; import javax.swing.UnsupportedLookAndFeelException; public class TestPopup02 { public static void main(String[] args) { new TestPopup02(); } public TestPopup02() { EventQueue.invokeLater(new Runnable() { @Override public void run() { try { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) { } JFrame frame = new JFrame("Testing"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setLayout(new BorderLayout()); frame.add(new TestPane()); frame.pack(); frame.setLocationRelativeTo(null); frame.setVisible(true); } }); } public class TestPane extends JPanel { private JButton button; private JPopupMenu popup; public TestPane() { popup = new JPopupMenu(); popup.add(new JMenuItem("Edit Playlist")); popup.addSeparator(); popup.add(new JMenuItem("Check for Available Downloads...")); popup.addSeparator(); popup.add(new JMenuItem("Export...")); popup.add(new JMenuItem("Burn Playlist to Disc")); popup.add(new JMenuItem("Copy To Play Order")); popup.addSeparator(); popup.add(new JMenuItem("Delete")); setLayout(new GridBagLayout()); button = new JButton("+"); button.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { popup.pack(); Point pos = new Point(); // get the preferred size of the menu... Dimension size = popup.getPreferredSize(); // Adjust the x position so that the left side of the popup // appears at the center of the component pos.x = (button.getWidth() / 2); // Adjust the y position so that the y postion (top corner) // is positioned so that the bottom of the popup // appears in the center pos.y = (button.getHeight() / 2) - size.height; popup.show(button, pos.x, pos.y); } }); add(button); } @Override public Dimension getPreferredSize() { return new Dimension(400, 200); } @Override protected void paintComponent(Graphics g) { super.paintComponent(g); // Simply draws a cross in the center of the window, so you // know where the center is... int width = getWidth() - 1; int height = getHeight() - 1; g.drawLine(width / 2, 0, width / 2, height); g.drawLine(0, height / 2, width, height / 2); } } } 

这实际上比我想象的要容易得多,并且在大多数情况下都应该有效。 我用了你的pastebin代码并玩了一下。 在框架上调用setVisible(true) ,我可以调用menu.getPreferredSize() 。 只是将它打印到标准输出给了我java.awt.Dimension[width=31,height=62] 。 这可以在调用ActionListener之前完成,这样您就可以使用高度。

如果您在菜单中使用边框,则可能需要考虑这一点,但上述情况应该有效。

这个答案只是用来备份我关于弹出窗口可见性和getPreferredSize()的评论。

请注意,您可以为弹出窗口获得零首选高度…如果它具有零菜单项,那将是合乎逻辑的结论。

同样,MadProgrammer的答案应该被接受。

 import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Insets; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JMenuItem; import javax.swing.JPopupMenu; import javax.swing.SwingUtilities; public class ButtonPopup extends JFrame { private JButton button; public ButtonPopup() { setLayout(new GridBagLayout()); button = new JButton("Click Meh~ /o/"); button.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { JPopupMenu popup = new JPopupMenu(); popup.add(new JMenuItem("A")); popup.add(new JMenuItem("B")); popup.add(new JMenuItem("C")); // you want this int height = popup.getPreferredSize().height; popup.show( button, button.getWidth() / 2, -height + (button.getHeight() / 2)); } }); GridBagConstraints gbc = new GridBagConstraints(); gbc.insets = new Insets(5, 5, 5, 5); add(button, gbc); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); pack(); setLocationRelativeTo(null); } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { public void run() { new ButtonPopup().setVisible(true); } }); } } 

PS:我通常不会复活长期死亡的问题,但这次我正在寻找类似的东西,这是在谷歌首次出现。