| Sign In/My Account | View Cart |
Hacking Swing: Translucent Windows
Pages: 1, 2, 3
Additionally, any user action that would change the screen will probably create lots of events, not just one. It's just the last event that should trigger updateBackground( ), not the first. To handle all these issues, the code creates a thread that watches for repaint requests and only processes a new screenshot if it hasn't already been done in the last 1,000 milliseconds. If the user generates events continuously for five seconds (searching for that lost browser window, for example), then only when everything else has settled down for a second will the refresh actually happen. This ensures that users won't have a window disappear out from under them while they are moving things around.
Another annoyance is that the window still has its border, which sort of ruins the effect of having a transparent background. Unfortunately, removing the borders with setUndecorated(true) would also remove the titlebar and window controls. This probably isn't too much of a problem, though, because the types of applications that typically use shaped windows usually have draggable backgrounds [Hack #34].
Here's a simple test program to put this into action:
public static void main(String[] args) {
JFrame frame = new JFrame("Transparent Window");
frame.setUndecorated(true);
TransparentBackground bg = new TransparentBackground(frame);
bg.snapBackground( );
bg.setLayout(new BorderLayout( ));
JPanel panel = new JPanel( ) {
public void paintComponent(Graphics g) {
g.setColor(Color.blue);
Image img = new ImageIcon("mp3.png").getImage( );
g.drawImage(img,0,0,null);
}
};
panel.setOpaque(false);
bg.add("Center",panel);
frame.getContentPane( ).add("Center",bg);
frame.pack( );
frame.setSize(200,200);
frame.setLocation(500,500);
frame.show( );
}
The code creates a faux MP3 player interface using a JPanel subclass and a PNG image with transparency. Note the call to frame.setUndecorated(true), which turns off the border and titlebar. The call to panel.setOpaque(false) turns off the default background (usually plain gray), allowing the screenshot background to shine through the transparent parts of the image (Figure 6-2). This produces a window that looks like Figure 6-3—a vision of Java programs to come?

Figure 6-2. Template for an MP3 player

Figure 6-3. Running the MP3 player
Joshua Marinacci is the author of "The Java Sketchbook" column for java.net, covering topics in Java client-side and web development.
Chris Adamson is an author, editor, and developer specializing in iPhone and Mac.
View catalog information for Swing Hacks
Return to ONJava.com.
Showing messages 1 through 7 of 7.
public void updateBackground() {
try {
Robot rbt = new Robot();
background = rbt.createScreenCapture(getTotalScreenBounds());
} catch (Exception ex) {
p(ex.toString());
ex.printStackTrace();
}
}
private Rectangle getTotalScreenBounds(){
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
int w = 0, h = 0;
for(int i = 0; i < ge.getScreenDevices().length; i++){
w += ge.getMaximumWindowBounds().width;
}
h = ge.getMaximumWindowBounds().height;
return new Rectangle(0,0, w, h);
}
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
public class TransparentBackground extends JComponent
implements ComponentListener, WindowFocusListener, Runnable {
// constants ---------------------------------------------------------------
// instance ----------------------------------------------------------------
private JFrame _frame;
private BufferedImage _background;
private long _lastUpdate = 0;
private boolean _refreshRequested = true;
private Robot _robot;
private Rectangle _screenRect;
private ConvolveOp _blurOp;
// constructor -------------------------------------------------------------
public TransparentBackground(JFrame frame) {
_frame = frame;
try {
_robot = new Robot();
} catch (AWTException e) {
e.printStackTrace();
return;
}
Dimension dim = Toolkit.getDefaultToolkit().getScreenSize();
_screenRect = new Rectangle(dim.width, dim.height);
float[] my_kernel = {
0.10f, 0.10f, 0.10f,
0.10f, 0.20f, 0.10f,
0.10f, 0.10f, 0.10f};
_blurOp = new ConvolveOp(new Kernel(3, 3, my_kernel));
updateBackground();
_frame.addComponentListener(this);
_frame.addWindowFocusListener(this);
new Thread(this).start();
}
// protected ---------------------------------------------------------------
protected void updateBackground() {
_background = _robot.createScreenCapture(_screenRect);
}
protected void refresh() {
if (_frame.isVisible() && this.isVisible()) {
repaint();
_refreshRequested = true;
_lastUpdate = System.currentTimeMillis();
}
}
// JComponent --------------------------------------------------------------
protected void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
Point pos = this.getLocationOnScreen();
BufferedImage buf = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_RGB);
buf.getGraphics().drawImage(_background, -pos.x, -pos.y, null);
Image img = _blurOp.filter(buf, null);
g2.drawImage(img, 0, 0, null);
g2.setColor(new Color(255, 255, 255, 192));
g2.fillRect(0, 0, getWidth(), getHeight());
}
// ComponentListener -------------------------------------------------------
public void componentHidden(ComponentEvent e) {
}
public void componentMoved(ComponentEvent e) {
repaint();
}
public void componentResized(ComponentEvent e) {
repaint();
}
public void componentShown(ComponentEvent e) {
repaint();
}
// WindowFocusListener -----------------------------------------------------
public void windowGainedFocus(WindowEvent e) {
refresh();
}
public void windowLostFocus(WindowEvent e) {
refresh();
}
// Runnable ----------------------------------------------------------------
public void run() {
try {
while (true) {
Thread.sleep(100);
long now = System.currentTimeMillis();
if (_refreshRequested && ((now - _lastUpdate) > 1000)) {
if (_frame.isVisible()) {
Point location = _frame.getLocation();
_frame.setLocation(-_frame.getWidth(), -_frame.getHeight());
updateBackground();
_frame.setLocation(location);
refresh();
}
_lastUpdate = now;
_refreshRequested = false;
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
JFrame frame = new JFrame("Transparent Window");
TransparentBackground bg = new TransparentBackground(frame);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(bg);
frame.pack();
frame.setSize(200, 200);
frame.setLocation(500, 500);
frame.setVisible(true);
}
}
It seems like a nice solution and solves many of the issues with the hack. The only problem here is that you need hardware and software support for it, and the lack of support is getting less and less common.