Animations and transitions are two very interesting features of
the Lightweight User
Interface Toolkit (LWUIT). In this article we will see how to use
these features in actual applications. We also develop a custom
transition animation. Although very simple, this transition
demonstrates fully the process of such customization.
I have used the
Sprint Wireless Toolkit 3.3.1 (Sprint WTK) for the screenshots
and also to build the CustomTransitionDemo. I have
also referred to the source code for the LWUIT Demo application
that comes with the
LWUIT download bundle to illustrate the usage of the animation
and transition capabilities of LWUIT. For a proper understanding of
this article, the toolkit and the LWUIT bundle should be downloaded
and installed on your computer. Version 3.3.2 of the
Sprint WTK has been released; I have tried it out and found that
there is no problem in running our demo on the new version.
Animation
It was possible to animate images on the Java ME platform even
before LWUIT appeared on the scene. There are a number of features
in the javax.microedition.lcdui.game package that
support animation. In addition, the JSR 226 API
provides support for SVG-based animation. LWUIT handles animation a
little differently, providing support for a capability that is
broad-based and simple to use. In the context of LWUIT, animation
essentially allows objects to paint succeeding frames at timed
intervals. Animation support in LWUIT also makes it easy for
transitions to be implemented.
To be capable of being animated, an object has to implement the
Animation interface. Animation has two
methods.
animate: This is a callback method invoked once
for every frame.
paint: This method is called if
animate returns true. If the object being animated is
a component, it will have a paint method and that is
the one to be called as the signature of the two methods is
identical.
The Component class extends Animation.
This makes every widget animation-capable. However, in order to
actually carry out animation, an object must also be registered for
animation by calling the registerAnimated method on
its parent form. The animate method of a registered
object is called once for every frame of animation as determined by
the Display class. If animate
returns true, the paint method of the
object is called and a repaint takes place. To stop animation, the
object must be deregistered by calling the
deregisterAnimated method on the parent form.
To see how an animation works, let us look at
the AnimationDemo class of the LWUITDemo application that
comes with the Sprint Toolkit and also with the LWUIT download bundle.
The screenshots below show snapshots of two different frames of the
animation demo.


