Well, here’s a little Swing effect that I’d like to share.
Brief: Animation effect to smoothly change components on the form.
Motivation: When you’re changing components in a usual way (remove, add, revalidate), there’s no visual connection between changing components: one of them just disappear, and another appear on its place.
Example: http://voituk.kiev.ua/demo/ws/fade/fade.jnlp
Sources: in post attachment
Explanation.
First thing I do after user presses “Test Fade” key is create a new BufferedImage same size as visible component and render that component to image (we create a “screenshot” of the component):
tableImage = new BufferedImage(
scrollPane.getWidth(),
scrollPane.getHeight(),
BufferedImage.TYPE_INT_ARGB);
Graphics2D g = (Graphics2D) tableImage.getGraphics();
scrollPane.paint(g);
g.dispose();
Now, when we have a screenshot we can apply some effects to it. I’ve applied Gaussian Blur. I took the code that calculates the Kernel of ConvolveOp from Filthy Rich Clients (great book, BTW).
tableImage = getGaussianBlurFilter(3, false).filter(tableImage, null);
Now we have a blurred screenshot. I put a screenshot on a separate component and place it instead of original component on a LayeredPane. On a “lower” layer I put the form that I’m going to show:
// Settings screen is what appears under the table
SettingsScreen settings = new SettingsScreen();
settings.setBounds(0, 0, scrollPane.getWidth(), scrollPane.getHeight());
layeredPane.add(settings, new Integer(10));
layeredPane.add(imageComponent, new Integer(20));
add(layeredPane, BorderLayout.CENTER);
revalidate();
Now everything is ready to start animation. I use TimingFramework to save time writing thread-handling code. You can do the same with plain threads if you don’t want to include additional libraries to your project.
Here’s what I do in the animation loop. I use AlphaCompolite.Clear to make a transparent “hole” in the image, and then repaint image component. Here’s the code:
private void drawHoles(Graphics2D g) {
g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1.0f));
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
for (int i = 0; i < drops.length; i++) {
g.setColor(Color.GRAY);
g.drawOval(
drops[i].x - radiuses[i]/2,
drops[i].y - radiuses[i]/2,
radiuses[i],
radiuses[i]);
}
g.setComposite(AlphaComposite.Clear);
for (int i = 0; i < drops.length; i++)
g.fillOval(
drops[i].x - radiuses[i]/2,
drops[i].y - radiuses[i]/2,
radiuses[i],
radiuses[i]);
}
That’s it. This was a quick experiment, so there are some more things to consider for production: clear the resources used, think what will happen if user resizes screen during animation (well, the probability of such action is really low, but you know those users), and finally put resulting component directly on the frame (get rid of LayeredPane that was used for animation only).
P.S. Don’t look at the components functionality (those “detect proxy”). I’ve taken the first component that was suitable to show the idea.