JavaFX Advent Calendar 2012 1日目 JavaFXでのマルチタッチアプリケーション開発

JavaFX Advent Calendarの1日目を担当することになってしまった@ / id:aoe-tkです。

実は前日の第8回JavaFX勉強会にて、JavaFXでのマルチタッチプログラミングについての発表を行いました。このエントリではその内容のフォローアップをしたいと思います。
当日の発表資料は以下です。

JavaFXでマルチタッチプログラミング from Takashi Aoe

また、デモに使ったサンプルアプリケーションのコードもgistにアップしています。
https://gist.github.com/4143183

JavaFX2.2で追加されたマルチタッチ関連のAPIについての補足

発表でもお話ししたように、JavaFX2.2になってタッチ関連のAPIが追加されました。Developer Previewの時に、特にリリースノートでの通知がなく、さりげなくJavaDocに追記されていたので驚いたのを覚えています。
JavaFX2.2新機能紹介ページ (日本OracleのエンベデッドJavaチームによる日本語訳です) では次のように紹介されています。

タッチ操作可能な機器のためのマルチタッチサポート。これは現段階では主にデスクトップクラスのタッチスクリーンやタッチパッド向けですが、将来的にはARMベースのチップ上にJava SE Embeddedが搭載されているさまざまな組み込み機器に洗練されたUIを導入することを可能にします。例えばキオスク端末、テレメトリシステム、ヘルスケア機器、多機能プリンタ、監視システムなどです。これらは、多くのアプリケーション開発者にはまだあまり注目されていないJavaアプリケーションの市場セグメントですが、近年活況を呈しつつあります。

当日の発表でも少し触れましたが、主に組み込み機器をメインターゲットにしているように見受けられます。こういったセグメントにもこれからAndroidが進出してくるでしょうから、その前にしっかり押さえておこうとOracleは考えているのでしょう。
もちろんiOSWindows8/RT/Phone、Android向けの開発も視野には入れているとは思いますが。

さて、追加されたAPIの内容についてですが、発表でも説明したように、javafx.scene.input パッケージに基本的なタッチ操作を扱う TouchEvent と、タッチ操作を組み合わせたより抽象度の高い GestureEvent というイベントが追加されています。
そして、Scene 及び Node にこれらイベントに対するハンドラを登録するためのプロパティが追加されています。

以下に時間の関係で当日の発表で話せなかったことを列挙します。

  • TouchEvent について
    • 複数点タッチされた場合、個々のEventには eventSetId が割り振られます。
    • control、alt、meta、shiftキーとのコンビネーションも検出可能です。(isAltDown() とか isShiftDown() といったメソッドがある)
    • 他のノードにイベントをコピーする copyFor() というメソッドがあります。
    • 何点同時にタッチされているかは getTouchCount() メソッドで取得できます。
    • タッチポイントの情報は TouchPoint クラスに格納されますが、TouchPoint には次のようなAPIが用意されています。
      • getX()/getY() はタッチの対象となる Node オブジェクト上の座標を、getSceneX()/getSceneY() は Scene 上の座標を、getScreenX()/getScreenY() は画面上の絶対座標を取得でします。
      • タッチポイントのイベントターゲットとなる Node オブジェクトを切り替えることのできる grabbing API というものがあり、grab() メソッドで切り替えることができる...らしいです。(ごめんなさい、まだ調べ切れていません)
  • RotateEvent について
    • マルチタッチをサポートしているトラックパッドならば、トラックパッドでもこのイベントを起こすことができます。トラックパッドで起こした場合はマウスカーソルの位置が基準になります。
  • ScrollEvent について
    • じつはマウスホイールによるスクロールでもこのイベントは発生します。ただし、マウスホイールの場合は SCROLL タイプのイベントのみ発生します。
    • 移動量については基本的に deltaX/Y で見ますが、テキスト系コンポーネントの場合は textDeltaX/Y でも取得可能なようです。単位は行単位とページ単位があるようで、getTextDeltaUnits() でどちらの単位なのかを取得できます。
    • getTouchCount() でタッチ数を取得できます。これを使うことで2本指スクロールや3本指スクロールが実現できます。

デモアプリケーションについての補足

デモでは次の3つのサンプルをお見せしました。デモが失敗したときに備えて録画しておいた動画がありますので、それも一緒にアップしておきます。

  • TouchEvent を使い、タッチされたポイントに対してサークルを描画するデモ。描画したサークルはタッチで移動でき、ダブルタップで消去可能。

  • GestureEvent のうち、Scroll/Rotate/ZoomEvent を利用したデモ。描画した Rectangle に対して、これらイベントから取得した値を使って、簡単に移動、変形が可能であることを示しました。

  • JavaFXのコントロールのスクロール対応のデモ。ListView コントロールとJavaFX2.2になって追加された Pagination コントロールを使いました。これらは何もしなくてもスクロール操作に対応しています。

スライドではスペースの都合上、コードをかなり圧縮して載せていました。上のgistにアップしたコードの方を良く確認してもらえたらと思います。
以下にちょっと補足を。

  • Node には移動や変形を行うための scaleX/YrotatetranslateX/Y といったプロパティがあります。GestureEvent で取得できる移動量はこれらプロパティにそのまま適用できるようになっています。
  • Pagination コントロールや ListView コントロールには、javafx.scene.text.Font.getFamilies() メソッドを使って取得した、システムがサポートしているフォント名の一覧を渡しています。
  • Pagination コントロールに表示するページの内容は次のように VBox を作って、その中にラベルを並べています。Pagination コントロールはパフォーマンスに考慮した作り方ができるようなAPIになっていて、個人的に気に入っています。
    @Override
    public void initialize(URL url, ResourceBundle rb) {
        fonts = Font.getFamilies();
        pagination.setPageCount(fonts.size() / FONTS_PER_PAGE);
        pagination.setPageFactory(new Callback<Integer, Node>() {
            @Override
            public Node call(Integer idx) {
                return createPage(idx);
            }
        });
        lvFonts.setItems(FXCollections.observableArrayList(fonts));
    }

    private VBox createPage(int idx) {
        VBox box = new VBox(5.0);
        int page = idx * FONTS_PER_PAGE;
        for (int i = page; i < page + FONTS_PER_PAGE; i++) {
            Label lblFont = new Label(fonts.get(i));
            box.getChildren().add(lblFont);
        }
        return box;
    }

このようにJavaFX2.2ではマルチタッチを容易に開発可能なAPIが追加されています。今のところまともに使える環境はWindows8くらい (しかもデスクトップモード) なのがちょっと悲しいですが、みなさんもどんどん使ってみてください。