Java JPopupMenu bug

好像我在Java中发现了一个错误:

我需要创建一个透明背景的JFrame ,现在我需要为一些用户操作显示JPopupMenu 。 当JPopupMenu完全位于JFrame内时,它工作正常。 但是当JPopupMenu部分位于JFrame之外时,没有任何项目可见。

SSCCE:

 public class PopupTest { 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)); panel.addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent e) { if (e.getButton() == MouseEvent.BUTTON3) { JPopupMenu menu = new JPopupMenu(); for (int i = 0 ; i < 10; i++) { menu.add(String.valueOf(i)); } menu.show(panel, e.getX(), 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: JDK 7u40,Win x64

这是Oracle JDK 7中的错误(顺便说一句,它无法在Open JDK 7中重现)。

要解决此问题,您可以进行解决方法(是的,这只是一种解决方法,不能保证它不会破坏某些Java更新),因此为弹出菜单创建的窗口将变为非透明状态显示,然后它将正确显示。 至少现在(是。 以下是Java 7及更高版本的完成方式:

 PropertyChangeListener propertyChangeListener = new PropertyChangeListener () { @Override public void propertyChange ( final PropertyChangeEvent evt ) { if ( evt.getNewValue () == Boolean.TRUE ) { // Retrieving popup menu window (we won't find it if it is inside of parent frame) final Window ancestor = getWindowAncestor ( popupMenu ); if ( ancestor != null && ancestor.getClass ().getCanonicalName ().endsWith ( "HeavyWeightWindow" ) ) { // Checking that parent window for our window is opaque, only then setting opacity final Component parent = ancestor.getParent (); if ( parent != null && parent instanceof Window && parent.getBackground ().getAlpha () == 0 ) { // Making popup menu window non-opaque ancestor.setBackground ( new Color ( 0, 0, 0, 0 ) ); } } } } private Window getWindowAncestor ( Component component ) { if ( component == null ) { return null; } if ( component instanceof Window ) { return ( Window ) component; } for ( Container p = component.getParent (); p != null; p = p.getParent () ) { if ( p instanceof Window ) { return ( Window ) p; } } return null; } }; popupMenu.addPropertyChangeListener ( "visible", propertyChangeListener ); 

如果您还想支持JDK 6版本,则需要付出更多努力,因为该代码甚至不能在早期版本的JDK上编译(早期版本的Window中没有“set / getBackground”方法)。

  • 记住根窗格! ==你的方式是错误的,这是答案(也是你的评论)—> JButton你的意思是什么?,有趣的是,JButton只对UIManager.setLookAndFeel(“com.sun.java.swing.plaf”透明。 windows.WindowsClassicLookAndF鳗鱼“);

  • 这是正确的方法 ,这里是半透明的一切,rest是Java6和7之间的一些二进制变形, 因为不可能创建具有更改的L&F的装饰容器(对于diff,因为是getSystemL&F)

  • 我的Java6 / 7 _011,025和040的测试代码也没有,任何LaF都没有错误(仅在Oracle API中实现分发)

 import java.awt.BorderLayout; import java.awt.Color; import java.awt.Dimension; import java.awt.Graphics2D; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import javax.swing.BorderFactory; import javax.swing.JButton; import javax.swing.JComponent; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.JPopupMenu; import javax.swing.Painter; import javax.swing.SwingUtilities; import javax.swing.UIManager; public class PopupTest { private JFrame frame = new JFrame(); private JPanel panel = new JPanel(new BorderLayout()) { private static final long serialVersionUID = 1L; @Override public Dimension getPreferredSize() { return new Dimension(500, 500); } }; private JButton button = new JButton("Close me"); public PopupTest() { //panel.setOpaque(false); panel.setBorder(BorderFactory.createLineBorder(Color.RED)); panel.addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent e) { if (e.getButton() == MouseEvent.BUTTON3) { JPopupMenu menu = new JPopupMenu(); for (int i = 0; i < 56; i++) {//only FHD display menu.add(String.valueOf(i)); } menu.show(panel, e.getX(), e.getY()); } } }); button.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { System.exit(0); } }); button.setOpaque(false); panel.add(button, BorderLayout.SOUTH); frame.setLocation(150, 150); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); //frame.add(panel); frame.setContentPane(panel); frame.setUndecorated(true); frame.pack(); frame.setBackground(new Color(150, 50, 50, 200)); frame.setVisible(true); } public static void main(String[] a) { try { for (UIManager.LookAndFeelInfo laf : UIManager.getInstalledLookAndFeels()) { if ("Nimbus".equals(laf.getName())) { UIManager.setLookAndFeel(laf.getClassName()); UIManager.getLookAndFeelDefaults().put("PopupMenu[Enabled].backgroundPainter", new FillPainter(new Color(127, 255, 191))); UIManager.getLookAndFeelDefaults().put("text", new Color(255, 0, 0)); //UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); //UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel"); //UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsClassicLookAndFeel"); } } } catch (Exception e) { e.printStackTrace(); } SwingUtilities.invokeLater(new Runnable() { @Override public void run() { new PopupTest(); } }); } } class FillPainter implements Painter { private final Color color; FillPainter(Color c) { color = c; } @Override public void paint(Graphics2D g, JComponent object, int width, int height) { g.setColor(color); g.fillRect(0, 0, width - 1, height - 1); } } 

感谢Mikle Garin提供了一个很好的解决方案,你帮我解决了类似的问题! 我想分享我的解决方案 – 基于Mikle’s – 略有不同,这在我的案例中很重要。

我在寻找:JPopupMenu后面的透明未装饰的Windows(我的自定义弹出窗口显示有花哨的语音气球边框,因此它背后的窗口应该是不可见的)。

使用PropertyChangeListener不能很好地完成一件事:在窗口显示在屏幕上之后调整窗口外观。 在Mac OS X 10.10上,弹出窗口后面的java 8窗口首先显示为白色背景和薄边框(默认为L&F),稍后调整一段时间(约0.1 – 0.3秒)。 很讨厌。

在看了一下放置调整代码的位置后,我想出了以下简单的解决方案:在菜单添加到窗口之后调整窗口,然后显示窗口。 以下示例显示如何扩展JPopupMenu来实现此目的:

 import java.awt.Color; import java.awt.Window; import javax.swing.JPopupMenu; import javax.swing.Popup; import javax.swing.RootPaneContainer; import javax.swing.SwingUtilities; import javax.swing.plaf.PopupMenuUI; public class MyPopup extends JPopupMenu { public MyPopup() { super(); //... setUI(new MyPopupMenuUI()); } //... private void adjustHeavyWeightWindowIfThereIsOne() { // Retrieve popup menu window // on Windows we won't find it if this popup is inside of parent frame // on Mac we may find it even when DefaultLightWeightPopupEnabled is set to true final Window ancestor = SwingUtilities.getWindowAncestor(MyPopup.this); if (ancestor != null && ancestor.getClass().getCanonicalName().endsWith("HeavyWeightWindow")) { adjustWindowAppearance(ancestor); } } private void adjustWindowAppearance(Window w) { w.setBackground(new Color(0, 0, 0, 0)); if (w instanceof RootPaneContainer) { ((RootPaneContainer)w).getRootPane().setBorder(null); ((RootPaneContainer)w).getRootPane().setBackground(new Color(0, 0, 0, 0)); } } class MyPopupMenuUI extends PopupMenuUI { public Popup getPopup(JPopupMenu popup, int x, int y) { Popup toreturn = super.getPopup(popup, x, y); adjustHeavyWeightWindowIfThereIsOne(); return toreturn; } } } 

实际上不需要扩展JPopupMenu,但设置触发调整的自定义PopupMenuUI至关重要。 窗口调整代码可以轻松修改以满足特定需求,并可以移动到自定义PopupMenuUI。