Creating drag and scroll animations

The Frame Animator API allows MIDlets to trigger linear drag and kinetic scroll animations in response to drag and flick gestures. The Frame Animator API does not draw the UI updates for an animation, but rather calculates the motion interpolation for the animation. To draw the animation, use the standard LCDUI methods for rendering graphics.

Use the Frame Animator API together with the Gesture API. For example MIDlets that show you how to do this, see sections Example: Creating a scroller list and Touch UI - FavoriteArtists.

API contents

The Frame Animator API consists of the following classes and interfaces (packaged as part of the Nokia UI API):

  • FrameAnimator

    Use the FrameAnimator class to trigger drag and scroll animations.

  • FrameAnimatorListener

    Use the FrameAnimatorListener interface to implement a listener for receiving a notification for each new frame in the animation. Use the notifications to define the corresponding UI update.

The Frame Animator API is supported since Nokia UI API 1.1b.

Device compatibility

The Frame Animator API is supported since Series 40 6th Edition FP1.

Using drag and scroll animations

Note: The following instructions and code snippets only focus on the Frame Animator API. For instructions on creating a full MIDlet, see sections Getting started and Implementing MIDlet lifecycle requirements. For instructions on using the Canvas class in a MIDlet, see sections Canvas and Example: Creating a simple test MIDlet.

