O'Reilly Hacks
oreilly.comO'Reilly NetworkSafari BookshelfConferences Sign In/My Account | View Cart   
Book List Learning Lab PDFs O'Reilly Gear Newsletters Press Room Jobs  


 
Buy the book!
Swing Hacks
By Joshua Marinacci, Chris Adamson
June 2005
More Info

HACK
#8
Animate Transitions Between Tabs
This hack shows how to create animated transitions that play whenever the user switches tabs on a .
[Discuss (0) | Link to this hack]

One of Swing's great strengths is that you can hack into virtually anything. In particular, I love making changes to a component's painting code. The ability to do this is one of the reasons I prefer Swing over SWT. Swing gives me the freedom to create completely new UI concepts, such as transitions.

With the standard paint methods, Swing provides most of what you will need to build the transitions. You will have to put together three additional things, however. First, you need to find out when the user actually clicked on a tab to start a transition. Next, you need a thread to control the animation. Finally, since some animations might fade between the old and new tabs, you need a way to provide images of both tabs at the same time. With those three things, you can build any animation you desire.

Scheduling the Animation

Since the pane was added as a ChangeListener to itself, the stateChanged( ) method will be called whenever the user switches tabs. This is the best place to start the animation thread. Once started, the thread will capture the previous tab into a buffer, loop through the animation, and control the repaint speed:

	// threading code
	public void stateChanged(ChangeEvent evt) {
		new Thread(this).start();
	}

	protected int step;
	protected BufferedImage buf = null;
	protected int previous_tab = -1;

	public void run() {
		step = 0;

	// save the previous tab
	if(previous_tab != -1) {
		Component comp = this.getComponentAt(previous_tab);
		buf = new BufferedImage(comp.getWidth(),

			comp.getHeight(),
			BufferedImage.TYPE_4BYTE_ABGR);
		comp.paint(buf.getGraphics());
	}

Notice that the run( ) method grabs the previous tab component only when the previous_tab index isn't -1. The component will always have a valid value, except for the first time the pane is shown on screen, but that's OK because the user won't have really switched from anything anyway. If there is a previous tab, then the code grabs the component and paints it into a buffer image.

WARNING

It's important to note that this is not thread-safe because the code is being executed on a custom thread, not the Swing thread. However, since the tab is about to be hidden anyway—and, in fact, the next real paint() call will only draw the new tab—you shouldn't have any problems. Any changes introduced by this extra paint() call won't show up on screen.

With the previous component safely saved away, you can now loop through the animation:

	for(int i=0; i<animation_length; i++) {
	step = i;
	repaint();
	try {

		Thread.currentThread().sleep(100);
	} catch (Exception ex) {
		p("ex: " + ex);
	}
}

	step = -1;
	previous_tab = this.getSelectedIndex();
	repaint();

This code shows a basic animation loop from 1 to N, with a 100-millisecond duration for each frame.

TIP

A more sophisticated version of the code could have dynamic frame rates to adjust for system speed.

Once the transition finishes, the animation step is set back to -1, the previous tab is stored, and the screen is repainted one last time, without the transition effects.

Drawing the Animation

The TransitionTabbedPane is now set up with the proper resources and repaints, but it still isn't drawing the animation. Because the animation is going to partially or completely obscure the tabs underneath, the best place to draw is right after the children are painted:

	public void paintChildren(Graphics g) {
		super.paintChildren(g);

		if(step != -1) {
			Rectangle size = this.getComponentAt(0).getBounds();
			Graphics2D g2 = (Graphics2D)g;
			paintTransition(g2, step, size, buf);
		}
	}

	public void paintTransition(Graphics2D g2, int step,
		Rectangle size, Image prev) {
	}

This code puts all of the custom drawing into the paintTransition( ) method, currently empty. It will only be called if step isn't -1, meaning during a transition animation. The paintTransition( ) method provides the drawing canvas, the current animation step, the size and position of the content area (excluding the tabs themselves), and the image buffer that stores the previous tab's content. By putting all of this in a single method, subclasses can build their own animations very easily. is a simple transition with a white rectangle that grows out of the center, filling the screen, then shrinking again to reveal the new tab content.

InOutPane implements only the paintTransition( ) method, leaving all of the harder tasks to the parent class. First, it determines how long the animation will be, and then it calculates an offset to grow and shrink the white rectangle. If the drawing process is currently in the first half of the animation (step < half), then it draws the previous tab below the rectangle, creating the illusion that old tab content is still really on screen with the rectangle growing above it. For the second half of the animation, it just draws the rectangle, letting the real tab (the new one) shine through as the rectangle shrinks.

Another Example

Because TransitionTabbedPane makes it so easy to build new animations, I thought I'd add another one. This is the old venetian blinds effect, where vertical bars cover the old screen and uncover the new one; puts it together.

Just like InOutPane, VenetianPane selectively draws the old tab and then calculates the placement of animated rectangles. In this case, there is a blind rectangle that spans the entire screen from top to bottom, but has the width of the current step. As a result of the step growing, this rectangle gets bigger with each frame. For the second half of the animation, it shrinks and moves to the right, making it appear to fade into nothing. Once the blind is calculated, VenetianPane draws the blind multiple times to cover the entire tab content area, creating the effect seen in .

Figure 3. Tab transition with a venetian blinds effect

This hack is quite extensible. With the power of Java2D you could add translucency, blurs, OS X-like genie effects, or anything else you can dream up. As a future enhancement, you could include more animation settings to control the frame rate and transition time. If you do create more, please post them on the Web for others to share.


O'Reilly Home | Privacy Policy

© 2007 O'Reilly Media, Inc.
Website: | Customer Service: | Book issues:

All trademarks and registered trademarks appearing on oreilly.com are the property of their respective owners.