Skip to main content

Scalable Vector Graphics on Java ME

July 10, 2007

{cs.r.title}



JSR 226 is the
Scalable 2D Vector Graphics API for low-end mobile devices. It is
an optional API that mandates support for the SVG Tiny profile and is
now available on a number of mobile handsets. Since "http://jcp.org/en/jsr/detail?id=248">Mobile Service Architecture
(MSA)
--the specification for the next wave of Java ME--makes support for JSR 226 mandatory, an increasing number of future
handsets will implement this API. In this article, we look at the
fundamentals of JSR 226.

Scalable Vector Graphics (SVG) is a language for describing
two-dimensional vector graphics, and SVG Tiny is a subset of SVG
suitable for displaying vector graphics on small devices. SVG is
based on XML and other applicable standards as described in its
specification. This article
does not intend to offer an insight into SVG itself. There are
excellent tutorials available on the net that cover the basics of
SVG and SVG Tiny. Links to some of these have been furnished in the
Resources section.

I assume you are familiar with "http://java.sun.com/javame/index.jsp">Java ME and the SVG Tiny
specs. The
applications (except the "Static Pointer Demo" and the "Moving
Pointer Demo") used to examine the basic features of JSR 226 are
drawn from the demos that come with the Sun Java Wireless
Toolkit 2.5 for CLDC and are part of the "SVGDemo" project. In
order to follow this article effectively, you'll need to download
the "http://java.sun.com/products/sjwtoolkit/download-2_5.html">Sun
Java Wireless Toolkit
and install it on your computer. I would
also recommend that you download the "http://jcp.org/aboutJava/communityprocess/final/jsr226">JSR 226
API documentation
for comprehensive information.

Create an SVG Image from Scratch

This demo application shows how to create an SVG image with
inline code. The image is created by the
CreateEmptyImageDemo MIDlet. The MIDlet creates an
instance of SVGImageCanvas and passes the created
image to it for rendering.

In the world of JSR 226, ScalableGraphics is the
class that takes care of all rendering. This is what the API
documentation says about ScalableGrapics:

This is the fundamental class for 2D rendering. The
ScalableGraphics context class provides and handles
all the rendering capability within this package. In other words,
the rendering can only be achieved through the render method
provided in this class. Note that the ScalableGraphics
instance must be bound to the rendering target prior to calling the
render method. The implementation must clip to the viewport
boundaries.

The other fundamental class is
ScalableImage, which is described in the API
documentation as follows:

This class models images in vector format, such as the Scalable
Vector Graphics (SVG) image format. Therefore, it is required that
all classes representing "scalable" images extend this class.

So every JSR 226 based application will have instances of these
two classes or of a subclass, if applicable. Accordingly, the
CreateEmptyImageDemo MIDlet creates an
SVGImage that is a subclass of
ScalableImage:

[prettify]
    SVGImage svgImage = SVGImage.createEmptyImage(null);
    Document doc = svgImage.getDocument();
    SVGSVGElement svg = (SVGSVGElement)doc.getDocumentElement();
[/prettify]

At this point we have an empty SVGImage and the
child element corresponding to the topmost tag. This element is
cast to SVGSVGElement, which represents the
element in the (SVG) document tree. The
next step is to define a viewBox (vb):

[prettify]
    // First, set the viewBox so that we can work in 
    // a 100 by 100 area.
    SVGRect vb = svg.createSVGRect();
    vb.setWidth(100);
    vb.setHeight(100);
    svg.setRectTrait("viewBox", vb);
[/prettify]

The viewBox attribute ensures that the dimensions
of image are calculated after taking into account the actual
display area available at the time of rendering. This issue is
elaborated upon later in the section An Animated
Menu
.

We then go on to create the images: a rectangle and a circle
within it. First we need to create an SVGElement:

[prettify]
    SVGElement r = 
        (SVGElement)doc.createElementNS(SVG_NAMESPACE_URI, "rect");
[/prettify]

The SVGElement interface defines methods to set
various attributes and properties required to define the specified
element, which is rect in this case. Note that the
specified name of the element must be one of those defined in the
SVG Tiny specification. The API documentation lists the valid
element names. Once the SVGElement has been created
and the relevant attributes/properties have been set, it is
appended to the SVGSVGElement already created. So the
SVGElement now becomes a part of our (SVG) document
tree:

[prettify]
    SVGRGBColor bkgColor = svg.createSVGRGBColor(99, 128, 147);
    r.setRGBColorTrait("fill", bkgColor);
    r.setFloatTrait("x", 25);
    r.setFloatTrait("y", 25);
    r.setFloatTrait("rx", 5);
    r.setFloatTrait("ry", 5);
    r.setFloatTrait("width", 50);
    r.setFloatTrait("height", 50);
    svg.appendChild(r);
[/prettify]

Similarly, the circle is defined:

[prettify]
    SVGElement c = (SVGElement)
       doc.createElementNS(SVG_NAMESPACE_URI, "circle");
    SVGRGBColor fgColor = svg.createSVGRGBColor(255, 255, 255);
    c.setRGBColorTrait("fill", fgColor);
    c.setFloatTrait("cx", 50);
    c.setFloatTrait("cy", 50);
    c.setFloatTrait("r", 20);
    svg.appendChild(c);
[/prettify]

The image--svgImage--can now be rendered on a
canvas. In this case, the rendering is performed by the
SVGImageCanvas class.

The SVGImageCanvas class works with two instance
variables: the SVGImage that has to be rendered and a
ScalableGraphics context. The rendering of the image
is done in the paint method. Once the display
dimensions are determined and a white background is established,
the MIDP Graphics object to be used by the
paint method has to be bound to the
ScalableGraphics context. The viewport dimensions are
then set to cover the available display area and the image is
rendered:

[prettify]
    class SVGImageCanvas extends Canvas {
    /**
     * The SVGImage painted by the canvas.
     */
    protected SVGImage svgImage;

    /**
     * The ScalableGraphics used to paint into the midp
     * Graphics instance.
     */
    protected ScalableGraphics sg = 
       ScalableGraphics.createInstance();

    /**
     * @param svgImage the SVGImage 
     * this canvas should paint.
     */
    protected SVGImageCanvas(final SVGImage svgImage) {
        this.svgImage = svgImage;
    }

    public void paint(Graphics g) {
        g.setColor(255, 255, 255);
        g.fillRect(0, 0, getWidth(), getHeight());
        sg.bindTarget(g);
        svgImage.setViewportWidth(getWidth());
        svgImage.setViewportHeight(getHeight());
        sg.render(0, 0, svgImage);
        sg.releaseTarget();
    }
}
[/prettify]

Note that it is important to invoke the
releaseTarget() method after the image is rendered.
This method flushes the image and ensures that it becomes visible.
Implementations do not guarantee that the rendered image will be
visible unless this method is called.

Static Pointer Demo

The usual method of handling SVG code is to have one or more SVG
files containing image information, and the application renders the
images based on such information. By separating Java code from SVG,
we can ensure that modifications in the image file will not affect
the application; only the SVG file will have to be changed and the
application will not have to be recompiled. In this section, we will
see how images can be drawn from SVG files. The source code of this
application is available in the "#resources">Resources section.

This is a simple application that draws a triangle at the center
of the screen. The image information is available in the
ptr2.svg file, and the StaticPointer MIDlet
creates an SVGImage from the information in the file.
To do this, an InputStream is created with the SVG file
as the parameter:

[prettify]
    //we'll work with the SVG file placed in the res folder
    public static final String POINTER_IMAGE = "/ptr2.svg";
    .
    .
    .
    public void startApp()
    {
            InputStream is = 
                getClass().getResourceAsStream(POINTER_IMAGE);
        .
        .
        .
[/prettify]

The next step is to create an SVGImage from this
stream:

[prettify]
    SVGImage pointerimage = (SVGImage)SVGImage.createImage(is, null);
[/prettify]

StaticPointer then instantiates a
StaticPointerCanvas object, which performs the actual
rendering. The SVGImage, pointerimage, is passed to
the StaticPointerCanvas object as a parameter. The
StaticPointerCanvas class is very similar to the
SVGImageCanvas already described.

"Static Pointer Demo" draws a blue pointer in the middle of the
screen, as shown in Figure 1.

<br "Display for StaticPointerDemo" width="325" height="450" />

Figure 1. Display for Static Pointer Demo

Moving Pointer Demo

SVG not only supports rendering of static images, but also
provides for animation in various ways. The simplest (and most
fundamental) form of animation is, of course, the sequential
display of a series of static images. This approach, shown in
"Moving Pointer Demo," does not use the animation-oriented
mechanisms built into the SVG specifications to simulate movement,
but can be useful in creating motion when only simple, static image
files are available. The source code of this application is
available in the Resources section.

In "Moving Pointer Demo," we deal with three SVG files each
describing a pointer-like figure, as in Static Pointer Demo. The
pointers are identical except in the following two respects:

  • The fill colors are different.
  • The location of each pointer is different.

The code for the MovingPointer MIDlet is almost the
same as that for StaticPointer. The only difference is
that three SVGImages are created by the
MovingPointer class and passed to
MovingPointerCanvas for rendering:

[prettify]
    InputStream is1 = 
        getClass().getResourceAsStream(POINTER_IMAGE1);
    if (is1 == null)
    {
        throw new Error("Could not load " + POINTER_IMAGE1);
    }

    InputStream is2 = 
        getClass().getResourceAsStream(POINTER_IMAGE2);
    if (is2 == null)
    {
        throw new Error("Could not load " + POINTER_IMAGE2);
    }

    InputStream is3 = 
        getClass().getResourceAsStream(POINTER_IMAGE3);
    if (is3 == null)
    {
        throw new Error("Could not load " + POINTER_IMAGE3);
    }

    try
    {
         System.out.print("Loading SVGImages .... ");

         SVGImage si1 = 
            (SVGImage)SVGImage.createImage(is1, null);
           SVGImage si2 = 
            (SVGImage)SVGImage.createImage(is2, null);
           SVGImage si3 = 
            (SVGImage)SVGImage.createImage(is3, null);
         System.out.println(" ... Done");

         movingpointercanvas = 
             new MovingPointerCanvas(si1, si2, si3);
            .
            .
            .
[/prettify]

The MovingPointerCanvas class implements the
Runnable interface for repetitive rendering of the
three pointers. In the run method, a switch statement
checks the variable state and, depending on its value
(0, 1, or 2), assigns the reference of one of the three
SVGImages received from MovingPointer to
the imagetodraw variable. The state
variable is then set to the next value and the repaint
method is called, which renders imagetodraw in the
same way as the paint method in
SVGImageCanvas described earlier:

[prettify]
    switch(state)
    {
        case 0:
        imagetodraw = si1;
        state++;
        repaint();
        break;

        case 1:
        imagetodraw = si2;
        state++;
        repaint();
        break;

        case 2:
        imagetodraw = si3;
        state = 0;
        repaint();
    }
[/prettify]

After returning from repaint, the thread sleeps for
500 milliseconds and iterates the loop once more, creating a moving
pointer.

The SVGAnimator

The SVG specification supports a variety of ways for animating
images: different kinds of motion, change of color, zooming,
panning, and so on. JSR 226 handles animation through the
SVGAnimator class. The API documentation says:

The SVGAnimator class handles automatic rendering of updates and
animations in an SVGImage to a target user interface (UI)
component.

We'll look at a very nice demo application--"Play SVG
Animation," which comes with WTK 2.5--to understand how
SVGAnimator can be used.

The entry point of this application is the
GreetingCard class, which extends a MIDlet.
Functionally, GreetingCard only initializes its
superclass--the PlaySVGImageDemo MIDlet--by
passing a reference to the animated SVG image file:

[prettify]
    public final class GreetingCard extends PlaySVGImageDemo {
        /**
         * The image that holds the animated SVG content.
         */
        private static final String SVG_IMAGE = 
            "/svg/Halloween.svg";

        /**
         * The image that holds the splash screen image.
         */
        private static final String SPLASH_IMAGE = 
            "/images/GreetingCardHelp.png";

        public GreetingCard() {
            super(SVG_IMAGE, SPLASH_IMAGE, true);
        }
    }
[/prettify]

The real action takes place in the startApp method
of PlaySVGImageDemo. After an SVGImage is built, it is
used to create an SVGAnimator object:

[prettify]
    // Get input stream to the SVG image 
    //stored in the MIDlet's jar.
    InputStream svgDemoStream = 
        getClass().getResourceAsStream(svgImageName);

    // Build an SVGImage instance from the stream
    svgImage = null;

    if (svgDemoStream != null) {
          try {
                 svgImage = 
                      (SVGImage)SVGImage.createImage
                      (svgDemoStream, null);
              } catch (IOException io) {
                   io.printStackTrace();
              }
          }
    if (svgImage == null) {
            throw new 
                Error(ERROR_COULD_NOT_LOAD_SVG + svgImageName);
    }

    // Build an SVGAnimator from the SVGImage. 
    //The SVGAnimator will handle
    // all the animation details and run the SVG animation in a 
    // Canvas instance.
    svgAnimator = SVGAnimator.createAnimator(svgImage);

[/prettify]

The SVGAnimator class has a method to set the
minimum time interval between two successive renderings of the
image being animated. In this case, this time interval is set to
0.01 second (ten milliseconds), which means the image will be
refreshed 100 times per second. If this value is set to, say, 1.0f
and the MIDlet is recompiled, the image will be refreshed once
every second. Consequently the motions will be jerky:

[prettify]
    svgAnimator.setTimeIncrement(0.01f);
[/prettify]

An SVGAnimator object has a platform-dependent user
interface component associated with it, on which the image is
rendered. Mobile devices support LCDUI and, for such
devices, the target component associated with an
SVGAnimator is a Canvas object. This
object is obtained by calling the getTargetComponent
method of the SVGAnimator class, and the dimensions of this
canvas are used to set the width and height of the viewport:

[prettify]
    svgCanvas = (Canvas)svgAnimator.getTargetComponent();
    svgImage.setViewportWidth(svgCanvas.getWidth());
    svgImage.setViewportHeight(svgCanvas.getHeight());
[/prettify]

Of course, in addition to the steps required for showing the
animated image, the MIDlet performs some other tasks that are
needed for supporting activities like responding to commands and
key presses.

Finally, the animation is started with a call to the
play method of the SVGAnimator class:

[prettify]
    if (autoStart) {
                svgAnimator.play();
    .
    .
[/prettify]

This application demonstrates how easy it is to show even
complex animations using the JSR 226 API. Note that it was not
necessary to get a ScalableGrphics context in this
case, as SVGAnimator takes care of all that is needed
to render the image.

An Animated Menu

"SVG Demo" is a suite of applications, one of which is
"Optimized Menu." This demo shows how to use the information in an
SVG file to convert an animated SVG image into a series of
rasterized images and then display these images in a sequence to
produce animation. Like many programming decisions, this too
involves a trade-off. Every time an SVG image is displayed, a
considerable amount of processing has to be done to convert the SVG
code into a form suitable for display. By pre-rasterizing the SVG
image, the processing is done only once and not every time the menu
is displayed. However, additional memory will have to be allocated
to hold the rasterized images.

The technique used here is quite straightforward. Nine frames
are created for each icon, with frame 0 representing the
unselected state and frame 8 the selected one. All intervening
frames represent animation in progress. When an icon is selected,
the corresponding frames are displayed in quick succession from 0
to 8 showing the transition from the unselected to the selected state. For the icon that is deselected, the frames are stepped
backwards from 8 to 0, thus creating a "reverse" animation.
"Optimized Menu" displays a menu in which the icons are arranged in
the form of a grid. Each icon is characterized by its position in
the grid--a row number and a column number. Therefore, the entire
set of rasterized images is structured as a three dimensional
array:

[prettify]
        menuIcons[r][c][f]

    where

        r = row number for the icon (0 to 2)
        c = column number for the icon (0 to 2)
        f = frame number (0 to 8)
[/prettify]

The image at menuIcons[0][0][0], for example, represents the
unselected frame for the icon at the top left corner.

The steps to be followed for filling the array are:

  • Load all the icons into a vector.
  • Establish a viewBox for each icon.
  • Render frames for each icon and load them into the array.

First Step

Loading the icons into a vector is basically a matter of
convenience so that subsequent handling becomes easier:

[prettify]
    // The input svgImage should have numRows * numCols 
    //icons under the 
    // root svg element.
    final int numIcons = numRows * numCols;
    Document doc = svgImage.getDocument();
    SVGSVGElement svg = (SVGSVGElement)doc.getDocumentElement();

    // Load all the icons in a Vector for future manipulation
    Vector iconVector = new Vector();

    for (int i = 0; i < numIcons; i++) {
        SVGElement iconElt = 
           (SVGElement)doc.getElementById("icon_" + i);

        if (iconElt == null) {
            throw new 
                IllegalArgumentException
                ("The SVG Image does not have " + numIcons +
                " icons under the root svg element" + 
                " icon_" + i +
                " does not exist in the document");
        }

        if (!(iconElt instanceof SVGLocatableElement)) {
             throw new IllegalArgumentException
             ("The " + (i + 1) + "th icon under the " +
                "root svg element is not a <g;");
        }

        // Hide all icons initially
        iconElt.setTrait("display", "none");

        iconVector.addElement(iconElt);
     }
[/prettify]

Second Step

Before the viewBoxes can be established, the space available for
each icon is determined. To set the viewBox parameters, dimensional
data furnished in the SVG document is translated into device-dependent units (pixels) through transformation matrices--for the
image (SVGSVGElement) as a whole, in conjunction with
those for individual icons. The result of these transformations,
the bounding boxes, are scaled to have a "padding" area around each
icon and are then saved as an array of viewBoxes
(iconViewBox):

[prettify]
    // Now, compute the size allocated to each icon.
    int width = getWidth();
    int height = getHeight();

    iconWidth = width / numCols;
    iconHeight = height / numRows;

    // Render each icon in a bitmap.
    svgImage.setViewportWidth(iconWidth);
    svgImage.setViewportHeight(iconHeight);

    final int numFrames = 1 + numFramesFocus;
    menuIcons = new Image[numRows][numCols][numFrames];
    currentFrame = new int[numRows][numCols];

    // calculate viewBox for each icon

    // svg -> screen
    SVGMatrix svgCTM = svg.getScreenCTM();

    // screen -> svg
    SVGMatrix svgICTM = svgCTM.inverse();

    SVGRect[] iconViewBox = new SVGRect[numIcons];

    for (int i = 0; i < numIcons; ++i) {
        SVGLocatableElement icon = (SVGLocatableElement)
        iconVector.elementAt(i);


        // Get the user space bounding box for the icon
        SVGRect bbox = icon.getBBox();
        if (bbox == null) {
            // If someone tampered with the 
            //svg menu file, the bbox 
            // could be null
            iconViewBox[i] = null;
            continue;
        }

        // icon -> svg -> screen
        SVGMatrix iconCTM = icon.getScreenCTM();

        // icon -> svg
        SVGMatrix iconToSvg =
        svg.createSVGMatrixComponents(svgICTM.getComponent(0), 
            svgICTM.getComponent(1),
            svgICTM.getComponent(2), 
            svgICTM.getComponent(3), 
            svgICTM.getComponent(4),
            svgICTM.getComponent(5));
        iconToSvg.mMultiply(iconCTM);

        // get the icon bounding box in svg coordinates
        float x0 = bbox.getX();
        float y0 = bbox.getY();
        float x1 = x0 + bbox.getWidth();
        float y1 = y0 + bbox.getHeight();
        float[] pointsX = { x0, x0, x1, x1 };
        float[] pointsY = { y0, y1, y0, y1 };
        float minX = Float.MAX_VALUE;
        float minY = Float.MAX_VALUE;
        float maxX = -Float.MAX_VALUE;
        float maxY = -Float.MAX_VALUE;
        float a = iconToSvg.getComponent(0);
        float b = iconToSvg.getComponent(1);
        float c = iconToSvg.getComponent(2);
        float d = iconToSvg.getComponent(3);
        float e = iconToSvg.getComponent(4);
        float f = iconToSvg.getComponent(5);

        for (int j = 0; j < pointsX.length; ++j) {
             float nx = (a * pointsX[j]) + 
                 (c * pointsY[j]) + e;
             float ny = (b * pointsX[j]) + 
                 (d * pointsY[j]) + f;

             if (nx < minX) {
                 minX = nx;
             }

             if (nx > maxX) {
                 maxX = nx;
             }

             if (ny < minY) {
                 minY = ny;
             }

             if (ny > maxY) {
                 maxY = ny;
             }
         }

         bbox.setX(minX);
         bbox.setY(minY);
         bbox.setWidth(maxX - minX);
         bbox.setHeight(maxY - minY);

         iconViewBox[i] = pad(bbox);
    }
[/prettify]

The getScreenCTM method of
the SVGLocatableElement class returns transformation
matrices that define affine transforms for mapping dimensional and
transformation information from SVG files into actual display units
applicable to the device.

Third Step

With the viewBox dimensions known, the frames can
be rendered. As we can see from the code snippet below, the process
of rendering is quite simple. For each frame an empty image is
created, and then the rendering is done using the
Graphics instance for that image. Note that the
"animation time" for the image is incremented by
frameLength (0.0625) seconds so that the next frame
rendered is the one that matches the image to be shown as per
animation settings given in the SVG document. In this case, we know
that there are nine frames to be shown during the animation of an
icon, and that the duration of animation as specified in the
optimizedSVGMenuG.svg file (located in the
\WTK25\apps\SVGDemo\res\svg folder) is 0.5 second. So the
time interval between two successive frames (the
frameLength) is 0.5 divided by 8. This is shown
below:

[prettify]
    // do the rendering
    int i = 0;

    for (int ri = 0; ri < numRows; ri++) {
        for (int ci = 0; ci < numCols; ci++, i++) {
            // Get the icon we want to draw
            SVGLocatableElement icon = 
                (SVGLocatableElement)iconVector.elementAt(i);

            // Now, set the icon's display to 'inline' 
            //before drawing
            // it to the offscreen.
            icon.setTrait("display", "inline");

            // "zoom" the icon
            if (iconViewBox[i] != null) {
                svg.setRectTrait("viewBox", iconViewBox[i]);
            }

            // Create a bitmap to draw into
            svg.setCurrentTime(0);

            for (int fi = 0; fi < numFrames; fi++) {
                menuIcons[ri][ci][fi] = 
                    Image.createImage(iconWidth, iconHeight);

                // Get a Graphics instance 
                //that we can draw into
                Graphics g = 
                    menuIcons[ri][ci][fi].getGraphics();
                g.setColor(255, 255, 255);
                g.fillRect(0, 0, iconWidth, iconHeight);
                sg.bindTarget(g);
                sg.render(0, 0, svgImage);
                sg.releaseTarget();

                svgImage.incrementTime(frameLength);
            }

            icon.setTrait("display", "none");
        }
    }
[/prettify]

Note that the tight bounding box returned by the
getBBox method is defined as "the smallest possible
rectangle that includes the geometry of all contained graphics
elements excluding stroke." Rendering an image as per the bounding
box dimensions would display the smallest possible image and would
not use the available screen area optimally. To ensure that the
entire display area is properly filled up, the viewBox
attribute has to be set so that image will be stretched to occupy
the entire viewport. To see what happens if this is not done, we
can comment out the following code:

[prettify]
    /*if (iconViewBox[i] != null) {
        svg.setRectTrait("viewBox", iconViewBox[i]);
    }*/
[/prettify]

If we now build SVGDemo and run the application, the
resulting display will be as shown in Figure 2.

Figure 2

Figure 2. Display without viewBox attribute

The Animation

Finally, it is time to see how the animation is performed. What
we have is an array containing nine frames covering the entire
animation sequence for each icon. We also have the following
variables:

  • The row index of the selected icon (updated by key presses): focusRow.
  • The column index of the selected icon (updated by key presses): focusCol.
  • An array of indices of the frame being currently shown for each icon: currFrame[row][col].

The animation is performed by a thread:

[prettify]
    // The following thread handles 
    //animating the currently focused item.
    final long frameLengthMs = (long)(frameLength * 1000);
    Thread th =
        new Thread() {
            public void run() {
                long start = 0;
                long end = 0;
                long sleep = 0;
                boolean interrupted = false;

                while (!interrupted) {

                     start = System.currentTimeMillis();

                     int cr = focusRow;
                     int cc = focusCol;
                     boolean needUpdate = false;

                     for (int ri = 0; ri < numRows; ri++) {
                        for (int ci = 0; ci < numCols; ci++) {
                            // Process icon (ri, ci)

                            // Frames are:
                            // [0] : unselected
                            // [1, numFramesFocusIn -1] : focusIn anim
                            // [numFramesFocus] : focused
                            int curFrame = currentFrame[ri][ci];

                            if((cr == ri)&&(cc == ci)) {
                                // We are processing the focused icon.
                                // If we are below the 
                                //focused frame, just 
                                //increase the frame index
                                if (curFrame < numFramesFocus) {
                                    // Move towards focused 
                                    //state on the focusIn animation
                                    curFrame += 1;
                                    needUpdate = true;
                                } else {
                                    // Do nothing, we are in 
                                    //the right frame already.
                                }
                            } else {
                                // We are _not_ on the 
                                //focused frame.
                                if (curFrame > 0) {
                                    curFrame -= 1;
                                    needUpdate = true;
                                }
                            }

                            currentFrame[ri][ci] = curFrame;
                        }
                    }

                    if (needUpdate) {
                        repaint();
                        serviceRepaints();
                    }

                    end = System.currentTimeMillis();
                    sleep = frameLengthMs - (end - start);

                    if (sleep < 10) {
                        sleep = 10;
                    }

                    try {
                        sleep(sleep);
                    } catch (InterruptedException ie) {
                        interrupted = true;
                    }
                }
            }
        };

    th.start();
[/prettify]

Each time the run method is executed, the current frame for the
selected icon is checked. If the current frame is less than 8,
animation of the selected icon is in progress. Accordingly, the
value of current frame is incremented by one and a
repaint is called. For unselected icons, the value of
the current frame should be 0. If it is higher than 0, then the
animation for deselection is in progress. In this case, the value
of the current frame is decremented by one and a repaint
is called. The thread then sleeps (approximately) for the time
period specified as the required interval between two frames before
reiterating through the loop.

The Canvas class has a repaint()
method just like the Canvas class in AWT. Unlike the
AWT version, however, Canvas here also has a
serviceRepaints() method that forces immediate
execution of a pending request for repaint and blocks until the
paint() method returns.

Conclusion

SVG is an extremely powerful platform, and that makes JSR 226 a
powerful API. All features of this combination cannot be covered in
one article; what I have tried to do here is provide a glimpse into
some of the possibilities. The Sun Java Wireless Toolkit for CLDC
2.5 comes with a number of MIDlets showing the capabilities of JSR
226. We have looked at only three of them. A study of the other
demos will give you an understanding of many interesting features
like zooming, panning, and layering.

Acknowledgment

The classes for "Static Pointer Demo" and "Moving Pointer Demo" have
been modelled on the CreateEmptyImageDemo and
SVGImageCanvas classes.

Resources

width="1" height="1" border="0" alt=" " />
Biswajit Sarkar is an electrical engineer with specialization in Programmable Industrial Automation. Biswajit is the author of "LWUIT 1.1 for Java ME Developers" published by PACKT Publishing.
Related Topics >> GUI   |   Mobility   |