似乎我在Java中发现了一个bug:
我需要创建具有透明背景的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
发布于 2013-09-20 14:00:41
这是Oracle JDK 7中的bug (顺便说一下,它不能在Open 7中复制)。
要解决这个问题,您可以做一个解决方案(是的,这只是一个解决方案,不能保证它不会与某些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版本上编译(在早期版本的窗口中没有"set/getBackground“方法)。
发布于 2013-09-20 21:57:00
。
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);
}
}发布于 2015-06-30 11:23:02
谢谢你给我一个很好的解决方案,你帮我解决了一个类似的问题!我想分享我的解决方案-基于米克尔的-有一点不同,这在我的情况下是有意义的。
我想要的是: JPopupMenu后面透明的、未装饰的窗口(我的自定义弹出窗口是用花哨的语音气球边框显示的,所以后面的窗口应该是不可见的)。
有一件事情在PropertyChangeListener中运行得不够好:在窗口显示在屏幕上之后,窗口外观得到了调整。在MacOSX10.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。
https://stackoverflow.com/questions/18918367
复制相似问题