好像我在 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”方法)。

关于Java JPopupMenu 错误,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/18918367/

10-12 14:40