Wheel or Touch

この記事は、JavaFX Advent Calendar 2015 の 4 日目の記事です。

昨日は蓮沼さんの MSI/EXE インストーラ版 Scene Builder を HiDPI 対応させるには です。明日は y_q1m さんです。

この Advent Calendar の 2 日目の id:torutk さんの 記事 の中で、マウスホイールでもスクロールイベントが発生してしまって困るとありました。えっ、そんなの簡単に判別できるのにと思って、ツィートしようと思ったのですが、いや待て、blog にした方が Advent Calendar が埋まるということで、ここに書いてますww

みんな ScrollEvent だけでタッチかホイールか判別しようとしているのが間違っているんですよ。ScrollEvent は GestureEvent のサブクラスで、GestureEvent はジェスチャーのイベントを扱うクラスです。

でも、ジェスチャーなんて高レベルな API なわけですよ。もっと低レベルで扱えばいいのです。

ようするに、タッチのイベントである TouchEvent を使うのです。

ちょっとしたテストプログラムを書いてみました。

public class WheelOrTouch extends Application {

    private boolean touch;

    @Override
    public void start(Stage stage) throws Exception {
        Pane pane = new Pane();
        pane.setPrefSize(600, 400);

        // タッチの開始
        pane.setOnTouchPressed(event -> {
            touch = true;
        });

        // タッチの終了
        pane.setOnTouchReleased(event -> {
            touch = false;
        });

        pane.setOnScroll(event -> {
            // タッチの有無でジェスチャーかホイールを判別
            if (touch) {
                System.out.println("TOUCH " + event.getEventType());
            } else {
                System.out.println("WHEEL " + event.getEventType());
            }
        });

        Scene scene = new Scene(pane);
        stage.setScene(scene);
        stage.show();
    }

    public static void main(String... args) {
        launch(args);
    }
}

これで、タッチしてみました。結果はこう。

TOUCH SCROLL
TOUCH SCROLL
TOUCH SCROLL
TOUCH SCROLL
TOUCH SCROLL
TOUCH SCROLL
WHEEL SCROLL
WHEEL SCROLL
WHEEL SCROLL
WHEEL SCROLL

あれっ、最後に WHEEL SCROLL が出てしまってる!!

と予定調和的につっこんでみました。

ここで、WHEEL SCROLL が出ているのは、慣性スクロールのためです。そして、慣性スクロールかどうかは GestureEvent クラスの isInertia メソッドで判別できます。

ということで、ちょっと変更したのがこちら。

public class WheelOrTouch extends Application {

    private boolean touch;

    @Override
    public void start(Stage stage) throws Exception {
        Pane pane = new Pane();
        pane.setPrefSize(600, 400);

        // タッチの開始
        pane.setOnTouchPressed(event -> {
            touch = true;
        });

        // タッチの終了
        pane.setOnTouchReleased(event -> {
            touch = false;
        });

        pane.setOnScroll(event -> {
            // タッチの有無、もしくは慣性スクロールでジェスチャーかホイールを判別
            if (touch || event.isInertia()) {
                System.out.println("TOUCH " + event.getEventType());
            } else {
                System.out.println("WHEEL " + event.getEventType());
            }
        });

        Scene scene = new Scene(pane);
        stage.setScene(scene);
        stage.show();
    }

    public static void main(String... args) {
        launch(args);
    }
}

これで、タッチなのかホイールなのか判別できます。タッチした結果がこちら。

TOUCH SCROLL
TOUCH SCROLL
TOUCH SCROLL
TOUCH SCROLL
TOUCH SCROLL
TOUCH SCROLL

マウスホイールした結果がこちら。

WHEEL SCROLL
WHEEL SCROLL
WHEEL SCROLL
WHEEL SCROLL

ちゃんと判別できました!!

でも、getTouchCount で判別した方が簡単かなぁ...