Figure 1. Two frames of the animation demo
The demo shows two examples of animation: one uses an animated
image of Duke and the other repeatedly draws a set of images to
simulate motion. In both cases, the base component that is animated is a
button. Other components can also be animated
in a similar way. You can replace the buttons in the code for
AnimationDemo with labels and the demo will
still work fine. Just remember to import the Label
class.
In the execute method of
AnimationDemo, the first button -- animation --
is instantiated and added to the form (f), which has
been passed as a parameter to the method:
Button animation = new Button(UIDemoMIDlet.
getResource("duke").getAnimation("duke3_1.gif"));
animation.setBorderPainted(false);
animation.getStyle().setBgTransparency(0);
f.setLayout(new BoxLayout(BoxLayout.Y_AXIS));
f.addComponent(animation);
The second and third lines are for visual effects only and the
fourth sets the layout manager for the form. The interesting thing
to note here is that the button has not been registered for
animation. This is because the image itself is animated and does
not require repainting. The image of Duke used here is an animated
gif. At this time LWUIT supports only animated gif format; as a
matter of fact, the Resource Editor does not recognize any other
animated format.
For the second button, the image array is set up for the paint
method:
Resources images = UIDemoMIDlet.getResource("images");
Image a = images.getImage("progress0.png");
Image b = images.getImage("progress1.png");
Image c = images.getImage("progress2.png");
final Image[] secondAnimation = new Image[] {
a,
b,
c,
a.rotate(90),
b.rotate(90),
c.rotate(90),
a.rotate(180),
b.rotate(180),
c.rotate(180),
a.rotate(270),
b.rotate(270),
c.rotate(270),
};
We now have a set twelve images that are to be drawn
sequentially. The code for creating the second button looks like
this:
Button animation2 = new Button() {
private int currentImage = 0;
private long lastInvoke;
public boolean animate() {
long current = System.currentTimeMillis();
if (current - lastInvoke > 50) {
lastInvoke = current;
currentImage++;
if (currentImage == secondAnimation.length) {
currentImage = 0;
}
return true;
}
return false;
}
public void paint(Graphics g) {
g.drawImage(secondAnimation[currentImage],
getX(), getY());
}
};
Here we have a button that implements both the animate
and paint methods. The animate method
will be called at every frame interval. If more than 50
milliseconds have elapsed since the last call, animate
will increment the pointer to the image array and return
true; otherwise, it will just return false.
If true is returned, then the paint method will be
called and the appropriate image will be drawn.
The rest of the code for the second button is mainly for setting
the visual parameters and for adding the button to the form. Note
the last line of code, which shows that the button has been
registered for animation. This ensures that the
animate method will be called for every
frame.
animation2.setPreferredSize(new Dimension
(secondAnimation[0].getWidth(),
secondAnimation[0].getHeight()));
animation2.setBorderPainted(false);
animation2.getStyle().setBgTransparency(0);
f.addComponent(animation2);
f.registerAnimated(animation2);
And that is all there is to applying animation!
The term transition refers to the way a form is brought
into (animate in transition) and taken out of (animate
out transition) display. The base class for implementing
transition is Transition, which implements
Animation. This is why transitions work basically as
animations and utilize the same callback mechanism as any other
animated component. However, Transition is different
from Component in the sense that a
Component animates its own rendering while
Transition controls the animated rendering of forms
and dialogs.
There is also a difference between the ways in which component
animations and transitions are used. To use transitions, it is not
necessary to register the form concerned for animation. When a form
transition occurs, the Display object directly places
the form into the queue for receiving callbacks.
The Transition class is an abstract one and cannot
be instantiated. The LWUIT library includes two concrete subclasses
of Transition that can be used to apply transitions.
These classes are:
CommonTransitions
Transition3D
CommonTransitions currently includes two types of
transition animations.
Slide: The new form slides in, pushing out the current
one.
Fade: The new form fades in, while the current one
fades out.
CommonTransitions works with a supporting class --
Motion -- that provides the models for simulating
physical motion. The three types of motions which
Motion contains are:
The Transition3D class works with the M3G API (JSR 184) to provide
transition animations with 3D effects. JSR 184 support is necessary
for these transitions to work properly, and so the
Motion class is not required for
Transition3D. A current limitation of this class is
that its transitions work only with forms, but that is likely to
change
soon. The 3D suite of transitions contains the
following.
Cube: Simulates a rotating cube, with the current
form on the face rotating off the screen and the new one on
the face rotating into the screen.
Fly In: The new form flies in.
Rotation: A two-dimensional version of
Cube in which a plane, rather than a cube, rotates.
Static Rotation: Applicable to a transition from
a form into a dialog. Only the dialog rotates while the form
remains static.
Swing In: The incoming form swings into place
either from top to bottom or from bottom to top.
Transitions are very easy to use, as we can see from the
following code snippets (from the TransitionDemo class
of the LWUIT demo application), which are examples of how to create
transitions by using the applicable factory methods:
//creates an out transition
//with a right-to-left outgoing slide
out = CommonTransitions.createSlide(
CommonTransitions.SLIDE_HORIZONTAL, false, runSpeed);
//creates an out transition
//with a left-to-right incoming slide
in = CommonTransitions.createSlide(
CommonTransitions.SLIDE_HORIZONTAL, true, runSpeed);
.
.
.
//creates outgoing and incoming fade transitions
out = CommonTransitions.createFade(runSpeed);
in = CommonTransitions.createFade(runSpeed);
.
.
.
//creates outgoing and incoming fly in transitions
out = Transition3D.createFlyIn(runSpeed);
in = Transition3D.createFlyIn(runSpeed);
You can see that there is no difference in the way a transition
is created, regardless of whether it is a common transition or a 3D
transition. After creating a transition, it has to be installed for
the desired form. This too is simple:
//f is the form for which transitions are being set
f.setTransitionOutAnimator(out);
f.setTransitionInAnimator(in);
Note that if you set the outgoing transition for a form it is
not necessary to set the incoming transition for the new form that
is entering the screen; as the old form goes out it is
automatically replaced by the new one. When an application has a
number of forms, I find it a good practice to set only the outgoing
transitions for all the forms. This way I avoid conflicts and
missing transitions.
A Custom Transition
One of the great things about LWUIT is that it supports a good
deal of customization. So, if you want a transition that is not
available in the transition classes that come with the library, you
can easily write your own. In this section we develop a simple
transition to demonstrate the basic techniques for creating custom
transitions. Before we start on our project, though, I would like
to add a caveat here. The new transition is only a tool for
understanding how transitions and motions work. To keep it simple,
I have imposed certain restrictions that make this class unsuitable
for general use. Some of these are:
- It does not support dialogs.
- It is not configurable and has only one kind of motion --
horizontal, towards the right.
- It does not check for all conditions that may be encountered in
a real device.
Our transition is called Step Transition. As its name
suggests, it makes the destination screen move into position in a
series of discrete steps, unlike Slide Transition.
Another difference between the two transitions is that with step
transition, the incoming screen does not appear to push the source
screen out -- it moves over the source, gradually covering it.
To create a custom transition, we need to write a class that
extends Transition. In this case the class is
StepTransition. We also need a model for the
appropriate motion and, for our demo, that is the
StepMotion class.
We start by looking at StepTransition class. The
Transition class has the following abstract methods
that have to be implemented by
StepTransition:
- public abstract Transition copy()
- public abstract boolean animate()
- public abstract void paint(Graphics g)
In addition, there is the empty initTransition
method that will have to be suitably implemented. Depending on the
nature of the transition, this method may not be required at all, in
which case you may omit it altogether.
The initTransition method is a callback that is
invoked at the beginning of each transition. So this is the place
to initialize all parameters for starting the transition. We also
instantiate a StepMotion object, which will work out
the position for rendering the destination screen (remember this is
the only screen that moves) for each frame. Our code for
initTransition is pretty simple:
public void initTransition()
{
//initialize variables
//required to set up motion
Component source = getSource();
int width = source.getWidth();
position = 0;
int startoffset = 0;
//create a StepMotion object for this transition
motion = new StepMotion(startoffset,
width, duration, step);
//start the movement for the transition
motion.start();
}
Thanks to the simplicity of our specifications, we need to do
just a few things to initialize the transition. What we do is
initialize the variables required to create the motion object,
create the motion, and start it running.
The animate method is called once for every frame.
This method checks to see if the motion object is missing or
whether the transition is over. The check for completion of
transition involves calling the isFinished method of
StepMotion, which returns true if the
transition is done. If there there is no motion object or if
the transition is complete, false is returned so that
this transition activity is terminated. Otherwise, the transition
will continue and the position for drawing the destination screen
corresponding to the next frame is obtained. Finally,
animate returns true so the next frame
can be drawn:
public boolean animate()
{
//see if there is a motion object
//and check if the transition is completed
if(motion == null || motion.isFinished())
{
//in either case terminate transition
return false;
}
//if transition is to continue
//get the new position
position = motion.getStep();
//continue with transition
return true;
}
Since StepTransition implements
Animation, it has a paint method that is
invoked to refresh the destination screen for each frame. As we see
in the code segment above, the position for drawing the destination
screen is returned by the getStep method of
the StepMotion class. Since the value of position changes
only in discrete steps, it may remain unchanged over a number of
frames. Drawing the screen(s) for every frame would be a waste of
CPU time. To avoid this, getStep returns -1 if the
position has remained unchanged since the previous frame. The
paint method checks the value of position and calls
paintSlideAtPosition to do the actual painting only
when necessary. The code for the paint method is given
below:
public void paint(Graphics g)
{
//paint only if new position is available
if(position > -1)
{
paintSlideAtPosition(g, position, 0);
return;
}
else
{
return;
}
}
The actual rendering is done by the paintComponent
method of the Component class after the graphics context
has been prepared by the following methods:
private void paintSlideAtPosition(Graphics g, int slideX,
int slideY)
private void paint(Graphics g, Component cmp, int x, int
y)
For our simple application, we could have combined all the paint
methods into one. However, I have retained the structure of
CommonTransitions class that comes with the library. I
hope this will make it easy to follow the method sequence for
anyone who wishes to study the source code.
The paintSlideAtPosition method first checks
whether the first form of the application is being displayed and,
in that case, inhibits transition. Otherwise it sets up the clip
region and, more importantly for this application, the co-ordinates
for translation so that proper shifting of the destination screen
can occur. The next method takes care of the actual translation,
calls the paintComponent on Component to
draw the destination screen at the right position, and restores the
original translation. The demo, as it stands, causes the
destination screen to move over a stationary source. This happens
because only the destination screen is repainted for each new
position. If you want to achieve a push effect, just
uncomment the two bold lines of code in the
paintSlideAtPosition method. This will cause the
source screen also to be repainted and you will see it moving out
of the display area as the destination screen moves in. The two
methods are shown below:
private void paintSlideAtPosition(
Graphics g, int slideX, int slideY)
{
Component source = getSource();
//if this is the first form being displayed we
//can't do a step transition
//since we have no source form
if (source == null)
{
return;
}
Component dest = getDestination();
int w = -source.getWidth();
int h = 0;
//set the clips
//uncomment the two following lines
//for the "push" effect
//g.setClip(source.getAbsoluteX(),
source.getAbsoluteY(), source.getWidth(),
source.getHeight());
//paint(g, source, slideX , slideY );
g.clipRect(dest.getAbsoluteX(),
dest.getAbsoluteY(), source.getWidth(),
source.getHeight());
paint(g, dest, slideX + w, slideY + h);
}
private void paint(Graphics g, Component cmp, int x, int y)
{
//get original clip details
int cx = g.getClipX();
int cy = g.getClipY();
int cw = g.getClipWidth();
int ch = g.getClipHeight();
//translate to proper position
//for drawing the form
g.translate(x, y);
//get it painted
cmp.paintComponent(g, false);
//restore original values
g.translate(-x, -y);
g.setClip(cx, cy, cw, ch);
}
The copy method just returns a copy of the step
transition object. Note that a copy is returned and not the object
itself, as the Display class wants a copy to work
with.
//return a functionally equivalent transition object
public Transition copy()
{
return new StepTransition(duration, step);
}
The constructor of StepMotion takes four integers
as parameters.
sourcevalue: This is the starting position. If the new screen
starts moving in from the left edge, then this will be zero.
destinationvalue: This is the finishing position. If the new
screen should stop at the right edge, then this will be equal to the
width of the display area.
duration: The time period (in milliseconds) over which the
transition should last.
steps: The number of steps the transition should take.
The constructor calculates the value by which the position of
the destination screen has to be incremented from one step to the
next. It also calculates the minimum time that has to elapse before
the screen can be refreshed.
public StepMotion(int sourcevalue, int destinationvalue,
int duration, int steps)
{
this.destinationvalue = destinationvalue;
this.duration = duration;
this.steps = steps;
//the size of a step
stepsize = (destinationvalue - sourcevalue)/steps;
//the time interval between two successive steps
interval = duration/steps;
}
We have already met the three methods of
StepMotion. The only point to note here is that
calculated step size may not be a factor of the total distance
(destinationvalue - sourcevalue) to be covered as we
are using integer math. In such a case, the getStep
method will ensure that missing distance is made up as it always
takes one step more than calculated number of steps. This is
because isFinished returns true only when the number
of steps taken (stepnumber) exceeds the required
number of steps. This extra step doesn't really cause any problem,
as the position is not allowed to exceed
destinationvalue. The methods are:
//save the time as the beginning of an interval
public void start()
{
starttime = System.currentTimeMillis();
}
//return true if all steps have been taken care of
public boolean isFinished()
{
return stepnumber > steps;
}
//return the position
public int getStep()
{
//check if interval for next step is over
//if so increment stepnumber
//and return (stepsize*stepnumber)
//also reset starttime by calling start()
//if (stepsize*stepnumber>destinationvalue)
//then return destinationValue
//if interval not over return -1
if(System.currentTimeMillis() >=
starttime + interval)
{
stepnumber++;
int offset = stepnumber*stepsize;
if(offset > destinationvalue)
{
offset = destinationvalue;
}
start();
return offset;
}
else
{
return -1;
}
}
The MIDlet that puts it all together is also quite
straightforward. After calling init on
Display, it initializes the parameters for transition
and sets up the theme. It then creates two labels for the
two forms that will be used in the demo. We would like the the two
forms to look somewhat different so that the transition becomes
visually prominent. So the first form is created and its titlebar
is made different from the theme setting. The label srclabel is added to the first form, and so are the
commands. The MIDlet is then set as the command listener for the
form. Finally the first form is made visible:
//init the LWUIT Display
Display.init(this);
//duration of transition in milliseconds
int duration = 3000;
//number of steps in transition
int numofsteps = 10;
//get the theme and set it
try
{
Resources r = Resources.open("/SDTheme1.res");
UIManager.getInstance().setThemeProps(
r.getTheme("SDTheme1"));
}
catch (java.io.IOException e)
{
}
//create two labels
Label srclabel = new Label("This is the source screen");
Label dstlabel = new Label(
"This is the destination screen");
//create the first form
first = new Form("Source");
//we make the titlebar different
//from the theme setting
first.getTitleStyle().setBgImage(null);
//add srclabel to the first form
first.addComponent(srclabel);
//add 'Next' command to first form
//the command id is 1
first.addCommand(new Command("Next", 1));
//add 'Exit' command to first form
//the command id is 0
first.addCommand(new Command("Exit", 0));
//this MIDlet is the listener
//for the first form's commands
first.setCommandListener(this);
//show the first form
first.show();
The next part of the code, which deals with the second form, is
almost the same. The fundamental difference is that in and
out transitions are set for this form. As this demo has only
two forms, I have violated my own recommendation and set both
transitions for the same form.
//create the second form
second = new Form("Destination");
//set the Out and In transitions for the second form
second.setTransitionOutAnimator(new StepTransition
(duration, numofsteps));
second.setTransitionInAnimator(new StepTransition
(duration, numofsteps));
//set Menu and background images for second form
try
{
second.getMenuStyle().setBgImage(Image.createImage
("/pattern3.png"));
}
catch(java.io.IOException ioe)
{
}
try
{
second.getStyle().setBgImage(Image.createImage
("/sdsym4.png"));
}
catch(java.io.IOException ioe)
{
}
//add dstlabel to second form
second.addComponent(dstlabel);
//add 'Previous' command to first form
//the command id is 2
second.addCommand(new Command("Previous", 2));
//this MIDlet is the listener
//for the second form's commands
second.setCommandListener(this);
All the above action takes place in the startApp
method. Now we take care of the commands and we're done:
public void actionPerformed(ActionEvent ae)
{
Command cmd = ae.getCommand();
switch (cmd.getId())
{
//'Exit' command
case 0:
notifyDestroyed();
break;
//'Next' command
//show second form
case 1:
second.show();
break;
//'Previous' command
//show first form
case 2:
first.show();
}
}
The three screenshots in Figures 2 through 4 show progressive
images of the step transition from the first form to the
second.

Figure 2. Step number 2 of transition

Figure 3. Step number 5 of transition

Figure 4. Step number 8 of transition
Conclusion
We have seen how to use the animation and transition functions
of LWUIT and how to create our own transition.
LWUIT is an evolving library and we are bound to see additions to
its current repertoire. Developers will have to constantly update
their knowledge to remain abreast of the new functions,
capabilities, and refinements. It's going to need some hard work, but
it's going to be great fun too!
Resources
Biswajit Sarkar is an electrical engineer with a specialization in programmable industrial automation.