To use drag and scroll animations in your MIDlet:

  1. Create a UI element that receives drag and flick events, and define handlers for these events. For instructions, see sections Receiving gesture events and Handling gesture events.

  2. Register a FrameAnimatorListener for the UI element using the FrameAnimator.register method. The FrameAnimatorListener notifies the MIDlet of each frame update in the triggered animation.

    The following code snippet registers a MyFrameAnimatorListener (see step 4 of this example) for a custom Canvas class called GestureCanvas.

    // Import the necessary classes
    import com.nokia.mid.ui.frameanimator.FrameAnimator;
    import com.nokia.mid.ui.gestures.GestureInteractiveZone;
    import com.nokia.mid.ui.gestures.GestureRegistrationManager;
    import javax.microedition.lcdui.Canvas;
    
    public class GestureCanvas extends Canvas {
    
        // FrameAnimator for registering a FrameAnimatorListener
        private FrameAnimator frameAnimator;
    
        // Variables for keeping track of the animation coordinates
        private int currentX;
        private int currentY;
    
        public GestureCanvas() {
    
            // Register a GestureListener and GestureInteractiveZone
            // ...
    
            // Specify int referenceX, int referenceY, short maxFps, short maxPps
            // ...
    
            this.currentX = referenceX;
            this.currentY = referenceY;
    
            // Create a MyFrameAnimatorListener instance
            MyFrameAnimatorListener myFrameAnimatorListener = new MyFrameAnimatorListener(this);
    
            // Create a FrameAnimator instance and
            // use it to register the MyFrameAnimatorListener instance
            frameAnimator = new FrameAnimator();
            frameAnimator.register(referenceX,
                                   referenceY,
                                   maxFps,
                                   maxPps,
                                   myFrameAnimatorListener);
    
        }
    
        // Return and update the animation coordinates
        public int getCurrentX() {
            return this.currentX;
        }
        public void setCurrentX(int newX) {
            this.currentX = newX;
        }
        public int getCurrentY() {
            return this.currentY;
        }
        public void setCurrentY(int newY) {
            this.currentY = newY;
        }
    
        // Return the FrameAnimator instance
        public FrameAnimator getFrameAnimator() {
            return this.frameAnimator;
        }
    
        // Create the Canvas UI by implementing the paint method and
        // other necessary methods (see the LCDUI instructions)
        // ...
    
    }

    Registering a FrameAnimatorListener requires that you specify the following values:

    • referenceX specifies the starting x-coordinate for the animation. The x-coordinates passed to the FrameAnimatorListener.animate method are initially calculated from referenceX.

    • referenceY specifies the starting y-coordinate for the animation. The y-coordinates passed to the FrameAnimatorListener.animate method are initially calculated from referenceY.

    • maxFps specifies the maximum number of frames per second (fps) for the animation. This controls how often the FrameAnimatorListener.animate method is called per second at maximum. The value is a percentage of the default platform value for maximum fps. To retrieve the default platform value, use the com.nokia.mid.ui.frameanimator.fps system property:

      defaultMaxFps = (Short)Integer.parseInt(System.getProperty("com.nokia.mid.ui.frameanimator.fps"));

      The supported maxFps values are 0-200. 0 and 100 indicate that the default platform value is used.

      For example, if your MIDlet can only handle 10 fps, but the native platform rate is 20 fps, ensure that maxFps is set to 50 or less. A value of 50 indicates 50% of the native platform rate of 20 fps, amounting to 10 fps, the MIDlet's maximum fps.

    • maxPps specifies the maximum number of pixels per second (pps) for a kinetic scroll animation. This controls the distance in pixels between consecutive animation frame updates. The value is a percentage of the default platform value for maximum pps. To retrieve the default platform value, use the com.nokia.mid.ui.frameanimator.pps system property.

      defaultMaxPps = (Short)Integer.parseInt(System.getProperty("com.nokia.mid.ui.frameanimator.pps"));

      The supported maxPps values are 0-200. 0 and 100 indicate that the default platform values is used.

      For example, if your MIDlet can only handle 10 pps, but the native platform rate is 20 pps, ensure that maxPps is set to 50 or less. A value of 50 indicates 50% of the native platform rate of 20 pps, amounting to 10 pps, the MIDlet's maximum pps.

  3. Trigger the animations from the corresponding gesture event handlers:

    • To trigger a linear drag animation, use the FrameAnimator.drag method.

    • To trigger a kinetic scroll animation, use the FrameAnimator.kineticScroll method.

    Calling the methods results in one or more calls to the FrameAnimatorListener.animate method, which describes the animation for the platform to draw (see step 4 of this example).

    The following code snippet triggers the animations directly from a GestureListener implementation called MyGestureListener.

    // Import the necessary classes
    import com.nokia.mid.ui.frameanimator.FrameAnimator;
    import com.nokia.mid.ui.gestures.GestureEvent;
    import com.nokia.mid.ui.gestures.GestureInteractiveZone;
    import com.nokia.mid.ui.gestures.GestureListener;
    
    public class MyGestureListener implements GestureListener {
    
        public void gestureAction(Object container,
                                  GestureInteractiveZone gestureInteractiveZone,
                                  GestureEvent gestureEvent) {
    
            // Cast the container as a GestureCanvas for easy access
            GestureCanvas gestureCanvas = (GestureCanvas)container;
    
            // Retrieve the FrameAnimator instance from the UI element
            FrameAnimator frameAnimator = gestureCanvas.getFrameAnimator();
    
            // Trigger the correct animation for the received event
            switch (gestureEvent.getType()) {
                case GestureInteractiveZone.GESTURE_DRAG: {
    
                    // Set the target coordinates for the animation by
                    // retrieving the current animation coordinates
                    // and adding the drag distance to them
                    int newX = gestureCanvas.getCurrentX() + gestureEvent.getDragDistanceX();
                    int newY = gestureCanvas.getCurrentY() + gestureEvent.getDragDistanceY();
    
                    frameAnimator.drag(newX, newY);
    
                }; break;
                case GestureInteractiveZone.GESTURE_FLICK: {
                    frameAnimator.kineticScroll(gestureEvent.getFlickSpeed(),
                                                FrameAnimator.FRAME_ANIMATOR_FREE_ANGLE,
                                                FrameAnimator.FRAME_ANIMATOR_FRICTION_MEDIUM,
                                                gestureEvent.getFlickDirection());
                }; break;
            }
    
        }
    
    }

    For more information about implementing the GestureListener interface, see section Handling gesture events.

    Tip: If you find that a drag animation works too slowly and does not match the physical drag action, consider implementing counters for keeping track of the distance moved after each animate call, and then using these counters to align the animation with the corresponding drag events. For instructions on how to create the counters, see article Getting started with the Gesture API & FrameAnimator API in the Nokia Developer Wiki.

  4. Implement the FrameAnimatorListener registered in step 2. Use the FrameAnimatorListener.animate method to define the UI updates for the animation. The animate method is called every time the MIDlet calls either FrameAnimator.drag or FrameAnimator.kineticScroll. For a kinetic scroll animation, the animate method is called at regular intervals with the updated position and other information describing the current state of the animation, that is, its current frame.

    The following code snippet creates the framework for the MyFrameAnimatorListener custom class.

    // Import the necessary classes
    import com.nokia.mid.ui.frameanimator.FrameAnimator;
    import com.nokia.mid.ui.frameanimator.FrameAnimatorListener;
    
    public class MyFrameAnimatorListener implements FrameAnimatorListener {
    
        private GestureCanvas gestureCanvas;
    
        public MyFrameAnimatorListener(GestureCanvas gestureCanvas) {
            this.gestureCanvas = gestureCanvas;
        }
    
        // Implement the animate method
        public void animate(FrameAnimator frameAnimator,
                            int x,
                            int y,
                            short delta,
                            short deltaX,
                            short deltaY,
                            boolean lastFrame) {
    
            // Update the current animation coordinates
            this.gestureCanvas.setCurrentX(x);
            this.gestureCanvas.setCurrentY(y);
    
            // Refresh the UI based on received animation events
            // ...
    
        }
    
    }

    Each animate call returns the following parameters:

    • frameAnimator specifies the FrameAnimator instance that triggered the animation.

    • x specifies the current x-coordinate of the animation. For a kinetic scroll animation, this value is initially calculated from referenceX used in registering the FrameAnimatorListener.

    • y specifies the current y-coordinate of the animation. For a kinetic scroll animation, this value is initially calculated from referenceY used in registering the FrameAnimatorListener.

    • delta specifies the distance in pixels from the previous position. This indicates the distance moved since the previous animate call.

    • deltaX specifies the distance in pixels between the current x-coordinate and the x-coordinate from the previous animate call. This indicates the horizontal distance moved since the previous animate call.

    • deltaY specifies the distance in pixels between the current y-coordinate and the y-coordinate from the previous animate call. This indicates the vertical distance moved since the previous animate call.

    • lastFrame specifies whether this is the end of the current animation. true indicates that this is the last frame of the animation, while false indicates that this is not the last frame.

    After the animation has ended, the last coordinates are stored in memory for as long as the FrameAnimatorListener remains registered. Therefore a new animation using the same FrameAnimatorListener instance begins where the last one ended.