The Source for Java Technology Collaboration
User: Password:



Kirill Grouchnikov

Kirill Grouchnikov's Blog

Animating layouts part I - the teaser

Posted by kirillcool on September 19, 2006 at 12:02 AM | Comments (21)

This is going to be the first part of a series about automatically animated layouts in Swing applications. The work on animation effects in Substance has begun a few months ago with the following design goals:

  • Make all animations on a single thread
  • Animate all Swing core components without any change to the application code
  • Make the animation as configurable as possible
  • Keep the animation overhead to the minimum
The animation layer originally written for Substance and later moved into the laf-widget project required minimal changes to provide animations on lists, tables and trees (by the way, i'm still waiting to hear how these can be done in timingframework), and hadn't to be changed a single bit in order to produce the following animated layouts (click on a green play button to start each one of the movies - i'm still learning how to use Wink properly):

Note that the movies show the SLOW animation kind - the default animation rate is twice as fast, and the animation itself can be turned off completely. There are no changes done between the two movies besides setting the layout on the main panel from Border to Flow. The layout animation itself is done with one single like in the application code. The animation rate in the real application is much more consistent than in the movie due to upload size constraints. Enjoy the next parts.

Here is the application code:

import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.*;

import org.jvnet.lafwidget.LafWidget;
import org.jvnet.lafwidget.layout.TransitionLayoutManager;
import org.jvnet.lafwidget.utils.LafConstants.AnimationKind;

public class SampleFrame extends JFrame {
  JButton b1, b2, b3;

  public SampleFrame() {
    super("Transition test");

    this.getContentPane().setLayout(new BorderLayout());

    JPanel buttons = new JPanel(new FlowLayout());
    this.getContentPane().add(buttons, BorderLayout.SOUTH);

    // for the first movie, change the following line to 
    // use the BorderLayout
    final JPanel mainPanel = new JPanel(new FlowLayout());
    // bring the magic in one single line
    TransitionLayoutManager.getInstance().track(mainPanel);
    this.b1 = new JButton("1");
    this.b2 = new JButton("2");
    this.b3 = new JButton("3");
    
    final JButton add = new JButton("add");
    add.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e) {
        SwingUtilities.invokeLater(new Runnable() {
          public void run() {
            mainPanel.add(b1, BorderLayout.WEST);
            mainPanel.add(b2, BorderLayout.CENTER);
            mainPanel.add(b3, BorderLayout.EAST);
            mainPanel.revalidate();
            add.setEnabled(false);
          }
        });
      }
    });
    buttons.add(add);

    this.getContentPane().add(mainPanel, BorderLayout.CENTER);

    final JCheckBox cb1 = new JCheckBox("1");
    cb1.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e) {
        b1.setVisible(!b1.isVisible());
        mainPanel.revalidate();
      }
    });
    buttons.add(cb1);

    final JCheckBox cb2 = new JCheckBox("2");
    cb2.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e) {
        b2.setVisible(!b2.isVisible());
        mainPanel.revalidate();
      }
    });
    buttons.add(cb2);

    final JCheckBox cb3 = new JCheckBox("3");
    cb3.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e) {
        b3.setVisible(!b3.isVisible());
        mainPanel.revalidate();
      }
    });
    buttons.add(cb3);

    final JCheckBox cb13 = new JCheckBox("13");
    cb13.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e) {
        b1.setVisible(!b1.isVisible());
        b3.setVisible(!b3.isVisible());
        mainPanel.revalidate();
      }
    });
    buttons.add(cb13);

    this.setSize(300100);
    this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    this.setLocationRelativeTo(null);

    mainPanel.putClientProperty(LafWidget.ANIMATION_KIND,
        AnimationKind.SLOW);
  }

  public static void main(String[] args) {
    SwingUtilities.invokeLater(new Runnable() {
      public void run() {
        new SampleFrame().setVisible(true);
      }
    });
  }
}

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

  • Out of curiocity, what is the practical use of these animations?

    Posted by: euxx on September 19, 2006 at 01:21 AM

  • seriously Kirill, WHEN is the Swing team going to hire you? Thought it would have happened by now...

    Posted by: benloud on September 19, 2006 at 03:44 AM

  • This is fantastic! I love it! Congratulations! Keep the good work! Euxx, the practical use of animations? Why do you think Adobe Flex is making so much success? Rich Internet Applications are popular now because of this fency animations and effects. These features will give Swing a lot of strength agaist Flex. That's already a good practical use of it ;-)

    Posted by: vecmath on September 19, 2006 at 07:54 AM

  • Excellent work krill,
    looking forward to use it sometime. Most Apps may not need this, but
    those who need will benefit from your work. Of course the Swing
    Team should hire you ;)
    jens

    Posted by: mac_systems on September 19, 2006 at 07:55 AM

  • euxx: You can see such animations in Aerith (aerith.dev.java.net) when you go from one screen to another. Sometimes it uses fade to black, sometimes cross fade. The result just looks more natural and more pleasing. Aerith also contains an extension to the Timing Framework that allows one to perform those animated layout transitions.

    Posted by: gfx on September 19, 2006 at 08:01 AM

  • The practical use can be debated, I think. Movement is best suited for objects in the peripheral vision (or for highlighting important events, like screen or information changes. Though the idea of an 'important' event is going to very from one developers taste to another). I think that selection animations should be used sparingly if at all. Its interesting that animations have become widespread enough that even Yahoo! has a UI library based on them.

    Posted by: wsnyder6 on September 19, 2006 at 08:15 AM

  • I wished you'd call them something other than "animations", since animation implies movement. I saw this thread thinking "hey, that sounds cool" but was rather disappointed.
    • If it's not moving, but the mouse is, isn't that a mouse-over or rollover effect?
    • If the whole display is changing, isn't that more aptly described as a "transition"?
    These fade-in/fade-out rollover effects to me look more like an LCD with a really slow refresh rate.

    BTW, this is what I think of when you talk about component animation.

    Posted by: twalljava on September 19, 2006 at 08:37 AM

  • Oops, sorry about that, that last comment was intended for Kirill's previous post.

    Posted by: twalljava on September 19, 2006 at 08:39 AM

  • twalljava,

    The layout itself is called TransitionLayout and not AnimationLayout. The title of the blog entry may be somewhat misleading, but in most cases this layout would involve some kind of animation (components moving because the container was resized, components moving because other components were hidden / removed). But it's just a word for us the developers - it doesn't matter to the end user.

    Posted by: kirillcool on September 19, 2006 at 08:46 AM

  • by the way, i'm still waiting to hear how these can be done in timingframework

    TimingFramework is a, well, a timing framework. Anytime you have animation work to do, the TimingFramework is a suitable framework to use. Why wouldn't it be suitable for any of the animations here or in your previous post?

    Animating cell renderers on rollover is not a hard problem, as long as either the component (which you are probably doing via the UI delegate) or the cell renderer know that they should reference some external registry (say, a Map) to determine the background color to use. The TimingFramework simply animates the color in that registry. The cell renderer is completely unaware that anything is being animated at all. The only additional task the TimingTarget of the TimingFramework must do is cause a repaint of the particular cell in question.

    Posted by: rbair on September 19, 2006 at 08:54 AM

  • Richard,

    But that's exactly the problem - how does the cell renderer know that it's being rolled over? In most cases, the cell renderer returns the same component for all the model entries (with some changes to account for selected status). So, even if the renderer references that external registry (which would require changes to the application code specifically for animations), how does it know to look at the "animated" entry? It can't unless the renderer knows that it's being in the rollover state.

    Things get even more complex in table renderers (x+y ID) and tree renderers (path ID). And to your last remark - yet more code to the application side for these animations.

    I'm not saying that it can't be done, but it's most certianly not 3 lines of code.

    Posted by: kirillcool on September 19, 2006 at 09:00 AM

  • Absolutely awesome!

    These effects rule. They are pleasing to the eye, and they help users understand what just happend. I have a login frame that would benefit greatly from these transition effects. Great work!

    -Bryan

    Posted by: prime21 on September 19, 2006 at 09:42 AM

  • Kirill

    I think you are missing the point. What you are arguing against is writing the animation effects in the client code. I agree! I don't want to write this in the client. I want support in the LAFs. But what does that have to do with TimingFramework? The point is, you don't need a different animation library. TimingFramework does what you (the LAF author) want. That's my point.

    how does it know to look at the "animated" entry?

    It doesn't, that's the point. Every time a cell renderer is called, it would go to this map, which is keyed by index (or tree path, or row/col). If an entry is in this map, it gets the Color and uses that. If it isn't in the map, then it paints the normal background color for the cell. The mouse listener on the table/tree/list is responsible for determining when a cell is moused over. It then adds the cell idnex/treepath/row/col to the map and starts the animation on that map entry's value.

    But this is beside the point. It doesn't matter what the implementation is since ideally it would be hidden by the LAF. When it isn't, you can *still* get what you want with TimingFramework, without having to write a lot of complicated code (hey, we've all written cell renderers and Maps, not that big of a deal). I'd love to see Synth support animations directly in the synth file too. (BTW: Synth allows you to do Java2D code as well -- it isn't all about images!)

    Posted by: rbair on September 19, 2006 at 09:53 AM

  • Kirill

    i'm still waiting to hear how these can be done in timingframework

    The reason the timing framework doesn't support Swing-specific animations (in fact, that it doesn't really have any knowledge of Swing at all) is that it was built from the ground up to enable increasing levels of timing and animation capabilities in general. The first rev was simply basic timing utilities that went beyond the built-in Timer classes. The next rev (the one currently posted) adds the ability to animate JavaBean properties (which can be used to animate Swing components, but are not Swing-specific), animate in a non-linear/keyframe fashion (good for more complex animations), and trigger animations from various application events.

    That's as far as the framework's gotten to date ... but that's not to say that it shouldn't go farther. In fact, one of the reasons I started it in the first place was to make animated GUIs easier. I've just been building up the infrastructure of the basic timing utilities first. Now it's in a state where I can begin to think of tying it more closely to Swing and GUI animation specifically.

    The animated transition stuff in Aerith is a good example of one of the ways to go here, although there are other directions I'd like to take it that tie more closely to individual component animations/transitions.

    So back to your original point about the timing framework: There's no reason that the framework can't support more Swing/GUI-specific animations. You can do it now, and hopefully the current facilities make it a whole lot easier than it is with the built-in timer facilities. But hopefully, we'll continue adding to the framework to make it even easier in the future.

    Posted by: chet on September 19, 2006 at 10:11 AM

  • Richard,

    Indeed i'm arguing about imposing the burden of animations on the client code - these should be done at the LAF level (like in Windows under Vista in Mustang). However, the example that you provide is still client-side. Having this map in the cell renderer (keyed by row / row+column / path) requires change in the client cell renderer to use this map and couples it to the specific LAF usage of the animation framework: LAF will have to update the entries in that map and the client code (cell renderer) will have to use the exact same keys to retrieve the background (for example). Where is this map stored? If it's in the cell renderer, you'll have to handle all the animations in the client code (how will LAF know about this map). If it's in the LAF, you tie your cell renderers to the specific LAF.

    Chet,

    The Timing Framework is indeed a general timing framework, and as such, requires a few tweaks to use it in Swing applications transparently. I'm hoping to see more work invested in this area - perhaps consider using Timing Framework on the core LAFs in Dolphin instead of the home-grown Vista emulations as done in Mustang?

    Posted by: kirillcool on September 19, 2006 at 11:27 AM

  • Kirill

    Of course I showed client side code. It's the clearest way to explain to people how the animations could be done.

    Posted by: rbair on September 19, 2006 at 12:42 PM

  • Richard,

    How this can be done in a LAF then (using the Timing Framework approach of animating properties) without changing the logic of the client cell renderer? Seems i can't get an answer to this question...

    Posted by: kirillcool on September 19, 2006 at 12:50 PM

  • gotta love a debate amongst the big names in Swing!

    Posted by: benloud on September 19, 2006 at 11:49 PM

  • Kirillcool, I was have for the past few days, been look for this type of animation in swing. Thank you so very much. I have been googling things like "java + animation" and "java + flash", and to my surprise, I see your article on dzone. This is the right course to a richer client experience. Many thanks..

    Posted by: nathan_clark80 on September 20, 2006 at 07:17 AM

  • Hi Kirill,

    As Bryan said it helps the user to undertand "what just happend".

    I like these(Flash kind) of Effects, animations these are new to the Java world.Looks Beautiful.

    GreatWork,

    Kishore.

    Posted by: kishoresjava on September 26, 2006 at 12:25 AM

  • I found this to be an interesting effect, that I have made use of. However Using the Latest release *3.1* of laf-widget.jar, the // bring the magic in one single line TransitionLayoutManager.getInstance().track(mainPanel); ought to read // bring in the even more magical single line TransitionLayoutManager.getInstance().track(mainPanel, true); Best regards Skjalg

    Posted by: skjalgb on October 22, 2007 at 08:35 AM





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