This tutorial describes how we can use the component input processors included in MT4j to detect and react to multi-touch gestures.

Contents

Goals

  • understand how we can enable multi-touch gesture functionality on a component
  • learn how to write your own gesture listener
  • learn about all relevant methods and what they do

Setting up multi-touch gesture functionality

To make mt4j components aware of multi-touch gestures, first we have to register component input processors with that component. The component input processors process the input events targeted at that component, process and analyze them for certain patterns or conditions and fire gesture events back to the component if a multi-touch gesture was detected.

Next, to specify the reaction to the gesture events, we add a gesture listener to the component. In the processGestureEvent(MTGestureEvent ge) method of that listener, we can specify the behaviour for that gesture.

As an example, setting up multi-touch drag functionality for a component can be achieved by following code:

Example:

component.registerInputProcessor(new DragProcessor(mtApplication));
component.addGestureListener(DragProcessor.class, new IGestureEventListener() {
        public boolean processGestureEvent(MTGestureEvent ge) {
                DragEvent de = (DragEvent)ge;
                de.getTargetComponent().translateGlobal(de.getTranslationVect()); //Moves the component
                switch (de.getId()) {
                case MTGestureEvent.GESTURE_STARTED:
                        System.out.println("Drag gesture on component " + de.getTargetComponent() + " started");
                        break;
                case MTGestureEvent.GESTURE_UPDATED:
                        System.out.println("Drag gesture on component " + de.getTargetComponent() + " updated");
                        break;
                case MTGestureEvent.GESTURE_ENDED:
                        System.out.println("Drag gesture on component " + de.getTargetComponent() + " ended");
                        break;
                default:
                        break;
                }         
                return false;
        }
});

As we can see, when we add the gesture listener we also pass the input processor's class as an argument. This way, the listener recieves gesture events only if the source of the event is the specified input processor (in this example a DragProcessor). This is why we can safely cast the MTGestureEvent object to a DragEvent object, because the DragProcessor will only send this type of event. Of course we can add more than one gesture listener to a component for additional gesture behaviour.
The id of a MTGesture event object has one of the three values:

  • GESTURE_DETECTED (now renamed to GESTURE_STARTED)
  • GESTURE_UPDATED
  • GESTURE_ENDED

indicating the current state of the gesture.

For often needed gesture actions, there already exist default implementations of gesture listeners which can be used, (e.g. DefaultDragAction, DefaultRotateAction, DefaultScaleAction, DefaultButtonClickAction etc.) So setting up drag functionality can actually be accomplished with 2 lines of code.

component.registerInputProcessor(new DragProcessor(mtApplication));
component.addGestureListener(DragProcessor.class, new DefaultDragAction());

Default input processors

It is important to note, that all MT4j shapes which are derived from the AbstractShape class (MTRectangle, MTPolygons, MTTriangleMesh etc) have the drag, rotate and scale processors already registered with corresponding gesture listeners by default. So be careful not to add the same input processors or gesture listeners twice to avoid strange behaviour!
To disable the creation and use of the default gestures for all shapes we can disable them globally using this command:

        AbstractShape.createDefaultGestures = false;


Through use of the input processors, the multi-touch gestures can be used completely modular. So if we want a component to be dragable and rotatable but not scalable, we add only the first two input processors and corresponding gesture listeners (or we remove the scale processor if already registered).

As a little more complex example we will turn a simple MTRectangle component into a button by adding a TapProcessor to it. We will also add a custom gesture listener to it which will change the color of the rectangle if we tap it.

Example:

        MTRectangle r = new MTRectangle(0,0, 100,100, pa);
        r.unregisterAllInputProcessors(); //Remove the default drag, rotate and scale gestures first
        r.registerInputProcessor(new TapProcessor(pa));
        r.addGestureListener(TapProcessor.class, new IGestureEventListener() {
                        public boolean processGestureEvent(MTGestureEvent ge) {
                                TapEvent te = (TapEvent)ge;
                                IMTComponent3D target = te.getTargetComponent();
                                if (target instanceof MTRectangle) {
                                        MTRectangle rectangle = (MTRectangle) target;
                                        switch (te.getTapID()) {
                                        case TapEvent.BUTTON_DOWN:
                                                System.out.println("Button down state on " + target);
                                                rectangle.setFillColor(new MTColor(200,100,100));
                                                break;
                                        case TapEvent.BUTTON_UP:
                                                System.out.println("Button up state on " + target);
                                                rectangle.setFillColor(new MTColor(255,255,255));
                                                break;
                                        case TapEvent.BUTTON_CLICKED:
                                                System.out.println("Button clicked state on " + target);
                                                rectangle.setFillColor(new MTColor(255,255,255));
                                                break;
                                        default:
                                                break;
                                        }
                                }
                                return false;
                        }
                });
        getCanvas().addChild(r);


As you might have noticed, we dont check the ID of the gesture event with the getId() method as usual but rather the TapID of the TapEvent. This is because the GESTURE_ENDED state is ambiguous with tapping as we dont know if it was really a tap/click or a button up event (BUTTON_UP state is triggered when the finger is lifted too far away from the position we first put the finger down). We also cast the event's target component to a MTRectangle object to be able to change its color.

Removing registered input processors

Removing an input processor from a component can be achieved by using the unregisterInputProcessor(AbstractComponentProcessor inputProcessor) method.
We can also remove a gesture listener with the removeGestureEventListener(, ) method.
For convenience, if we dont want the component to react to any multi-touch gestures we can use the methods unregisterAllInputProcessors() and removeAllGestureEventListeners() which do what their names imply.

If we for example just wanted to completely remove the scale gesture functionality from a MTComponent, we would do the following:

for (AbstractComponentProcessor ip : component.getInputProcessors()){
                        if (ip instanceof ScaleProcessor) {
                                component.unregisterInputProcessor(ip);
                        }
                }
                component.removeAllGestureEventListeners(ScaleProcessor.class);
This will remove both the scale input processor and the listeners.

Powered by MediaWiki contact