...then someone comes along and trumps it.
Chet Haase has blogged on the new animation features in the SceneGraph project being created at https://scenegraph.dev.java.net/. A new and improved version of the Timing Framework...and just as I thought I'd mastered the previous incarnation.
Well, they say you can't stop progress...
Here's the link to Chet's blog entry
Showing posts with label Swing. Show all posts
Showing posts with label Swing. Show all posts
Monday, January 7, 2008
Tuesday, December 4, 2007
Timing Framework (part 2) - Triggers
In the last blog, I showed a simple demonstration of how the Timing Framework can help reduce the amount of boilerplate code required to code animations. In this entry I'll show how to use triggers to reduce this even further.
In the last entry we used code like this to start an animation when a button was clicked.
We can simplify the above code using an action trigger which corresponds to ActionEvents and so is perfect to use with a button. The trigger may be instantiated directly by client code but of more convenience is the static addTrigger method which also takes the object on which the trigger should operate.
For example, to add an action trigger to the button we'd do the following:
Currently the framework provides four trigger implementations:
Next time I'll take this a step further and use a property setter to simplify the code even more.
In the last entry we used code like this to start an animation when a button was clicked.
button.addActionListener(new ActionListener() {The Timing Framework provides triggers which extend the basic functionality of the framework. The framework provides a number of trigger classes which can trigger animations.
public void actionPerformed(ActionEvent e) {
Animator anim = new Animator(2000, 5,
Animator.RepeatBehavior.REVERSE, new ButtonTimingTarget());
anim.setAcceleration(0.4f);
anim.setDeceleration(0.4f);
anim.start();
}
});
We can simplify the above code using an action trigger which corresponds to ActionEvents and so is perfect to use with a button. The trigger may be instantiated directly by client code but of more convenience is the static addTrigger method which also takes the object on which the trigger should operate.
For example, to add an action trigger to the button we'd do the following:
Animator anim = new Animator(2000, 5,This may not seem like a huge reduction but it removes the boilerplate of the action listener implementation and removes the need for the anonymous inner class making the code arguably much more readable.
Animator.RepeatBehavior.REVERSE, new ButtonTimingTarget());
anim.setAcceleration(0.4f);
anim.setDeceleration(0.4f);
ActionTrigger.addTrigger(button, anim);
Currently the framework provides four trigger implementations:
- ActionTrigger - as shown above, may be used on any component that fires action events.
- FocusTrigger - may be used on components that fire focus events such as text fields
- MouseTrigger - may be used on components that fire mouse events (pretty much every component) such as when the mouse enters or leaves a component
- TimingTrigger - allows sequencing of different animations, for example starting an animation when another animation ends
Next time I'll take this a step further and use a property setter to simplify the code even more.
Saturday, December 1, 2007
Timing Framework 101
One of the most important factors in computer animation is that it needs to be time based. Otherwise the animation may be impacted by the performance of the underlying system when run on platforms with differing performance characteristics; what looks impressive and realistic on the development system may look terrible on a system with differing performance. Time based animation removes this; the animation is given a duration and the state of the animation is interpolated based on the fraction of the duration that has passed. This helps ensure that animations look consistent when run on different platforms.
As of JSE6 there are 3 timer classes supplied; java.util.Timer, javax.swing.Timer and javax.management.Timer. For Swing based applications, the most useful of these is the javax.swing.Timer class which ensures that all events it fires are fired on the Event Dispatch Thread. This is important if the event is to subsequently update the state of the UI which is the case for UI animation.
The Swing timer class fires instances of ActionEvent and so components register ActionListener instances with the timer which are informed of events. This excerpt from the JSE6 javadoc illustrates one method of instantiating a timer which might be used for animation purposes.
If the animation is fired in response to some other action such as a button click then the code rapidly becomes unwieldy. The button fires an action event which has a listener that sets up a timer, which fires an action event of its own. The listener for this event then performs the necessary animation. In doing so, it must calculate the fraction of the animation duration that has passed in order for the animation to be properly time based.
There's a large amount of boilerplate code that is required in order to set up the actions and timers and to calculate the animation fraction. The timing framework helps to abstract this away from the developer so they may concentrate on the action of the animation itself. The framework handles the setup of the timer and calculating the fraction of the animation. It uses callbacks which the developer can use to perform the animation.
The core class of the Timing Framework is the Animator class. This is the class used to create and control animations. It provides 3 constructors, one of which is shown below. The others are simpler versions that use defaults for one or more of the options.
The duration is the length of the animation; repeatCount specifies the number of times the animation should repeat; repeatBehaviour specifies the behaviour of the animation when it begins a new cycle, should it perform the previous cycle in reverse or loop back to the beginning and repeat; finally the target is the object that receives the animation events.
Once the Animator instance has been created, further options may be specified by calling methods on the Animator.
The animation is started by calling the start method.
The final parameter in the above constructor is an instance of TimingTarget. This object is used by the Animator to perform callbacks to perform the animation. The TimingTarget interface specifies the following methods:
The begin, end and repeat methods are self explanatory and are called when the animation begins, ends and repeats a cycle respectively. The key method is the timingEvent method. As can be seen, this callback passes the fraction of the animation that has passed which is calculated by the framework. This value, which is always between 0 and 1, is used to perform the appropriate animation.
A simple example will help illustrate. If we have a panel containing a button that we wish to animate along the x axis when it is clicked, we could do the following:
This specifies an animation of 2 seconds duration that repeats 5 times with each cycle reversing the previous cycle. The ButtonTimingTarget which receives the callbacks is shown below:
The TimingTargetAdapter class is a convenience class that implements the TimingTarget interface with empty methods.
Screenshots of this animation are below.
1. Initial position

