activity | |
initial post: | 01 dec 2008 |
last update: | 02 dec 2008 |
Few months ago I wanted some kind of page transition effect for a wizard I made in swing (an applet).
I found a nice effect in examples provided with an older release of JXLayer (from dev.java.net). The demo is still available at http://swinghelper.dev.java.net (the TabbedPaneAnimationDemo.jnlp link).
Now the project is stand alone (http://jxlayer.dev.java.net) and in version 3 was a major api change but the demo was not ported to the new JXLayer api.
I make some tries to port the TabbedPaneAnimationDemo to latest JXLayer, I failed, and I decide to implement the effect myself.
Here is my approach:
I have:
1. a container(a JXPanel that I named FadeEffectTransitionContainer) on with I set as layout a CardLayout. - This is the container that holds all wizard pages and the pages are switched with CardLayout.show().
2. and wizard pages that are also JXPanels.
The effect I wanted to be the same as that provided by TabbedPaneAnimationDemo: old wizard page disappear with diminishing alpha while the new page appear with increasing alpha.
For a while I was trying to override paint (or paintComponents) in main panel and from this method to call paint on child panels. I was not able to make it work in that way. So I will tell you how I finally make this effect.
When the page in wizard is about to change, I make 2 screenshots of the 2 panels involved in page transition:
private BufferedImage takePicture(JXPanel aPanel){ BufferedImage image = createBuffer(aPanel.getWidth(), aPanel.getHeight()); Graphics g = image.getGraphics(); aPanel.paint(g); g.dispose(); return image; } private BufferedImage createBuffer(int width, int height) { return new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); }
And this 2 images are painted on the FadeEffectTransitionContainer in paint(Graphics g). First the old panel(the picture of it) then the new panel with increasing alpha.
Here is a screen-shot:
I see that with some containers (JPanel, JXRadioGroup etc.) added in my pages, if I make them transparent (setOpaque(false)), I have some garbage on them when I make the images. So I make them opaque. And after that I realize that I don't need to make a diminishing alpha on the old page that disappear because the page that appear been opaque I get the same effect.
Here is the main container of the wizard:
FadeEffectTransitionContainer.java |
package ro.arhinet.scan.ui.effects.transition; import java.awt.AlphaComposite; import java.awt.Composite; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.image.BufferedImage; import java.math.BigDecimal; import org.jdesktop.swingx.JXPanel; /** * * @author Mihai Vasilache */ public class FadeEffectTransitionContainer extends JXPanel{ private boolean isPaintingEffects; public BufferedImage oldPanelPicture; public BufferedImage newPanelPicture; private BigDecimal alpha = new BigDecimal("1");; @Override public void paint(Graphics g){ if( ! isPaintingEffects){ super.paint(g); return; } eraseBackground(g); doCustomPaint(g); } private void eraseBackground(Graphics g){ g.setColor(getBackground()); g.fillRect(0, 0, getWidth(), getHeight()); } private void doCustomPaint(Graphics g_param){ //paint old panel with no alpha Graphics2D g2 = (Graphics2D) g_param.create(); g2.drawImage(oldPanelPicture, 0, 0, null); g2.dispose(); //paint the new panel on top of old panel //because of panels been opaque the old panel apear with dimishing alpha Graphics2D g3 = (Graphics2D) g_param.create(); setAlphaOnGraphics((Graphics2D)g3); g3.drawImage(newPanelPicture, 0, 0, null); g3.dispose(); } protected void setAlphaOnGraphics(Graphics2D g2) { BigDecimal negativeAlpha = new BigDecimal("1").subtract(alpha); Composite composite = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, negativeAlpha.floatValue()); g2.setComposite(composite); } public void startPaintingEffects(){ isPaintingEffects = true; } public void stopPaintingEffects(){ isPaintingEffects = false; } public void setOldPanelPicture(BufferedImage thePicture){ oldPanelPicture = thePicture; } public void setNewPanelPicture(BufferedImage thePicture){ newPanelPicture = thePicture; } public void setAlphaTransition(BigDecimal alpha) { this.alpha = alpha; } public BigDecimal getAlphaTransition() { return alpha; } }
And this is the class that controls the alpha and has a timer to apply the effect:
PanelChangeAnimator.java |
package ro.arhinet.scan.ui.effects.transition; import java.awt.Color; import java.awt.Graphics; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.image.BufferedImage; import java.math.BigDecimal; import javax.swing.Timer; import org.jdesktop.swingx.JXPanel; public class PanelChangeAnimator { private Timer timer; private float delta; private BigDecimal alpha = new BigDecimal("1"); JXPanel oldPanel; JXPanel newPanel; FadeEffectTransitionContainer effectsContainer; private static PanelChangeAnimator instanceRunning; public PanelChangeAnimator() { this(50, .05f); } private PanelChangeAnimator(int delay, final float delta) { setDelta(delta); timer = new Timer(delay, new ActionListener() { public void actionPerformed(ActionEvent e) { if (effectsContainer.getAlphaTransition().floatValue() == 0.0f) { timer.stop(); effectsContainer.stopPaintingEffects(); synchronized(PanelChangeAnimator.class){ instanceRunning = null; } return; } alpha = alpha.subtract(new BigDecimal(String.valueOf(delta))); BigDecimal negativeAlpha = new BigDecimal("1").subtract(alpha); effectsContainer.setAlphaTransition(alpha); //otherwise the child components of the new Panel (visible one //in CardLayout) paints themselfs with alpha=1 (on mouse over) newPanel.setAlpha(negativeAlpha.floatValue()); effectsContainer.repaint(); } }); } public void setDelta(float delta) { if (delta <= 0 || delta > 1) { throw new IllegalArgumentException(); } this.delta = delta; } private boolean stopPrevious(){ synchronized(PanelChangeAnimator.class){ if (instanceRunning == null){ instanceRunning = this; return false; } instanceRunning.timer.stop(); instanceRunning.effectsContainer.stopPaintingEffects(); instanceRunning.newPanel.setAlpha(1f); instanceRunning = this; return true; } } public void changePanels(JXPanel newPanel, JXPanel oldPanel, FadeEffectTransitionContainer container) { stopPrevious(); this.effectsContainer = container; this.oldPanel = oldPanel; this.newPanel = newPanel; this.alpha = new BigDecimal("1"); takePicturesOfPanels(); effectsContainer.setAlphaTransition(alpha); effectsContainer.startPaintingEffects(); effectsContainer.repaint(); timer.start(); } private void takePicturesOfPanels(){ boolean oldPanelOldVisible = oldPanel.isVisible(); float oldPanelOldAlpha = oldPanel.getAlpha(); oldPanel.setVisible(true); oldPanel.setBackground(Color.WHITE); oldPanel.setOpaque(true); oldPanel.setAlpha(1f); effectsContainer.setOldPanelPicture(takePicture(oldPanel)); oldPanel.setVisible(oldPanelOldVisible); oldPanel.setAlpha(oldPanelOldAlpha); boolean newPanelOldVisible = newPanel.isVisible(); float newPanelOldAlpha = newPanel.getAlpha(); newPanel.setVisible(true); newPanel.setBackground(Color.WHITE); newPanel.setOpaque(true); newPanel.setAlpha(1f); effectsContainer.setNewPanelPicture(takePicture(newPanel)); newPanel.setVisible(newPanelOldVisible); newPanel.setAlpha(newPanelOldAlpha); // try{ // String fname = System.currentTimeMillis() + ""; // ImageIO.write(effectsContainer.b1, "gif", new File("c:\\TEMP\\io\\" + fname + ".gif")); // ImageIO.write(effectsContainer.b2, "gif", new File("c:\\TEMP\\io\\" + fname + "_2.gif")); // }catch(Exception e){ // e.printStackTrace(); // throw new RuntimeException(e); // } } private BufferedImage takePicture(JXPanel aPanel){ BufferedImage image = createBuffer(aPanel.getWidth(), aPanel.getHeight()); Graphics g = image.getGraphics(); aPanel.paint(g); g.dispose(); return image; } protected BufferedImage createBuffer(int width, int height) { return new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); } }
And pages are changed with these lines of code:
((CardLayout) container.getLayout()).show(container, newPanelName); new PanelChangeAnimator().changePanels(newPanel, currentPanel, container);
And of course, the container is built in that way:
CardLayout cardLayout = new CardLayout(); container = new FadeEffectTransitionContainer(); container.setOpaque(true); container.setBackground(Color.WHITE); container.setLayout(cardLayout); .... container.add(aStepPanel, aStepName); ....
No comments:
Post a Comment