Implementing multipoint touch functionality

You can implement multipoint touch functionality on Series 40 full touch devices using:

Multipoint touch and the Multipoint Touch API are supported on Series 40 full touch devices with Java Runtime 2.0.0 for Series 40 or newer. For information about multipoint touch on Symbian devices, see section Multipoint touch.

Multipoint Touch API

The Multipoint Touch API allows MIDlets to implement multipoint touch functionality on Canvas and Canvas-based elements, such as GameCanvas and FullCanvas, and on CustomItem. The API registers multipoint touch interaction as multipoint touch events, each of which involves one or more touch points ("pointers"). Each touch point corresponds to a finger pressed against the screen. The API keeps track of each touch point separately, meaning MIDlets can track individual fingers as they move across the screen. Each touch point is associated with a unique ID, current x and y coordinates, and a current state, which can be one of the following:

  • POINTER_PRESSED indicates that a finger is pressed against the screen, thus creating a new touch point. This corresponds to a touch action. This state is not registered for stops during a drag action.

  • POINTER_DRAGGED indicates that a pressed finger is being dragged over the screen. This corresponds to a drag action.

    If the user stops dragging but keeps the finger pressed against the screen, the state of the touch point is still registered as POINTER_DRAGGED. However, the coordinates of the touch point do not change from its preceding POINTER_DRAGGED state. Thus, to handle stops during a drag action, check whether the coordinates of a dragged touch point change between multipoint touch events.

  • POINTER_RELEASED indicates that a pressed finger is lifted from the screen, thus ending the touch point. This corresponds to a release action.

You can retrieve the current state and coordinates of a touch point at any time, not just during a multipoint touch event. However, you need the ID of the touch point, which you can only retrieve from a multipoint touch event involving the touch point.

Tip: If you want to implement a simple pinch action, use the Gesture API instead, since it provides a ready-made pinch implementation.

Different devices support a different number of simultaneous touch points. To determine the maximum number of simultaneous touch points supported by a device, call the static MultipointTouch.getMaxPointers method.

For an example MIDlet that shows you how to implement multipoint touch functionality using the Multipoint Touch API, see Paint.

Multipoint Touch API contents

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

  • MultipointTouch

    Use the MultipointTouch class to access touch point information, determine the maximum number of simultaneous touch points supported by a device, or register or remove a MultipointTouchListener.

  • MultipointTouchListener

    Use the MultipointTouchListener interface to implement a listener for receiving a notification whenever a multipoint touch event occurs.

The Multipoint Touch API also provides the com.nokia.mid.ui.multipointtouch.version system property, which MIDlets can use to query the Multipoint Touch API version supported by the device.

The Multipoint Touch API is supported since Nokia UI API 1.6.

Implementing multipoint touch functionality using the Multipoint Touch API

To implement multipoint touch functionality using the Multipoint Touch API:

  1. Check that the Multipoint Touch API is supported on the device by querying the com.nokia.mid.ui.multipointtouch.version system property:

    if (System.getProperty("com.nokia.mid.ui.multipointtouch.version") != null) {
        // API is supported: Can implement multipoint touch functionality
    } else {
        // API is not supported: Cannot implement multipoint touch functionality
    }
  2. Retrieve the MultipointTouch singleton instance by calling the static getInstance method:

    MultipointTouch mpt = MultipointTouch.getInstance();

    The singleton instance is only needed for registering (and removing) a multipoint touch event listener.

  3. Register a MultipointTouchListener for the MIDlet by calling the MultipointTouch.addMultipointTouchListener method. The MultipointTouchListener notifies the MIDlet of multipoint touch events.

    The following code snippet registers an MPTCanvas object as a MultipointTouchListener. The MPTCanvas class is assumed to extend Canvas and implement MultipointTouchListener.

    MPTCanvas mptCanvas = new MPTCanvas();
    mpt.addMultipointTouchListener(mptCanvas);
  4. Create the UI element for which you want to implement the multipoint touch functionality.

    The following code snippet creates the framework for a Canvas class called MPTCanvas that implements MultipointTouchListener.

    // Import the necessary classes
    import com.nokia.mid.ui.multipointtouch.MultipointTouch;
    import com.nokia.mid.ui.multipointtouch.MultipointTouchListener;
    import javax.microedition.lcdui.*;
    // Import other classes required by the Canvas implementation
    // ...
    
    public class MPTCanvas extends Canvas implements CommandListener, MultipointTouchListener {
    
        // Implement Canvas functionality and MultipointTouchListener
        // ...
    
    }
  5. Implement the MultipointTouchListener registered in step 3. The MultipointTouchListener interface defines the pointersChanged method, which is called every time the platform triggers a multipoint touch event. A multipoint touch event involves a change in one or more touch points, that is, a change in their coordinates or state, or both. The pointersChanged method returns the IDs of the changed touch points. Use these IDs to access the current coordinates and states of the changed touch points, and use the coordinate and state information to update the MIDlet UI accordingly.

    The following code snippet implements a pointersChanged method that calls separately defined methods for updating the MIDlet UI in response to the multipoint touch event. (The implementation of the update methods is not part of this example.) The code snippet assumes that the containing class is set to implement MultipointTouchListener.

    public void pointersChanged(int[] pointerIds) {
    
        int pointerId;
        int x;
        int y;
        int state;
    
        // Loop through the changed touch points
        for(int i=0; i<pointerIds.length; i++) {
    
            // Get the touch point ID
            pointerId = pointerIds[i];
    
            // Get the touch point state
            state = MultipointTouch.getState(pointerId);
    
            // Get the touch point x and y coordinates
            x = MultipointTouch.getX(pointerId);
            y = MultipointTouch.getY(pointerId);
    
            // Handle the UI update based on the touch point state,
            // ID, and coordinates
            switch(state) {
                case MultipointTouch.POINTER_PRESSED:
                    // A new finger was pressed against the screen
                    updatePress(pointerId, x, y);
                    break;
                case MultipointTouch.POINTER_DRAGGED:
                    // A pressed finger was dragged over the screen
                    updateDrag(pointerId, x, y);
                    break;
                case MultipointTouch.POINTER_RELEASED:
                    // A pressed finger was lifted from the screen
                    updateRelease(pointerId, x, y);
                    break;
                default:
                    break;
            }
            
        }
        
    }