2. Mid way through the animation

3. After animation completion.

Although this is a simple example, it illustrates the simplicity of setting up an animation using the TimingFramework.
However, there's still no small amount of boilerplate in this example. We still have to add the action listener to the button in order to set up and start the animation. We also calculate and set the new location of the button ourselves based on the fraction of the animation. In the next blog I'll demonstrate the use of Triggers and PropertySetters to remove more of this boilerplate and make animations even simpler.
As of JSE6 there are 3 timer classes supplied; java.util.Timer, javax.swing.Timer and javax.management.Timer. For Swing based applications, the most useful of these is the javax.swing.Timer class which ensures that all events it fires are fired on the Event Dispatch Thread. This is important if the event is to subsequently update the state of the UI which is the case for UI animation.
The Swing timer class fires instances of ActionEvent and so components register ActionListener instances with the timer which are informed of events. This excerpt from the JSE6 javadoc illustrates one method of instantiating a timer which might be used for animation purposes.
int delay = 1000; //milliseconds
ActionListener taskPerformer = new ActionListener() {
public void actionPerformed(ActionEvent evt) {
//...Perform a task...
}
};
new Timer(delay, taskPerformer).start();
If the animation is fired in response to some other action such as a button click then the code rapidly becomes unwieldy. The button fires an action event which has a listener that sets up a timer, which fires an action event of its own. The listener for this event then performs the necessary animation. In doing so, it must calculate the fraction of the animation duration that has passed in order for the animation to be properly time based.
There's a large amount of boilerplate code that is required in order to set up the actions and timers and to calculate the animation fraction. The timing framework helps to abstract this away from the developer so they may concentrate on the action of the animation itself. The framework handles the setup of the timer and calculating the fraction of the animation. It uses callbacks which the developer can use to perform the animation.
The core class of the Timing Framework is the Animator class. This is the class used to create and control animations. It provides 3 constructors, one of which is shown below. The others are simpler versions that use defaults for one or more of the options.
Animator(int duration,
double repeatCount,
Animator.RepeatBehavior repeatBehavior,
TimingTarget target)
The duration is the length of the animation; repeatCount specifies the number of times the animation should repeat; repeatBehaviour specifies the behaviour of the animation when it begins a new cycle, should it perform the previous cycle in reverse or loop back to the beginning and repeat; finally the target is the object that receives the animation events.
Once the Animator instance has been created, further options may be specified by calling methods on the Animator.
The animation is started by calling the start method.
The final parameter in the above constructor is an instance of TimingTarget. This object is used by the Animator to perform callbacks to perform the animation. The TimingTarget interface specifies the following methods:
begin()
end()
repeat()
timingEvent(float fraction)
The begin, end and repeat methods are self explanatory and are called when the animation begins, ends and repeats a cycle respectively. The key method is the timingEvent method. As can be seen, this callback passes the fraction of the animation that has passed which is calculated by the framework. This value, which is always between 0 and 1, is used to perform the appropriate animation.
A simple example will help illustrate. If we have a panel containing a button that we wish to animate along the x axis when it is clicked, we could do the following:
button = new JButton("Click Me!");
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
Animator anim = new Animator(2000, 5,
Animator.RepeatBehavior.REVERSE,
new ButtonTimingTarget());
anim.start();
}
});
This specifies an animation of 2 seconds duration that repeats 5 times with each cycle reversing the previous cycle. The ButtonTimingTarget which receives the callbacks is shown below:
class ButtonTimingTarget extends TimingTargetAdapter {
@Override
public void timingEvent(float fraction) {
// Calculate button location bounds...
...
loc = ....
// Alter x location based on animation fraction.
button.setLocation(loc.x * fraction), loc.y);
}
}
The TimingTargetAdapter class is a convenience class that implements the TimingTarget interface with empty methods.
Screenshots of this animation are below.
1. Initial position
2. Mid way through the animation
3. After animation completion.
Although this is a simple example, it illustrates the simplicity of setting up an animation using the TimingFramework.
However, there's still no small amount of boilerplate in this example. We still have to add the action listener to the button in order to set up and start the animation. We also calculate and set the new location of the button ourselves based on the fraction of the animation. In the next blog I'll demonstrate the use of Triggers and PropertySetters to remove more of this boilerplate and make animations even simpler.
Wednesday, November 28, 2007
Swingin' into '08
My first foray into the world of Java was back in 99-2000 when I was tasked with creating a rudimentary client/server app. The app was Swing based with simple UIs for the client and server parts of the app. I remember being struck at the time of how easy it was to create basic
UIs; much more so than the C++ Win 32 UIs I had written previously.
Since those days I've not really used Swing that much, my career since then has taken me more towards N-tier applications, generally with a web front end. In those eight years, I've used a number of web MVC frameworks and through the years have seen a great improvement in developer productivity and ease of development/integration as new frameworks have come along. In recent years, technologies such as AJAX have added a richer experience to the web client.
So, when I decided to take another look at my old friend Swing, I was excited to see what had happened in the intervening years. On initial inspection, not a whole lot seems to have changed. There have been good performance improvements, which is always worthwhile. One introduction in JSE6 (developed initially at SwingLabs) I've since used a lot is the SwingWorker class which helps offload long running tasks from the Event Dispatch Thread. There have also been tweaks to the look and feels (esp the Windows L&F).
However, I was surprised not to see more new features jumping out at me and begging to be tried. Perhaps there are more gems lurking which I haven't yet discovered, I hope so.
I think the main frustration in developing Swing apps is that although there is a wealth of supplied components; and it is relatively straightforward to create custom components; there's no centralised framework or patterns for laying out your application architecture.
There's also a large amount of boilerplate that has to be created for all apps. For example, how often have you written...
... or something similar, just to make sure that the app is created on the EDT. I don't keep track (I'm not quite that geeky!) but it must be in the hundreds.
I'm therefore excited by JSR296 which should hopefully standardise the main structure of a Swing app and help development teams get up to speed more quickly as there should be a common understanding of how common functions are performed.
I'm also excited by JSR295 - Beans Binding which should simplify the process of binding the properties of GUI widgets to model properties removing yet more boilerplate for adding PropertyChangeListener instances.
Hopefully both of these will make it into JSE7 (Dolphin) but early versions are available on java.net.
I haven't had a chance to experiment with them yet but it's on the ToDo list.
One area I've been getting into since my return to client apps is animation and I've been experimenting with the Timing Framework developed by Chet Haase of Sun (available at http://timingframework.dev.java.net). It's fairly simple, only a handful of interfaces and classes. It's this simplicity that makes it a really nice framework to use. It's very easy to set up fairly sophisticated animations, quickly and easily with minimal coding required.
In the next few blogs I'll be running through some Timing Framework examples, showing how it can really simplify the creation of animations in Swing applications.
UIs; much more so than the C++ Win 32 UIs I had written previously.
Since those days I've not really used Swing that much, my career since then has taken me more towards N-tier applications, generally with a web front end. In those eight years, I've used a number of web MVC frameworks and through the years have seen a great improvement in developer productivity and ease of development/integration as new frameworks have come along. In recent years, technologies such as AJAX have added a richer experience to the web client.
So, when I decided to take another look at my old friend Swing, I was excited to see what had happened in the intervening years. On initial inspection, not a whole lot seems to have changed. There have been good performance improvements, which is always worthwhile. One introduction in JSE6 (developed initially at SwingLabs) I've since used a lot is the SwingWorker class which helps offload long running tasks from the Event Dispatch Thread. There have also been tweaks to the look and feels (esp the Windows L&F).
However, I was surprised not to see more new features jumping out at me and begging to be tried. Perhaps there are more gems lurking which I haven't yet discovered, I hope so.
I think the main frustration in developing Swing apps is that although there is a wealth of supplied components; and it is relatively straightforward to create custom components; there's no centralised framework or patterns for laying out your application architecture.
There's also a large amount of boilerplate that has to be created for all apps. For example, how often have you written...
public void static main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new MyAppFrame().setVisible(true);
}
};
}
... or something similar, just to make sure that the app is created on the EDT. I don't keep track (I'm not quite that geeky!) but it must be in the hundreds.
I'm therefore excited by JSR296 which should hopefully standardise the main structure of a Swing app and help development teams get up to speed more quickly as there should be a common understanding of how common functions are performed.
I'm also excited by JSR295 - Beans Binding which should simplify the process of binding the properties of GUI widgets to model properties removing yet more boilerplate for adding PropertyChangeListener instances.
Hopefully both of these will make it into JSE7 (Dolphin) but early versions are available on java.net.
I haven't had a chance to experiment with them yet but it's on the ToDo list.
One area I've been getting into since my return to client apps is animation and I've been experimenting with the Timing Framework developed by Chet Haase of Sun (available at http://timingframework.dev.java.net). It's fairly simple, only a handful of interfaces and classes. It's this simplicity that makes it a really nice framework to use. It's very easy to set up fairly sophisticated animations, quickly and easily with minimal coding required.
In the next few blogs I'll be running through some Timing Framework examples, showing how it can really simplify the creation of animations in Swing applications.
Subscribe to:
Posts (Atom)