Java:透明的 JDialog 被重绘为白色
Java: transparent JDialog gets repainted white
我在 Windows 上遇到了一个非常大的 Java 程序的奇怪问题。我写了一个小测试程序来重现这个问题。
在 Windows 打开 UAC 提示覆盖后,自定义的透明 JDialog 被重新粉刷成完全白色。
给定以下简单测试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);
}
}
以下一系列操作能够为我重现该问题:
- 启动程序。不要点击任何按钮或将鼠标移到对话框上!
- 强制出现 UAC 提示。例如,当您将 UAC 设置为最高安全级别时,禁用或启用网络适配器。确认提示。
- 点击 "test button 2"。对话框重新粉刷成白色,只有两个按钮保持可见(因为它们是为系统外观效果重新绘制的)
如果您不想或无法重现该问题,请查看以下两个屏幕截图:
之前:
之后:
我想知道此错误的解释或可能的解决方法。最好两者都:)
关于我正在使用的系统的一些细节:
- Windows 7 x64
- Java 8u60
- eclipse Mars 4.5.0(用于启动和调试)
非常感谢!
如非必要,请不要在颜色中使用不透明度。这打破了关于不透明度的绘画契约,并可能导致绘画伪影。有关可能导致的问题,请参阅 Background With Transparency。
使用255时,组件不透明。使用 0 时,组件是透明的。所以只需使用:
component.setOpaque(true or false);
在框架或对话框上使用透明度时,您可以直接设置不透明度。尝试使用:
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);
我自己找到了解决方法,其中涉及使用 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");
必须在创建 window 之前设置
- window属性,如未修饰和背景颜色,必须在使用前设置
WindowUtils.setWindowTransparent(this, true);
,之后不能更改
- 作为(对我来说理想的)副作用,所有未绘制的透明像素现在都可以点击(意味着鼠标事件会传播到此 window 后面的其他 windows)
我在 Windows 上遇到了一个非常大的 Java 程序的奇怪问题。我写了一个小测试程序来重现这个问题。
在 Windows 打开 UAC 提示覆盖后,自定义的透明 JDialog 被重新粉刷成完全白色。
给定以下简单测试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);
}
}
以下一系列操作能够为我重现该问题:
- 启动程序。不要点击任何按钮或将鼠标移到对话框上!
- 强制出现 UAC 提示。例如,当您将 UAC 设置为最高安全级别时,禁用或启用网络适配器。确认提示。
- 点击 "test button 2"。对话框重新粉刷成白色,只有两个按钮保持可见(因为它们是为系统外观效果重新绘制的)
如果您不想或无法重现该问题,请查看以下两个屏幕截图:
之前:
之后:
我想知道此错误的解释或可能的解决方法。最好两者都:)
关于我正在使用的系统的一些细节:
- Windows 7 x64
- Java 8u60
- eclipse Mars 4.5.0(用于启动和调试)
非常感谢!
如非必要,请不要在颜色中使用不透明度。这打破了关于不透明度的绘画契约,并可能导致绘画伪影。有关可能导致的问题,请参阅 Background With Transparency。
使用255时,组件不透明。使用 0 时,组件是透明的。所以只需使用:
component.setOpaque(true or false);
在框架或对话框上使用透明度时,您可以直接设置不透明度。尝试使用:
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);
我自己找到了解决方法,其中涉及使用 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");
必须在创建 window 之前设置
- window属性,如未修饰和背景颜色,必须在使用前设置
WindowUtils.setWindowTransparent(this, true);
,之后不能更改 - 作为(对我来说理想的)副作用,所有未绘制的透明像素现在都可以点击(意味着鼠标事件会传播到此 window 后面的其他 windows)