简体   繁体   English

Java:透明的JDialog被重新粉刷成白色

[英]Java: transparent JDialog gets repainted white

I am experiencing a very strange problem with a quite large Java program on Windows. 我在Windows上使用大型Java程序遇到一个非常奇怪的问题。 I have written a small test program to reproduce the issue. 我已经编写了一个小型测试程序来重现该问题。

A custom, transparent JDialog gets repainted completely white after a UAC prompt overlay has been opened by Windows. Windows打开UAC提示覆盖后,自定义的透明JDialog将重新粉刷成白色。

Given the following simple test class: 给定以下简单的测试类:

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);
    }

}

the following sequence of actions is able to reproduce the issue for me: 以下操作顺序可以为我重现此问题:

  1. Launch the program. 启动程序。 Don't click any buttons or move your mouse over the dialog! 不要单击任何按钮或将鼠标移到对话框上!
  2. Force an UAC prompt to appear. 强制出现UAC提示。 For example, disable or enable a network adapter when you have UAC set to the highest security level. 例如,当您将UAC设置为最高安全级别时,请禁用或启用网络适配器。 Confirm the prompt. 确认提示。
  3. Click on "test button 2". 单击“测试按钮2”。 The dialog becomes repainted white, only the two buttons remain visible (because they are redrawn for the System Look&Feel effects) 对话框重新粉刷成白色,只有两个按钮保持可见(因为为系统外观而重新绘制了它们)

If you do not want to or cannot reproduce the issue, here are two screenshots: 如果您不想或无法重现此问题,请参见以下两个屏幕截图:

Before: 之前:

开始之后

After: 后:

白漆虫

I would like to know an explanation for this bug, or a possible workaround. 我想知道此错误的解释或可能的解决方法。 Preferably both :) 最好两个都:)

Some details about the system I am working with: 有关正在使用的系统的一些详细信息:

  • Windows 7 x64 Windows 7 x64
  • Java 8 u60 Java 8 U60
  • eclipse Mars 4.5.0 (used for launching & debugging) 蚀火星4.5.0(用于启动和调试)

Thank you very much! 非常感谢你!

Don't use opacity in a Color if you don't have to. 如果不需要,请不要在“颜色”中使用不透明度。 This breaks the painting contract regarding opaqueness and can cause painting artifacts. 这会破坏有关不透明性的绘画协定,并可能导致绘画伪影。 See Background With Transparency for the problems that can result. 有关可能导致的问题,请参阅透明背景

When using 255, the component is opaque. 使用255时,组件是不透明的。 When using 0, the component is non-opaque. 使用0时,该组件是不透明的。 So just use: 因此,只需使用:

component.setOpaque(true or false);

When using transparency on a frame or dialog you can set the opactity directly. 在框架或对话框上使用透明度时,可以直接设置透明度。 Try using: 尝试使用:

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);

I have found a workaround myself, which involves using the JNA library . 我自己找到了一种解决方法,其中涉及使用JNA库 It appears that by using WindowUtils.setWindowTransparent() the render mode gets changed in a way which does not lead to the bug anymore. 似乎通过使用WindowUtils.setWindowTransparent() ,呈现方式已被更改,而不再导致该错误。

The following augmented code should be working fine: 以下增强代码应该可以正常工作:

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);
    }

}

The only things to consider: 唯一要考虑的事项:

  • System.setProperty("sun.java2d.noddraw", "true"); has to be set before creating the window 必须在创建窗口之前进行设置
  • window properties, such as undecorated and background color, have to be set before using WindowUtils.setWindowTransparent(this, true); 在使用WindowUtils.setWindowTransparent(this, true);之前,必须设置窗口属性,例如未装饰的颜色和背景颜色WindowUtils.setWindowTransparent(this, true); and cannot be changed afterwards 以后不能更改
  • as a (for me desirable) side effect, all not-drawn transparent pixels are now click-through (means mouse events are propagated to other windows behind this window) 作为(对我来说很理想)副作用,所有未绘制的透明像素现在都可以单击(意味着鼠标事件会传播到该窗口后面的其他窗口)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM