首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Java JPopupMenu bug

Java JPopupMenu bug
EN

Stack Overflow用户
提问于 2013-09-20 13:44:50
回答 3查看 1.4K关注 0票数 4

似乎我在Java中发现了一个bug:

我需要创建具有透明背景的JFrame,现在我需要显示一些用户操作的JPopupMenu。当JPopupMenu完全封装在JFrame中时,它工作得很好。但是,当JPopupMenu部分在JFrame之外时,没有一个项目是可见的。

SSCCE:

代码语言:javascript
复制
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

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2013-09-20 14:00:41

这是Oracle JDK 7中的bug (顺便说一下,它不能在Open 7中复制)。

要解决这个问题,您可以做一个解决方案(是的,这只是一个解决方案,不能保证它不会与某些Java更新中断),这样为弹出菜单创建的窗口一出现就会变得不透明,然后它就会被正确显示。至少现在是这样。以下是如何在7和更高版本中这样做:

代码语言:javascript
复制
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版本上编译(在早期版本的窗口中没有"set/getBackground“方法)。

票数 4
EN

Stack Overflow用户

发布于 2013-09-20 21:57:00

代码语言:javascript
复制
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<JComponent> {

    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);
    }
}
票数 1
EN

Stack Overflow用户

发布于 2015-06-30 11:23:02

谢谢你给我一个很好的解决方案,你帮我解决了一个类似的问题!我想分享我的解决方案-基于米克尔的-有一点不同,这在我的情况下是有意义的。

我想要的是: JPopupMenu后面透明的、未装饰的窗口(我的自定义弹出窗口是用花哨的语音气球边框显示的,所以后面的窗口应该是不可见的)。

有一件事情在PropertyChangeListener中运行得不够好:在窗口显示在屏幕上之后,窗口外观得到了调整。在MacOSX10.10上,在弹出窗口后面有java 8窗口,首先显示为白色背景和细边框(L&F默认值),然后进行调整(大约0.1-0.3秒)。很烦人。

看了一会儿调整代码后,我想出了以下简单的解决方案:在菜单被添加到窗口之后,在显示窗口之前,立即调整窗口。下面的示例演示如何扩展JPopupMenu以实现这一点:

代码语言:javascript
复制
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。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/18918367

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档