非常奇怪的Java2D setClip()效果-bug?
我想创建一个具有自定义形状和透明度的对话框,想想指向某个组件的信息气泡 为此,我将非常奇怪的Java2D setClip()效果-bug?,java,swing,java-2d,Java,Swing,Java 2d,我想创建一个具有自定义形状和透明度的对话框,想想指向某个组件的信息气泡 为此,我将JPanel添加到JDialog并覆盖面板的paintComponent(Graphics)方法。面板本身包含常规的jlabel和JButtons。 工作正常,但只要我在面板绘制代码中使用Graphics2D.setClip(Shape),组件就会被背景透支。如果我不将剪辑设置为一个全新的Graphics2D对象,那么一切都正常。 这对我来说非常困惑,我不知道我能做些什么来修复它 注意:我不能在JDialog上使用
JPanel
添加到JDialog
并覆盖面板的paintComponent(Graphics)
方法。面板本身包含常规的jlabel
和JButtons
。
工作正常,但只要我在面板绘制代码中使用Graphics2D.setClip(Shape)
,组件就会被背景透支。如果我不将剪辑设置为一个全新的Graphics2D
对象,那么一切都正常。
这对我来说非常困惑,我不知道我能做些什么来修复它
注意:我不能在JDialog
上使用setShape(Shape)
,因为那里没有抗锯齿功能。
P.P.S.:实际的用例是绘制一个大背景图像,该图像必须精确地在信息气泡形状处截断
以下SSCCE演示了将鼠标多次悬停在右上角的“x”上时出现的问题
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextArea;
import java.awt.Polygon;
import java.awt.Shape;
public class Java2DTransparencySSCE extends JDialog {
JPanel panel;
private JButton close;
private JLabel headline;
private JLabel mainText;
public Java2DTransparencySSCE() {
setLayout(new BorderLayout());
setFocusable(false);
setFocusableWindowState(false);
setUndecorated(true);
setBackground(new Color(0, 0, 0, 0));
panel = new JPanel(new GridBagLayout()) {
@Override
public void paintComponent(final Graphics g) {
super.paintComponent(g);
Graphics2D gImg = (Graphics2D) g.create();
// if the line below is removed, everything works..
Shape oldClip= gImg.getClip();
// both the shape and a standard rectangular clip break everything
// gImg.setClip(50, 50, 50, 50);
Polygon shape = new Polygon(new int[] {0, 100, 100, 50, 0}, new int[] {200, 200, 100, 50, 100}, 5);
gImg.setClip(shape);
gImg.setColor(new Color(255, 0, 0, 50));
gImg.fill(shape);
gImg.setClip(oldClip);
gImg.dispose();
}
};
panel.setOpaque(false);
add(panel, BorderLayout.CENTER);
headline = new JLabel("This is a title");
mainText = new JLabel("<html><div style=\"line-height: 150%;width:100px \">This is some sort of text which is rather long and thus pretty boring to actually read. Thanks for reading anyway!</div></html>");
close = new JButton("X");
close.setBorderPainted(false);
close.setContentAreaFilled(false);
close.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
System.exit(0);
}
});
layoutPanel();
}
private void layoutPanel() {
GridBagConstraints constraints = new GridBagConstraints();
constraints.gridx = 0;
constraints.gridy = 0;
constraints.insets = new Insets(10, 10, 10, 10);
constraints.fill = GridBagConstraints.BOTH;
constraints.weightx = 1;
panel.add(headline, constraints);
constraints.gridx += 1;
constraints.weightx = 0;
constraints.fill = GridBagConstraints.NONE;
panel.add(close, constraints);
constraints.gridx = 0;
constraints.gridy += 1;
constraints.gridwidth = 2;
constraints.weightx = 1;
constraints.weighty = 1;
constraints.fill = GridBagConstraints.BOTH;
panel.add(mainText, constraints);
pack();
}
public static void main(String[] args) {
JFrame parent = new JFrame();
JPanel pane = new JPanel(new BorderLayout());
pane.add(new JTextArea("This is a text."));
parent.setContentPane(pane);
parent.setSize(400, 400);
parent.setLocation(400, 300);
parent.setDefaultCloseOperation(JDialog.EXIT_ON_CLOSE);
parent.setVisible(true);
JDialog dialog = new Java2DTransparencySSCE();
dialog.setLocation(500, 400);
dialog.setAlwaysOnTop(true);
dialog.setVisible(true);
}
}
您应该在自定义图形之前存储原始剪辑,然后恢复
Shape oldCLip=g2d.getClip();
...
your code
...
g2d.setClip(oldClip);
您应该在绘制完所有代码后调用super.paintComponent(g)(它将是覆盖的最后一行)
这样,您的绘图将位于组件子对象的下方。当鼠标悬停在
JButton
上时,面板仅使用重新绘制所需的边界重新绘制自身-按钮的边界(如果选中oldClip
,则应为JButton
的边界)。更改剪辑边界会导致alpha颜色是以前每个绘制调用的合成,因为剪辑未被super.paintComponent
清除,并且JDialog的背景是完全透明的
我想创建一个具有自定义形状和透明度的对话框,想想指向某个组件的信息气泡
考虑使用轻量级组件方法-可以通过设置包含对话框项的JFrame的玻璃窗格,根据需要切换玻璃窗格的可见性和/或内容来实现 在MacOSX10.9和Java8上,我看不到任何异常。我看到了与下面的变体相同的外观,其中派生图形上下文既没有创建也没有处理。建议“程序员应该在完成使用
图形对象时调用dispose,前提是它是直接从组件或其他图形对象创建的。”我不确定实现在内部有何不同,但这可能是罪魁祸首
@Override
public void paintComponent(final Graphics g) {
super.paintComponent(g);
Graphics2D gImg = (Graphics2D) g;
Shape oldClip= gImg.getClip();
Polygon shape = new Polygon(
new int[] { 0, 100, 100, 50, 0},
new int[] {200, 200, 100, 50, 100}, 5);
gImg.setClip(shape);
gImg.setColor(new Color(255, 0, 0, 50));
gImg.fill(shape);
gImg.setClip(oldClip);
}
放置gImg.setClip(null)时会发生什么;在gImg.dispose()之前?我猜gImg.setClip()也会影响g,所以其他按钮会被剪掉。不会改变任何东西。置零或将其设置回旧剪辑都没有帮助。在Mac OS X 10.9、Java 8上看起来还可以,@trashgood quirieve!我不认为值得一提的是,这发生在Windows7、Java8上。在Linux、Java(TM)SE运行时环境(build 1.8.0_05-b13)上看起来也不错。不过,我也可以确认一个窗口的损坏行为。在一个新创建的图形对象上,我会马上扔掉吗?我还编辑了我的代码以包含它,因为它不修复任何东西。我在写SSCE的时候忘了把它放回去。你不能直接转换成Graphics2D g而不是创建一个新的吗?在调用super.paintComponent(g)之前还存储/设置新剪辑;并调用setOpaque(false)以避免背景绘制这只适用于jlabel等,对于JPanel等容器则是错误的。你可以在你的定制物品上画上灰色的背景。不用说,它不起作用。谢谢您的测试。我现在还测试了各种Linux和OS X机器上的代码,按照上面的屏幕截图工作。然而,在所有经过测试的Windows机器(7和8)上,多边形完全变红,覆盖了其子对象的内容。我试着不处理图形,也没有任何区别。这表明平台或驱动程序异常。这为我指明了正确的方向!这仍然是Java和Windows之间的某种缺陷,但是我能够通过重写JDialog的paint(Graphics)方法并调用g.setClip(null)来防止它;在调用super.paint(g)之前。现在它按预期工作。在我的例子中,当重置剪辑时,它不会导致任何问题或性能问题,所以它对我来说是有效的。
@Override
public void paintComponent(final Graphics g) {
super.paintComponent(g);
Graphics2D gImg = (Graphics2D) g;
Shape oldClip= gImg.getClip();
Polygon shape = new Polygon(
new int[] { 0, 100, 100, 50, 0},
new int[] {200, 200, 100, 50, 100}, 5);
gImg.setClip(shape);
gImg.setColor(new Color(255, 0, 0, 50));
gImg.fill(shape);
gImg.setClip(oldClip);
}