Java:透明的JDialog重新绘制白色

时间:2015-09-11 17:53:25

标签: java windows transparent jdialog

我遇到了一个非常奇怪的问题,在Windows上有一个非常大的Java程序。我写了一个小测试程序来重现这个问题。

在Windows打开UAC提示叠加后,自定义透明的JDialog将重新绘制为全白色。

给出以下简单的测试类:

import java.awt.Color;
import java.awt.EventQueue;

import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.UIManager;

public class DialogTests extends JDialog {
    private static final long serialVersionUID = 1L;

    public static void main(String[] args) {
        try {
            UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
        } catch (Exception e) {
            e.printStackTrace();
        }
        EventQueue.invokeLater(() -> {
            new DialogTests().setVisible(true);
        });
    }

    public DialogTests() {
        this.setAutoRequestFocus(false);
        this.setUndecorated(true);
        this.setAlwaysOnTop(true);
        this.setFocusableWindowState(true);
        this.setBackground(new Color(0,255,255,0));

        JPanel contentPane = new JPanel();
        contentPane.setBackground(new Color(0,0,0,200));
        setContentPane(contentPane);
        setBounds(200, 200, 500, 500);

        JLabel label = new JLabel("this is just to see something!");
        label.setForeground(new Color(255,0,0,255));
        contentPane.add(label);

        JButton button1 = new JButton("test button 1");
        button1.setBackground(new Color(0,0,0,0));
        contentPane.add(button1);

        JButton button2 = new JButton("test button 2");
        button2.setBackground(new Color(0,0,0,0));
        contentPane.add(button2);
    }

}

以下一系列操作能够为我重现这个问题:

  1. 启动该计划。不要单击任何按钮或将鼠标移到对话框上!
  2. 强制显示UAC提示。例如,在UAC设置为最高安全级别时禁用或启用网络适配器。确认提示。
  3. 点击“测试按钮2”。对话框重新绘制为白色,只有两个按钮保持可见(因为系统外观和感觉效果会重新绘制)
  4. 如果您不想或不能重现此问题,请点击以下两个屏幕截图:

    在:

    After starting

    后:

    White paint bug

    我想知道这个错误的解释,或者可能的解决方法。最好是:)

    我正在使用的系统的一些细节:

    • Windows 7 x64
    • Java 8 u60
    • eclipse Mars 4.5.0(用于启动和调试)

    非常感谢!

2 个答案:

答案 0 :(得分:0)

如果你不必,不要在Color中使用不透明度。这打破了关于不透明的绘画合同,并可能导致绘画伪影。有关可能导致的问题,请参阅Background With Transparency

使用255时,组件是不透明的。使用0时,组件是不透明的。所以只需使用:

component.setOpaque(true or false);

在框架或对话框上使用透明度时,可以直接设置opactity。尝试使用:

this.setBackground(new Color(0,255,255)); // play with this color
this.setOpacity(0.75f);  // play with this opacity.
...
//contentPane.setBackground(new Color(0,0,0,200));
contentPane.setOpaque(false);

答案 1 :(得分:0)

我自己找到了一个解决方法,涉及使用JNA library。似乎通过使用WindowUtils.setWindowTransparent(),渲染模式的改变方式不再导致错误。

以下扩充代码应该可以正常工作:

import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;

import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.UIManager;

import com.sun.jna.platform.WindowUtils;

public class DialogTests extends JDialog {
    private static final long serialVersionUID = 1L;

    public static void main(String[] args) {
        try {
            System.setProperty("sun.java2d.noddraw", "true");
            UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
        } catch (Exception e) {
            e.printStackTrace();
        }
        EventQueue.invokeLater(() -> {
            new DialogTests().setVisible(true);
        });
    }

    public DialogTests() {
        this.setAutoRequestFocus(false);
        this.setUndecorated(true);
        this.setAlwaysOnTop(true);
        this.setFocusableWindowState(true);
        this.setBackground(new Color(0,255,255,0));
        WindowUtils.setWindowTransparent(this, true);

        JPanel contentPane = new JPanel();
        contentPane.setBackground(new Color(0,0,0,200));
        contentPane.setPreferredSize(new Dimension(200,200));
        setContentPane(contentPane);
        setBounds(200, 200, 500, 500);

        JLabel label = new JLabel("this is just to see something!");
        label.setForeground(new Color(255,0,0,255));
        contentPane.add(label);

        JButton button1 = new JButton("test button 1");
        button1.setBackground(new Color(0,0,0,0));
        contentPane.add(button1);

        JButton button2 = new JButton("test button 2");
        button2.setBackground(new Color(0,0,0,0));
        contentPane.add(button2);
    }

}

唯一需要考虑的事项:

    在创建窗口之前必须设置
  • System.setProperty("sun.java2d.noddraw", "true");
  • 窗口属性,例如未修饰和背景颜色,必须在使用WindowUtils.setWindowTransparent(this, true);之前设置,之后无法更改
  • 作为(对我来说可取)副作用,所有未绘制的透明像素现在都是点击式(意味着鼠标事件会传播到此窗口后面的其他窗口)