Back to documentBack to Document

TouchEvents.java
Legal Terms and Copyright Notice
/*
 * Copyright (c) 2012 Oracle and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the distribution.
 *  - Neither the name of Oracle nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

                            
package touchevents;

import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.effect.DropShadow;
import javafx.scene.effect.Lighting;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.input.TouchEvent;
import javafx.scene.input.TouchPoint;
import javafx.scene.paint.Color;
import javafx.scene.paint.CycleMethod;
import javafx.scene.paint.RadialGradient;
import javafx.scene.paint.Stop;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;

/**
 *
 * Sample that shows how touch events can be used. UI contains four folders
 * and a row of blocks with a circle on one of the blocks. The folders
 * can be moved independently by touching one or more simultaneously
 * (number of folders that can be moved at one time can be limited by the
 * number of touch points the screen supports).
 * The circle can be moved from block to block by first touching the circle
 * and then touching an empty block. Jumping can continue until the finger
 * over the circle is removed.
 * 
 */
public class TouchEvents extends Application {
    
    @Override
    public void start(Stage primaryStage) {
         Group root = new Group();
         final TouchImage img1 = new TouchImage(50, 20,
                 new Image(TouchEvents.class.getResource(
                 "images/folder002.png").toExternalForm(), false));
         final TouchImage img2 = new TouchImage(350, 20,
                 new Image(TouchEvents.class.getResource(
                 "images/folder004.png").toExternalForm(), false));
         final TouchImage img3 = new TouchImage(150, 200,
                 new Image(TouchEvents.class.getResource(
                 "images/folder006.png").toExternalForm(), false));
         final TouchImage img4 = new TouchImage(450, 200,
                 new Image(TouchEvents.class.getResource(
                 "images/folder007.png").toExternalForm(), false));
        
         root.getChildren().addAll(img1, img2, img3, img4);
         
         for (int i = 1; i <= 5; i++) {
             Rectangle pad = new Rectangle(100, 85, Color.BLANCHEDALMOND);
             pad.setTranslateX((i*125-100));
             pad.setTranslateY(470);
             pad.setStroke(Color.GRAY);
             
             root.getChildren().add(pad);
         } 
         
         root.getChildren().add(new Ball(75, 512));
         
         Scene scene = new Scene(root, 650, 600);
         scene.setFill(Color.GAINSBORO);
        
         primaryStage.setTitle("Touch Events Example");
         primaryStage.setScene(scene);
         primaryStage.show();
    }
    
    /**
     * Image that can be dragged by a finger on a touch screen
     */
    public static class TouchImage extends ImageView {
        private long touchId = -1;
        double touchx, touchy;
        

        public TouchImage(int x, int y, Image img) {
            super(img);
            setTranslateX(x);
            setTranslateY(y);
            setEffect(new DropShadow(8.0, 4.5, 6.5, Color.DARKSLATEGRAY));
            
            setOnTouchPressed(new EventHandler<TouchEvent>() {
                @Override public void handle(TouchEvent event) {
                    if (touchId == -1) {
                        touchId = event.getTouchPoint().getId();
                        touchx = event.getTouchPoint().getSceneX() - getTranslateX();
                        touchy = event.getTouchPoint().getSceneY() - getTranslateY();
                    }
                    event.consume();
                }
            });

            setOnTouchReleased(new EventHandler<TouchEvent>() {
                @Override public void handle(TouchEvent event) {
                    if (event.getTouchPoint().getId() == touchId) {
                        touchId = -1;
                    }
                    event.consume();
                }
            });

            setOnTouchMoved(new EventHandler<TouchEvent>() {
                @Override public void handle(TouchEvent event) {
                    if (event.getTouchPoint().getId() == touchId) {
                        setTranslateX(event.getTouchPoint().getSceneX() - touchx);
                        setTranslateY(event.getTouchPoint().getSceneY() - touchy);
                    }
                    event.consume();
                }
            });
        }
    }
    
    /**
     * Ball that can jump from one rectangle to another by touching the
     * ball on a touch screen with one finger and touching another rectangle
     * with a second finger. As long as the finger currently over the ball is
     * not lifted, the ball can be jumped again.
     */
    private static class Ball extends Circle {
        double touchx, touchy;

        public Ball(int x, int y) {
            super(35);
            
            RadialGradient gradient = new RadialGradient(0.8, -0.5, 0.5, 0.5, 1,
                true, CycleMethod.NO_CYCLE, new Stop [] {
                    new Stop(0, Color.FIREBRICK),
                    new Stop(1, Color.BLACK)
                });
            
            setFill(gradient);
            setTranslateX(x);
            setTranslateY(y);

            setOnTouchPressed(new EventHandler<TouchEvent>() {
                @Override public void handle(TouchEvent event) {
                    if (event.getTouchCount() == 1) {
                        touchx = event.getTouchPoint().getSceneX() - getTranslateX();
                        touchy = event.getTouchPoint().getSceneY() - getTranslateY();
                        setEffect(new Lighting());
                    }
                    event.consume();
                }
            });

            setOnTouchReleased(new EventHandler<TouchEvent>() {
                @Override public void handle(TouchEvent event) {
                    setEffect(null);
                    event.consume();
                }
            });

            // Jump if the first finger touched the ball and is either
            // moving or still, and the second finger touches a rectangle
            EventHandler<TouchEvent> jumpHandler = new EventHandler<TouchEvent>() {
                @Override public void handle(TouchEvent event) {

                    if (event.getTouchCount() != 2) {
                        // Ignore if this is not a two-finger touch
                        return;
                    }

                    TouchPoint main = event.getTouchPoint();
                    TouchPoint other = event.getTouchPoints().get(1);

                    if (other.getId() == main.getId()) {
                        // Ignore if the second finger is in the ball and
                        // the first finger is anywhere else
                        return;
                    }

                    if (other.getState() != TouchPoint.State.PRESSED ||
                            other.belongsTo(Ball.this) ||
                            !(other.getTarget() instanceof Rectangle) ){
                        // Jump only if the second finger was just 
                        // pressed in a rectangle
                        return;
                    }

                    // Jump now
                    setTranslateX(other.getSceneX() - touchx);
                    setTranslateY(other.getSceneY() - touchy);

                    // Grab the destination touch point, which is now inside
                    // the ball, so that jumping can continue without
                    // releasing the finger
                    other.grab();

                    // The original touch point is no longer of interest, so
                    // call ungrab() to release the target
                    main.ungrab();

                    event.consume();
                }
            };

            setOnTouchStationary(jumpHandler);
            setOnTouchMoved(jumpHandler);
        }
    }
   
    /**
     * The main() method is ignored in correctly deployed JavaFX application.
     * main() serves only as fallback in case the application can not be
     * launched through deployment artifacts, e.g., in IDEs with limited FX
     * support. NetBeans ignores main().
     *
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        launch(args);
    }
}