I recently watched the interesting Screencast by Adobe's James Ward, entitled "Web 2.0 Design Trend & Tools". It shows off some of the stuff Adobe are doing with Flash and Flex to make them better suited for the Rich Internet Applications space, and I have to say I found what I saw quite impressive.
RIA's seem to be the buzzword of the moment, now that the shine has started to come off Ajax. Developers are starting to ask why they can't have the power of a desktop interface, coupled with the convenience of a web app. And while Adobe has recently bolstered its animation centric technology with data handling capabilities, Sun has been busy beefing up Java's desktop and graphics support.
But the one thing that Swing still lacks is eye candy. Swishy, bouncy, scrolly stuff. Now personally I can't stand it. The first thing I do when faced with a fresh install of Windows is to switch off the GUI effects (although, paradoxically, I actually quite like the OS X effects!) But the Flex demo got me wondering: just how easy would it be to add swishy, bouncy, scrolly stuff to a standard Swing GUI?
Throughout the first part of the Flex demo there's a tab bar with a subtle animation effect, and I figured it would be interesting to find out just how much work is actually involved in duplicating this kind of effect in Swing. Then I thought "sod it, in for a penny, in for a pound" — why not just turn the eye candy knob right up to eleven, and go all out for as much bling as I could possibly manage without reaching for a sick bag?
These Webstart demos require permissions to run because one of the components used is a file chooser, which needs access to the local file store. The demo has ABSOLUTELY NO FUNCTION WHAT-SO-EVER, the UI is merely for demonstration and has no underlying functionality.
The above two demos are identical in all but 'skin'. No 'Swing bling' would be complete without a custom PLaF, and my demo is no exception. The darker GUI has a high contrast colour scheme which better shows off the transition effects — however the black UI is incomplete (as you'll see from the missing icons) and isn't particularly attractive either, so the Metal/Ocean version has been provided too.
JWizardPane is an experimental component which works like a hybrid between a tabbed pane and a wizard. There's a lot of rough edges, but hopefully it looks suitably cute to make a modest impact (although in the right hands it could probably be even better.)
The good news, I discovered, is that Swing gives you most of the right tools to create some nice transition effects, it just expects you to do all the donkey work of putting them together. To add a bit of challenge I decided to code for Tiger, rather than Mustang. This was for three practical reasons: (1) using the latest and greatest of everything seemed like cheating, particularly as I doubt every user has upgraded yet; (2) I'm still exploring some of the new stuff in 6.0; and (3) if I'd started messing about with JOGL and Swing I'd never have known when to stop :)
The first problem I encountered was movement. Rigid stop/start movements look shoddy, and I wanted my transitions to accelerate and decelerate. It quickly became clear this would require a lot of duplicated boiler-plate code, so I crafted a quick-n-dirty library. There's at least one animation API out there already, I know, but I needed something simple, fast, and movement centric (rather than time centric.)
Velocity interface defines movement along hypothetical line across an undefined timescale. To translate this to co-ordinates on a concrete line we assign the
Velocity to an instance of
Path. For example, to mimic a car pulling away from a traffic signal at a junction,
Velocity would handle the acceleration, while
Path would determine whether the car turned left, right, or carried straight on. I wrote wrapper instances which allowed several velocities and paths to be concatenated into a single movement, which meant movements could be built up from simple building blocks. Finally, an
AnimationController interpolates the movement across a concrete timescale, or alternatively at a steady beat (handy if you want to capture your output for later playback.)
If that sounds complex, believe me, the implementation is blissfully simple. Writing animations becomes a case of saying "I want to move from this point, to this point, speeding up then slowing down" and launching a Swing timer to wake every so often, ask the controller how far into the animation we are, query the path for the related co-ordinates, before updating the component.
The next problem I had was a sudden ramp up in CPU whenever the transition effects occurred. This had me scratching my head for half an hour, but the answer was painfully obvious: if components aren't opaque, Swing attempted to paint their parent container, which (it seemed) invalidated all the child components which are siblings of the original. A schoolboy error — ho-hum!
Even so, the demo still gobbles a little CPU, but only for the briefest of moments while the animations are in effect. I've no doubt more experienced !ahem! 'Swinger' could improve the performance further.
Apart from those two issues, writing the transition effect was actually quite straight forward. Indeed, the custom Synth PLaF I wrote to showcase the effects actually gave me far far more hassle!
For those of you who haven't worked it out, here's how the UI works:
- The top bar is a custom component, using a sequenced soft in/out velocity with a
MemoryPathto allow me to add streams of colour when the bar retracts (
MemoryPathis a wrapper which keeps a record of where its been, allowing the animation to query not just for the current co-ords, but previous co-ords in the animation.)
- The central part is a bog standard AWT CardLayout and Swing JPanel, which has been 'supercharged' thanks to some trickery involving an extra 'card' to capture a transition's incoming/outgoing cards and perform a simple little soft in/out shuffle with alpha. Kid's stuff really.
- The lower part is just over-the-top eye candy — and it breaks the UI because one can't quickly flip through the panels as the buttons are unusable while animating — but I had the idea in my mind, so I just had to try implementing it! Using the same technique as the central pane, a
CardLayoutwith extra panel grabs the images of the buttons and move/fades them. The buttons cannot be clicked during the animation because they are literally not on the screen when the animating 'card' is shown.
The source code is a little chaotic, and contains a number of short cut hacks (mainly for the PLaF) but if anyone really wants it I'll post it someplace, perhaps.