非顶层窗口中的非不透明JButton背景变为不透明?

7

在阅读本文之前,以下是对问题的一些澄清:

  1. SSCCE是为Java 7设计的。可以使用sun.*.AWTUtilities将其适配到Java 6,但Java 6上的运行方式对我并不重要。
  2. 导致问题的代码行是[...]new JDialog(someWindow)。只需将该行更改为[...]new JDialog()即可在SSCCE中解决"ghosting"问题。

为什么顶级窗口不会出现"ghosting"问题?


期望的行为:final JDialog d = new JDialog()(参见SSCCE) both TL and non-TL windows have translucent background

如您所看到的,右侧窗口具有半透明背景(如预期)。

实际行为:final JDialog d = new JDialog(f)(参见SSCCE) TL window shows translucent background, while non-TL background becomes opaque even after a single repaint

在这种情况下,右侧窗口具有不透明的背景。实际上,需要3-4次重绘出现任何原因(最容易重现的是在悬停时重绘)才能使背景完全不透明。


SSCCE:

import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;

import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.plaf.synth.ColorType;
import javax.swing.plaf.synth.Region;
import javax.swing.plaf.synth.SynthConstants;
import javax.swing.plaf.synth.SynthContext;
import javax.swing.plaf.synth.SynthLookAndFeel;
import javax.swing.plaf.synth.SynthPainter;
import javax.swing.plaf.synth.SynthStyle;
import javax.swing.plaf.synth.SynthStyleFactory;

public class SynthSSCCE
{
        public static void main(String[] args) throws Exception
        {
                final SynthLookAndFeel laf = new SynthLookAndFeel();
                UIManager.setLookAndFeel(laf);
                SynthLookAndFeel.setStyleFactory(new StyleFactory());

                SwingUtilities.invokeLater(new Runnable()
                {
                        @Override
                        public void run()
                        {
                                final JFrame f = new JFrame();
                                {
                                        f.add(new JButton("Works properly"));
                                        f.setUndecorated(true);
                                        f.setBackground(new Color(0, true));
                                        f.setSize(300, 300);
                                        f.setLocation(0, 0);
                                        f.setVisible(true);
                                }
                                {
                                        final JDialog d = new JDialog(f);
                                        final JButton btn = new JButton("WTF?");
                                        // uncomment and notice that this has no effect
                                        // btn.setContentAreaFilled(false);
                                        d.add(btn);
                                        d.setUndecorated(true);
                                        d.setBackground(new Color(0, true));
                                        d.setSize(300, 300);
                                        d.setLocation(320, 0);
                                        d.setVisible(true);
                                }
                        }
                });
        }

        static class StyleFactory extends SynthStyleFactory
        {
                private final SynthStyle style = new Style();

                @Override
                public SynthStyle getStyle(JComponent c, Region id)
                {
                        return style;
                }
        }

        static class Style extends SynthStyle
        {
                private final SynthPainter painter = new Painter();

                @Override
                protected Color getColorForState(SynthContext context, ColorType type)
                {
                        if (context.getRegion() == Region.BUTTON && type == ColorType.FOREGROUND)
                                return Color.GREEN;

                        return null;
                }

                @Override
                protected Font getFontForState(SynthContext context)
                {
                        return Font.decode("Monospaced-BOLD-30");
                }

                @Override
                public SynthPainter getPainter(SynthContext context)
                {
                        return painter;
                }

                @Override
                public boolean isOpaque(SynthContext context)
                {
                        return false;
                }
        }

        static class Painter extends SynthPainter
        {
                @Override
                public void paintPanelBackground(SynthContext context, Graphics g, int x, int y, int w, int h)
                {
                        final Graphics g2 = g.create();
                        try
                        {
                                g2.setColor(new Color(255, 255, 255, 128));

                                g2.fillRect(x, y, w, h);
                        }
                        finally
                        {
                                g2.dispose();
                        }
                }

                @Override
                public void paintButtonBackground(SynthContext context, Graphics g, int x, int y, int w, int h)
                {
                        final Graphics g2 = g.create();
                        try
                        {
                                if ((context.getComponentState() & SynthConstants.MOUSE_OVER) == SynthConstants.MOUSE_OVER)
                                        g2.setColor(new Color(255, 0, 0, 255));
                                else
                                        g2.setColor(new Color(0xAA, 0xAA, 0xAA, 255));
                                g2.fillRoundRect(x, y, w, h, w / 2, h / 2);
                        }
                        finally
                        {
                                g2.dispose();
                        }
                }
        }
}

以下是我的问题:

  1. 发生了什么?我的意思是,为什么这显示了一个自定义的非透明组件的行为,它忘记调用super方法?
  2. 为什么在 TL 窗口中不会出现这种情况?
  3. 除了不使用非 TL 窗口之外,最简单的解决方法是什么?

用完整代码替换链接。 - Tadas S
呃,但在Java6中对我有效... - mKorbel
@mKorbel:我曾考虑过添加一个基于反射的AWTUtilities#setWindowOpaque(Window, boolean)的Java 6 SSCCE,但是Java 6早已被EoLed很久了。研究如何在过时的技术上工作毫无意义... - Tadas S
嗯嗯(再次测试)现在我有点困惑了,看到这个问题,我不确定是Java6还是Java7的问题,特别是为什么Win8/Java7中JDialog是透明的。但是请确保setContentAreaFilled正常工作,因为您必须覆盖JButton实例的paintText和paintIcon方法,创建自己的SyntButtonUI,我会在这里发布我的测试结果作为答案。 - mKorbel
显示剩余6条评论
2个回答

1
由于任何原因(最容易重现的是悬停时重新绘制),需要3-4次重新绘制才能使背景完全不透明。请参阅具有透明度的背景,这应该会让您对问题有所了解。我从未使用过Synth,所以我不知道是否同样的解决方案会起作用。

Synth不遵循先前的Nimbus L&F共识,即不透明组件是指绘制其背景及其反之的组件。 Synth允许非不透明组件按其应该的方式绘制其背景(对于某些组件,UIDefaults中的isContentAreaFilled控制是否绘制背景)。 主要问题是为什么TL窗口上的所有内容都可以正常工作,而在非TL窗口上却不能? - Tadas S

0
为什么顶层窗口不会出现幽灵效果?
根据Oracle(Java教程)的说法:
每个顶级容器都有一个内容面板,通常包含该顶级容器GUI中的可见组件(直接或间接)。

http://docs.oracle.com/javase/tutorial/uiswing/components/toplevel.html

enter image description here

玻璃窗格通常用于拦截发生在顶级容器上的输入事件,并可用于涂绘多个组件。它不允许透明度。
因此您如何使用
final Graphics g2 = g.create();

如果您在一个方法中重写了javax.swing.JComponent.paintComponent而不是自己创建图形对象,那么通过super.g()可以缓解透明度问题。

通过创建上面列出的一个单独的方法来修复这个问题。


严重怀疑玻璃窗格与此问题有任何关系:例如,这如何解释new JDialog()与new JDialog(frame)的不同行为? - kleopatra
所有组件都已添加到内容面板。玻璃面板完全未被使用(可以在SSCCE中看到)。 - Tadas S

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接