The Source for Java Technology Collaboration
User: Password:



Simon Morris

Simon Morris's Blog

Swing Bling

Posted by javakiddy on February 23, 2007 at 03:22 AM | Comments (28)

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?

Wizard_black.png Wizard_ocean.png
Wizard_ocean.png Wizard_ocean.png

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.)

The 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 MemoryPath to allow me to add streams of colour when the bar retracts (MemoryPath is 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 CardLayout with 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.


Bookmark blog post: del.icio.us del.icio.us Digg Digg DZone DZone Furl Furl Reddit Reddit
Comments
Comments are listed in date ascending order (oldest first) | Post Comment

  • Excellent! Have you looked at Chet's TimingFramework and AnimatedTransitions libraries? Kirill also has something similar.

    Posted by: gfx on February 23, 2007 at 04:44 AM

  • Very nice :-) Just showed it to the team who create software installation packs where I work, and they were "impressed"... understatement..!

    Posted by: chris_e_brown on February 23, 2007 at 04:56 AM

  • "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." Simon, can you show us how you build the movements? That sounds really interesting.

    Posted by: gfx on February 23, 2007 at 05:50 AM

  • Really great! I'd love a online repository of such custom swing components or simple a site with links to them..

    Posted by: zero on February 23, 2007 at 06:14 AM

  • @gfx: the mini-API is actually very very trivial (and therefore fast!) It focuses on movement, not timing (you need to handle the timing yourself) and all it does it deliver x/y co-ords across a given line from 0.0 to 1.0 ...

    I wrote a quick app with a custom Swing component to test the code, which looked kind of like a heart monitor screen. Here's the code which delivers x/y positions running horizontally back/forward across the screen, with up to nine 'trailing' co-ords behind it. When plotted by the custom component the effect is like the red light on the Knight Rider car. (Excuse the rather eccentric formatting :)

    // Knight Rider special
    // Create a velocity which accelerates then decelerates.
    SequencedVelocity seqVel = new SequencedVelocity
    (   new SoftAccelerationVelocity(),
        new SoftDecelerationVelocity()
    );
    // Create a path which travels forewards then backwards across the screen
    // and wrap it to retain a trail of past co-ordinates.
    Path path = new MemoryPath
    (   new SequencedPath
        (   new StraightLinePath(0,0, width,0 , seqVel) ,
            new StraightLinePath(width,0, 0,0 , seqVel)
        )
        ,10 
    );
    

    Posted by: javakiddy on February 23, 2007 at 06:30 AM

  • And as an afterthought, here's how you actually use the path with an AnimationController (which, again, is a very very lightweight class which just keeps track of time.)

    // Create a timer for a given duration
    anim = new AnimationController(ANIM_DURATION);
    // When the animation starts, log the current time
    anim.start();
    :
    :
    :
    // This stuff should run whenever your timer (java.util.Timer or 
    // javax.swing.Timer) runs
    double time = anim.getMoment();
    if(time>1.0d)
    {   // The animation has ended.  Clean up now!
    }
    else
    {   // Do next frame...
        // Get the next coords
        double d[] = path.getCoords(time);
        // Draw your stuff...
    }
    

    Posted by: javakiddy on February 23, 2007 at 06:40 AM

  • Hi Simon,

    Can you post the code somewhere, please!!!

    Raheem

    Posted by: rrufai on February 23, 2007 at 07:07 AM

  • @javakiddy: That's a very interesting approach. I especially like the trails automation (nice reference by the way ;-)

    Posted by: gfx on February 23, 2007 at 10:20 AM

  • Great demo, but I would like to point out that when using dual monitor dragging the application to the second monitor breaks it. Most likely due to the fact that (typically) the second monitor is not 3d accelerated.

    Posted by: atehrani on February 23, 2007 at 10:51 AM

  • @gfx: Well I'm glad you thought it was okay, Romain -- praise indeed coming from someone like yourself. Chet's framework is really really cool, but I wanted something which was quite lightweight and focused on just tracking movement over a period of time, rather than a full animation framework with forward/reverse and loop control, etc, etc. It keeps the actual code which executes per frame down to a handful of instructions, and the ultra simple route makes it easier to extend for bespoke/novelty functionality like the memory paths.

    (Btw: I also wrote a Path called DrunkenLinePath -- I'm pretty sure you can guess what that did. I substituted it for the StraightLinePath used in the main panel, and got rather sea-sick watching the cards wibble violently in and out of view.)

    Not heard about the other two APIs. I'll check them out. Thanks for the heads up.

    @rrufai: Sure. Can you wait until after the weekend?

    @atehrani: I don't want to pass the buck, but that might be an AWT issue, not something specific to the demo. What do you mean by "breaks" -- throws an exception (if so what?) Does it break when you start it on the unaccelerated display, or whenever it is moved between displays?

    Posted by: javakiddy on February 23, 2007 at 12:09 PM

  • @javakiddy: The AnimTrans framework is not totally public yet. You can find an early version of it at aerith.dev.java.net. This framework is used to automatically handle transitions between screens, but it's more generic than simple moves. It's based on effect (scale, motion, fade, ripple, whatever) rather than movement. That's why I find your approach very interesting. I can't wait to play with your stuff :)

    Posted by: gfx on February 23, 2007 at 04:30 PM

  • Simon: Very cool!

    To clarify: he intent of the Timing Framework is not an end-all for Swing animations, but rather the fundamentals of a more functional/useful timing engine to support animations.

    Given that framework a building-block, I hope to make it easier to build other, easier means of animation on top of it, like the AnimTrans stuff Romain mentioned (earlier version in Aerith, current version published somewhere around when the book comes out), like other more component-focused animations, or like what you've done. So noone has to worry about the timing details, they can just focus on animations instead. Hopefully all Swing animation can get easier and there can be more apps with cool effects...

    Posted by: chet on February 24, 2007 at 06:31 AM

  • @chet: Yeah, I did consider your framework for my demo, but in terms of timing I needed only the most basic of functionality.

    @gfx: oow 'eck! I'd better clean the source up then :) Seriously though, I think you'll be surprised at just how little code there is in some of those classes. The API just hides some simple maths behind interfaces, so they can be combined and/or swapped in and out easily.

    Posted by: javakiddy on February 24, 2007 at 02:21 PM

  • Okay, the source is up. I tweaked it a little to add a new effect to the top bar (read the source to learn how to switch it off.) The movement API is in its own directory.

    Download zip file.

    Posted by: javakiddy on February 26, 2007 at 02:53 AM

  • Just made a minor edit to the GravityBounceVelocity class, to make it slightly less sucky! I've uploaded a fresh copy of the zip over the original, so if you fetched the source before this message, you might want to download it again.

    Posted by: javakiddy on February 26, 2007 at 06:32 AM

  • Very interesting. Those panel sliding animations really enhance the feel. Nevertheless, i am having problems. animations sometimes don't occur, top bar disappears. i'm using jdk1.6.0_01 under xp and this occured with both demos.

    Posted by: pepe on February 26, 2007 at 08:20 AM

  • The way the demo currently works, clicking back or forward causes the contents to exit to one side and be replaced from the same side. This seems wrong. Back should cause components to exit to the right and be replaced from the left. Forward should do the reverse. That way the bling serves aiding user orientation more.

    Posted by: coxcu on February 26, 2007 at 10:23 AM

  • Very nice job. One general comment about these sorts of effects as regards Swing: it seems very easy to create effects that will end up with jerky/non-smooth motion, at least that's my experience. As far as I can tell, Swing is just very sensitive to pauses in certain parts of the code. Sorry, it's late, am not being clear. What I mean to say is, if we want Swing "bling" to really impress people, it has to be really portable and work nearly perfectly on most platforms. You can imagine how annoying Mac OSX effects would be if they weren't as smooth as they are...I try out as many of the Swing demos as I can find, and in about 100% of the cases where there is animation or FX going on, there's usually some jerkiness in the animation. In your demo, it occurs on my machine when flipping back and forth between the panels quickly; I see it on the slide-in motion for the main panel, and occasionally on the fill-in for the top bar. It happens infrequently enough that I think it may be GC related, but makes me think, once again, the goal should be to raise the bar, but not too high. Thanks for sharing this, it's nice work. Cheers Patrick

    Posted by: pdoubleya on February 26, 2007 at 12:27 PM

  • This looks great - but, alas, I have the same problem Bruce Eckel described in his blog a few weeks ago - WebStart doesn't start. This is so frustrating - I switched to JDK 6 preview on a Mac, because software I use work better on JDK 6. But now on occasion some WebStart applications don't work. WebStart fires up, and then nothing happens - no error messages, nothing. I know I shouldn't use "preview" software - but there is no JDK 6 final for the Mac, and some other WebStart demos on java.net, for instance, require JDK 6. So I would agree that Swing needs some pizazz - but let us solve first the most important problem, and make Swing run across all supported platforms well.

    Posted by: fsommers on February 26, 2007 at 04:51 PM

  • @fsommers: Seriously, blame Apple for that. I'm using their Java SE 6 preview as my main JVM and it keeps switching itself back to 1.4 or 1.5 as the default VM from time to time. And I recently had to reinstall it because it just stopped running WebStart apps. Was Swing the culprit? Nope.

    Posted by: gfx on February 26, 2007 at 05:05 PM

  • Yay! At last an error which isn't a bug in my code! :)

    Posted by: javakiddy on February 27, 2007 at 02:05 AM

  • gfx, you're right, but blaming Apple doesn't solve our problem. I mean, most of potential Swing users, project managers, decision makers, etc... won't be satisfied by the "blame Apple" sentence and will just drop Swing. So this IS a problem of ours. What can we do? Well, at the minimum we should try to prevent that silent failure, which is very annoying and delivering a feeling of instability, immaturity of the technology. Being able to pop up a dialog box (maybe with "I won't work, blame Apple for this!" ;-) would be a great improvement. I think that in most cases this could be done by putting a big try / catch (Throwable) around the main() method. For instance, can we try with Simon's demo to see whether it's possible? fsommers, can you retry running the demo and look at some logs to see what's happening? Simon, could you try to add the try / catch and see whether it catches the problem?

    Posted by: fabriziogiudici on February 27, 2007 at 02:32 AM

  • About the supercharged card layout, this project: https://animatingcardlayout.dev.java.net/ does it the same way: snapshot in an image the before and after and then animated from one to the other by fading, rotating (I like that one) or whatever. I attempted to add a J3D rotate (which would be way more smooth) but was not able yet to get it 100% right.

    Posted by: tbee on February 27, 2007 at 03:14 AM

  • @fabriziogiudici:

    Putting a try/catch around main() is unlikely to do much good, as most of the work occurs away from the main thread, and if its WebStart itself which is to blame then the fault may manifest before my main() is even called. (Does main() even get called from JWS? :)

    A link to the source was posted in a comment above (dated 2.53AM 26th Feb), and it really isn't that big. The bootstrap code is in boot.Boot (where else!?!) and all you'd need to do is download a Jar and a corresponding JNLP, modify the JNLP to use a file:// URL, and give it a try on a Mac or other 'suspect' desktop machine.

    WebStart is quite hard to debug: for example when I first moved the application over to use WebStart it failed to display -- all the usual JWS banners came up, but then the app simple shut down without a GUI. Seems like there was an exception being thrown on the SWT when the GUI was built (SwingUtilities.invokeAndWait()) which I was (lazily) doing a printStackTrace() on. But of course JWS seems to just swallow stderr, which is probably a wise move in an end-user environment, but rather infuriating in a developer environment.

    Posted by: javakiddy on February 27, 2007 at 04:07 AM

  • I have the same problem as atehrani with dual monitors. It does not display the fancy sliding bar at all on my second monitor. However, the buttons still function if you click around in the empty area. If I move it back to my primary, it will show up when the window needs to be repainted. It's pretty slick regardless.

    Posted by: jonaqua on February 27, 2007 at 10:23 AM

  • @javakiddy: Well, I suppose that JWS does call main() as I've written at least a couple of applications in that way ;-) In any case, with "try / catch arount main()" I was generally meaning to check for troubles in the initalization, including code in an invokeAndWait(), instead of just printing the stack trace, since as you said it isn't easy to find where it goes (it's possible, but this is not the point: most of people at this point get a bad impression of the technology and don't even think about searching for the missing log). Yes, this doesn't catch JWS errors, but the most typical error I've experienced (in just a few JWS apps) is some missing resource (ClassNotFoundError or missing property files, etc...) or access control exceptions, all stuff that could be easily traced. BTW the same holds for Applets - I've just got an issue raised by a customer of mine who's using an applet and fortunately I popped up an error dialog box instead of silently failing.

    Posted by: fabriziogiudici on February 28, 2007 at 02:02 AM

  • I am having some trouble creating and running a jar file from the source code. even though it creates the jar file, running it throws this error: java.lang.reflect.InvocationTargetException at java.awt.EventQueue.invokeAndWait(Unknown Source) at javax.swing.SwingUtilities.invokeAndWait(Unknown Source) at boot.Boot.(Boot.java:29) at boot.Boot.main(Boot.java:84) Caused by: java.lang.NoClassDefFoundError: movement/Velocity at boot.Boot._initGUI(Boot.java:62) anyone have any ideas how to solve this?

    Posted by: andres625 on April 12, 2007 at 11:52 AM

  • The Movement API and the Bling demos are two separate projects in their own directories. Bling uses Movement, so for convenience I'd included the compiled Jar from Movement in the Bling directory. Check it's still there, and your compiled Wizball.jar refers to it.

    Posted by: javakiddy on April 13, 2007 at 08:51 AM



Only logged in users may post comments. Login Here.


Powered by
Movable Type 3.01D
 Feed java.net RSS Feeds