问题
I'm trying to do animation in Java with a translucent JFrame. I have modified the demo code from the Oracle Java Tutorials here. Specifically the GradientTranslucentWindowDemo.
The following code works great in Windows XP SP3 up to 8 and Mac OS X Mountain Lion and even in Linux mostly. The problem in Linux, and what I need help with, is the animation is flickering.
I'm running Ubuntu Linux 12.04 LTS 64bit with nVidia drivers, Metacity and Compiz. PERPIXEL_TRANSLUCENT reports true and is working well.
Is there something I'm missing in the following code or is there something on the Linux side I need to change? I tried setDoubleBuffered(true) on the JPanel, but it did not remove the flickering.
Please reference my code changes to the Demo below:
import static java.awt.GraphicsDevice.WindowTranslucency.PERPIXEL_TRANSLUCENT;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.GridBagLayout;
import java.awt.Paint;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class GradientTranslucentWindowDemo extends JFrame implements ActionListener {
private Timer timer = new Timer(100, this);
private double percentage = 0.0;
private JPanel surface = new JPanel() {
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (g instanceof Graphics2D) {
final int R = 0;
final int G = 240;
final int B = 240;
Paint p =
new GradientPaint(0.0f, 0.0f, new Color(R, G, B, 0),
0.0f, getHeight(), new Color(R, G, B, 255), false);
Graphics2D g2d = (Graphics2D)g;
// CHANGE 1
// Clear the previous graphics using a completely transparent fill
g2d.setBackground(new Color(0, 0, 0, 0));
g2d.clearRect(0, 0, getWidth(), getHeight());
g2d.setPaint(p);
// CHANGE 2
// Only do a gradient fill for the current percentage of the width
g2d.fillRect(0, 0, (int)Math.ceil(getWidth() * percentage), getHeight());
}
}
};
public GradientTranslucentWindowDemo() {
super("GradientTranslucentWindow");
setBackground(new Color(0,0,0,0));
setSize(new Dimension(300,200));
setLocationRelativeTo(null);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// CHANGE 3
// I thought this might remove the flicker, nope
this.surface.setDoubleBuffered(true);
// CHANGE 4
// This seems to be required or the g2d.clearRect doesn't do anything
this.surface.setOpaque(false);
setContentPane(this.surface);
setLayout(new GridBagLayout());
add(new JButton("I am a Button"));
}
// CHANGE 5
// On each tick of the timer increment the percentage until its
// more than one and always repaint
@Override
public void actionPerformed(ActionEvent event) {
this.percentage += 0.05;
if (this.percentage > 1.0) {
this.percentage = 0.0;
}
this.surface.repaint();
}
public static void main(String[] args) {
// Determine what the GraphicsDevice can support.
GraphicsEnvironment ge =
GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice gd = ge.getDefaultScreenDevice();
boolean isPerPixelTranslucencySupported =
gd.isWindowTranslucencySupported(PERPIXEL_TRANSLUCENT);
//If translucent windows aren't supported, exit.
if (!isPerPixelTranslucencySupported) {
System.out.println(
"Per-pixel translucency is not supported");
System.exit(0);
}
JFrame.setDefaultLookAndFeelDecorated(true);
// Create the GUI on the event-dispatching thread
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
GradientTranslucentWindowDemo gtw = new GradientTranslucentWindowDemo();
// Display the window.
gtw.setVisible(true);
// CHANGE 6
// Wait until the window is visible to start the timer
gtw.timer.start();
}
});
}
}
UPDATE 1: Removing the translucency and choosing a black background fixes the flicker problem. The flicker is most certainly related to having a translucent window. I also noticed that the flicker becomes worse as the window is enlarged.
UPDATE 2:
The line this.surface.setOpaque(false);
is what is causing the problem. If this is commented out, the animation does not flicker and there is translucency. However, upon each iteration of the animation it blends with the previous paint (its not clearing the contents before repainting). Doing g2d.setBackground(new Color(0, 0, 0, 0));
and g2d.clearRect(0, 0, getWidth(), getHeight());
does nothing unless this.surface.setOpaque(false);
is set. It's almost like this line disables double buffering on linux.
Having a translucent window is a requirement.
来源:https://stackoverflow.com/questions/15704947/java-window-translucency-animation-flicker-on-linux