Pointer event methods

You can use the following LCDUI Canvas pointer event methods on Series 40 full touch devices to register the x and y coordinates of two or more simultaneous touch points:

  • pointerPressed is called when a finger is pressed against the screen, thus creating a new touch point. This corresponds to a touch action.

  • pointerDragged is called when a pressed finger is being dragged over the screen. This corresponds to a drag action.

  • pointerReleased is called when a pressed finger is lifted from the screen, thus ending the touch point. This corresponds to a release action.

Using the pointer event methods ensures compatibility with multipoint touch MIDlets developed for Symbian devices. However, the Series 40 implementation differs from the Symbian implementation in two important ways:

  • The com.nokia.pointer.number system property is not supported on Series 40 devices. This means that you cannot use the system property to track individual touch points. To receive multipoint touch events in your MIDlet with these pointer event methods, you must implement your own logic. For example, you must implement your own logic for buffering the touch points when you need to display simultaneous touches on the screen.

    For an example implementation, see section Example: Implementing multipoint touch functionality using the pointerPressed method.

  • Multipoint touch events are enabled by default on Series 40 devices. You do not need to set the EnableMultiPointTouchEvents value for the Nokia-UI-Enhancement attribute in the MIDlet JAD file (unless, of course, you also want to run the MIDlet on Symbian devices).

Different devices support a different number of simultaneous touch points. To determine the maximum number of simultaneous touch points supported by a device, call the static MultipointTouch.getMaxPointers method.

Tip: If you do not need to maintain compatibility with Symbian devices, use the Multipoint Touch API instead, as it provides a ready-made implementation for handling multipoint touch interaction.

Example: Implementing multipoint touch functionality using the pointerPressed method

This example demonstrates how concurrent touches using two fingers can be displayed on the screen as small dots at the coordinate points the user has tapped on. The logic can be easily expanded to match the number of maximum pointers supported by the device.

A TimerTask is used to repetitively wait for very short intervals until a buffer array storing the coordinates of the multitouch points is filled. The paint method is then called to draw all the points sequentially to the screen.

        inactivityCounter = new Point[2]; //2-point multi-touch implementation
        inactivityTimer = new Timer();
        inactivityTimer.schedule(
                new TimerTask() {

                    public void run() {
                    	//if one or more points are waiting to be painted
                        if(current >=0 ) {   
                            repaint();
                            
                        } 
                    }
                },
                0,
                20); //20 millisecond period for gathering points

This logic is necessary because concurrency issues arise inside the paint method when the painted points are pressed, dragged or released.

The inactivityCounter array is the buffer array storing the multitouch points. Point is a custom made class storing the x and y coordinates that define the touched point on the screen:

    class Point {

        public int x;
        public int y;

        public Point(int x, int y) {
            this.x = x;
            this.y = y;
        }
        
        public int getX() {
        	return x;
        }
        
        public int getY() {
        	return y;
        }
        
    }

The pointerPressed method fills the inactivityCounter array with points.

	protected void pointerPressed(int x, int y) {
		current ++; //the total number of points is increased per point press
		inactivityCounter[current] = new Point(x, y); //adds this point to the buffer array 

	}

Finally the paint method is called from the scheduled TimerTask to draw blue-painted circles on the screen.

Note: The paint method is called for the first time when the canvas is activated and displayed, and the array has not yet been filled with points. So a control should be in place to prevent any attempt to traverse the array which would result in a NullPointerException.

	public void paint(Graphics g){
		g.setColor(0, 0, 255);
		/* This control ensures that no null pointer exceptions occurs 
		 * the first time the canvas is displayed and painted when
		 * the buffer array is not yet filled with points 
		 */
		if(current >= 0) {
			//paints the current item
			g.fillArc(inactivityCounter[current].x, inactivityCounter[current].y, 10, 10, 0, 360);
			current--; //one point less to be painted
		}	
	}