6 タッチ・イベントの使用
このトピックでは、ユーザーがタッチ画面からJavaFXアプリケーションと対話するときに使用されるタッチ・イベントについて説明します。タッチ・ポイントは、タッチの各接触ポイントを表します。このトピックでは、タッチ・アクションに対して高度なレスポンスを返すために、タッチ・ポイントを識別し、タッチ・イベントを処理する方法を紹介します。
タッチ・アクションは、タッチ画面上の1つ以上の接触ポイントで構成されます。押下して解放するだけの単純なアクションもあれば、押下して解放するまでの間に一連のホールドや移動を伴う複雑なアクションもあります。アクションの実行中に個々の接触ポイントで一連のイベントが生成されます。タッチ・イベントだけでなく、マウス・イベントやジェスチャ・イベントも生成されます。JavaFXアプリケーションでタッチ・アクションに対して複雑なレスポンスを返す必要がなければ、タッチ・イベントではなく、マウス・イベントやジェスチャ・イベントを処理するのが望ましい場合もあります。ジェスチャ・イベントの処理の詳細は、「タッチ対応デバイスからのイベントの使用」を参照してください。
タッチ・イベントを扱うには、タッチ画面とWindows 7オペレーティング・システムが必要です。
タッチ・アクションの概要
タッチ・アクションという用語は、ユーザーのタッチ範囲全体(タッチ画面に触れてから、タッチ画面上のすべての接触ポイントから指が放されるまで)を指しています。タッチ・アクションの実行中に生成されるタッチ・イベントのタイプは、TOUCH_PRESSED
、TOUCH_MOVED
、TOUCH_STATIONARY
およびTOUCH_RELEASED
です。
画面上の各接触ポイントがタッチ・ポイントとみなされます。各タッチ・ポイントでタッチ・イベントが生成されます。タッチ・アクションに複数の接触ポイントが含まれる場合は、タッチ・アクションの各状態においてイベント・セット(タッチ・ポイントごとに1つのイベント)が生成されます。
これらの要素の詳細は、「タッチ・ポイント」、「タッチ・イベント」および「イベント・セット」の各項を参照してください。JavaFXアプリケーションでのタッチ・イベントの使用方法を示す例は、「タッチ・イベントの例」を参照してください。
タッチ・ポイント
ユーザーがタッチ画面にタッチすると、個々の接触ポイントでタッチ・ポイントが作成されます。タッチ・ポイントは、TouchPoint
クラスのインスタンスによって表され、接触ポイントの位置、状態およびターゲットに関する情報を持ちます。タッチ・ポイントの状態は、押下、移動、静止および解放です。
ヒント:
生成されるタッチ・ポイントの数は、タッチ画面により制限される場合があります。たとえば、タッチ画面で接触ポイントが2つしかサポートされていないのに、ユーザーが3本の指で画面にタッチしても、2つのタッチ・ポイントしか生成されません。この記事の中では、タッチ画面がすべての接触ポイントを認識するものと仮定しています。
各タッチ・ポイントがタッチ・アクションに追加されるときに、それらのタッチ・ポイントに順番にIDが割り当てられます。タッチ・ポイントのIDは、タッチ画面に触れて放すまで同じままです。接触ポイントから指が放された時点で、関連付けられているタッチ・ポイントはタッチ・アクションの一部ではなくなります。たとえば、2本の指でタッチ画面にタッチした場合、1つ目のタッチ・ポイントにはID 1が割り当てられ、2つ目のタッチ・ポイントにはID 2が割り当てられます。2本目の指をタッチ画面から放した場合、タッチ・ポイント1のみがタッチ・アクションの一部として残ります。別の指をタッチ・アクションに追加した場合、新しいタッチ・ポイントにID 3が割り当てられ、タッチ・アクションはタッチ・ポイント1および3で構成されることになります。
タッチ・イベント
タッチ・ポイントのアクションを追跡するためにタッチ・イベントが生成されます。タッチ・イベントは、TouchEvent
クラスのインスタンスによって表されます。タッチ・イベントは、タッチ画面へのタッチによってのみ生成されます。タッチ・イベントはトラックパッドからは生成されません。
タッチ・イベントは、他のイベントと同様に、発生するアクションを詳細に定義するソース、ターゲットおよびイベント・タイプを持ちます。タッチ・イベントのタイプは、TOUCH_PRESSED
、TOUCH_MOVED
、TOUCH_STATIONARY
およびTOUCH_RELEASED
です。移動距離やタッチ・ポイントでの静止時間に応じて、タッチ・ポイントに対して複数のTOUCH_MOVED
イベントおよびTOUCH_STATIONARY
イベントを生成できます。イベントおよびその処理方法の基本情報は、「イベントの処理」を参照してください。
タッチ・イベントには次の項目もあります。
-
タッチ・ポイント
該当するイベントに関連付けられているメイン・タッチ・ポイント
-
タッチ・カウント
タッチ・アクションに現在関連付けられているタッチ・ポイントの数
-
タッチ・ポイントのリスト
タッチ・アクションに現在関連付けられているタッチ・ポイントのセット
-
イベント・セットID
該当するイベントが含まれているイベント・セットのID
イベント・セット
タッチ・アクションに単一の接触ポイントが含まれる場合は、アクションの各状態において単一のタッチ・イベントが生成されます。タッチ・アクションに複数の接触ポイントが含まれる場合は、アクションの各状態においてタッチ・イベント・セットが生成されます。セット内の各タッチ・イベントは、それぞれ異なるタッチ・ポイントに関連付けられます。
各イベント・セットにはイベント・セットIDが割り当てられます。イベント・セットIDは、タッチ・アクションに対するレスポンスとしてセットが生成されるたびに1ずつ増分します。セット内の各イベントのイベント・タイプは、関連付けられているタッチ・ポイントの状態に応じてそれぞれ異なる場合があります。タッチ・アクションの実行中に接触ポイントが追加または削除された場合、イベント・セット内のイベントの数が変わります。例として、表6-1に、ユーザーが2本の指でタッチ画面にタッチし、両方の指を移動し、3本目の指でタッチ画面にタッチし、すべての指を移動し、最後に画面からすべての指を放す場合に生成されるイベント・セットを示します。
表6-1 単一のタッチ・アクションのイベント・セット
イベント・セットID | タッチ・イベントの数 | 各イベントのイベント・タイプ |
---|---|---|
1 |
1 |
|
2 |
2 |
|
3 |
2 |
|
4 |
3 |
|
5 |
3 |
|
6 |
3 |
|
7 |
3 |
|
8 |
3 |
|
9 |
2 |
|
10 |
1 |
|
タッチ・ポイントのターゲットとタッチ・イベントのターゲット
タッチ・イベントのターゲットは、そのイベントに関連付けられているタッチ・ポイントのターゲットになります。タッチ・ポイントの初期ターゲットは、タッチ画面上の最初の接触ポイントにある最上位ノードです。タッチ・アクションに複数の接触ポイントが含まれる場合は、各タッチ・ポイントおよび各タッチ・イベントのターゲットがそれぞれ異なっていてもかまいません。この機能を使用すると、各タッチ・ポイントを他のタッチ・ポイントから切り離して処理できます。例は、「同時タッチ・ポイントの個別処理」を参照してください。
通常は、1つのタッチ・ポイントのすべてのイベントが同じターゲットに配信されます。ただし、タッチ・ポイントに対してgrab()
およびungrab()
メソッドを使用することによって、後続イベントのターゲットを変更できます。
grab()
メソッドを使用すると、現在イベントを処理しているノード自体をタッチ・ポイントのターゲットにすることができます。grab(
target)
メソッドを使用すると、別のノードをタッチ・ポイントのターゲットにすることができます。イベント・セット内のイベントはそのセットのすべてのタッチ・ポイントにアクセスできるため、grab()
メソッドを使用してタッチ・アクションの後続イベントをすべて同じノードに配信できます。また、「タッチ・ポイントのターゲットの変更」に示すように、grab()
メソッドを使用してタッチ・ポイントのターゲットをリセットすることもできます。
ungrab()
メソッドを使用すると、現在のターゲットからタッチ・ポイントを解放できます。タッチ・アクションの後続イベントは、タッチ・ポイントの現在の位置にある最上位ノードに送信されるようになります。
タッチにより生成されるその他のイベント
ユーザーがタッチ画面にタッチすると、タッチ・イベント以外に次のタイプのイベントが生成されます。
-
マウス・イベント
マウス・イベントがシミュレートされるため、アプリケーションがタッチ・イベントに対応していない場合でも、タッチ画面付きのデバイスでアプリケーションを実行できます。マウス・イベントがタッチ・アクションによるものかどうかを判別するには、
isSynthesized()
メソッドを使用します。例は、「マウス・イベントの処理」を参照してください。 -
ジェスチャ・イベント
ジェスチャ・イベントは、一般に認識されているタッチ・アクション(スクロール、スワイプ、回転およびズーム)に対して生成されます。これらのタイプのタッチ・アクションのみをアプリケーションで処理する場合は、タッチ・イベントではなく、ジェスチャ・イベントを処理できます。ジェスチャ・イベントの詳細は、「タッチ対応デバイスからのイベントの使用」を参照してください。
タッチ・イベントの例
タッチ・イベントの例では、4つのフォルダを使用して、セット内の各タッチ・ポイントを個別に処理できることを例示します。この例では、grab()
メソッドを使用して、四角形内の円を別の四角形に繰り返し移動する方法も示します。図6-1に、この例のユーザー・インタフェースを示します。
タッチ・イベントの例は、TouchEventsExample.zip
ファイルで提供されています。NetBeansプロジェクトを抽出し、NetBeans IDEで開きます。タッチ・イベントを生成するには、タッチ画面付きのデバイスで例を実行する必要があります。
同時タッチ・ポイントの個別処理
一般的なジェスチャでは、すべての接触ポイントの中心にあるノードがターゲットになり、ジェスチャに対するレスポンスはその1つのノードのみに作用します。各タッチ・ポイントを個別に処理することによって、タッチされたすべてのノードを作用対象にすることができます。
タッチ・イベントの例では、フォルダにタッチして指を移動することによって各フォルダを移動できます。複数のフォルダをまとめて移動するには、各フォルダに別々の指でタッチし、すべての指を移動します。
各フォルダは、TouchImage
クラスのインスタンスです。TouchImage
クラスを使用して、イメージ・ビューを作成し、TOUCH_PRESSED
、TOUCH_RELEASED
およびTOUCH_MOVED
イベントに対するイベント・ハンドラを追加します。例6-1に、このクラスの定義を示します。
例6-1 TouchImageクラスの定義
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(); } }); } }
フォルダにタッチすると、各接触ポイントにタッチ・ポイントが作成され、タッチ・イベントがそのフォルダに送信されます。フォルダ上に複数の接触ポイントが存在する場合でもフォルダからのレスポンスが1回のみになるように、タッチIDが使用されています。
TOUCH_PRESSED
イベントが渡された場合、タッチIDがチェックされ、今回のタッチがそのフォルダに対して初めて行われたものかどうかが確認されます。その場合、タッチIDがタッチ・ポイントのIDに設定され、タッチ・ポイントの位置が保存されます。
TOUCH_RELEASED
イベントが渡された場合、タッチIDがチェックされ、処理中のタッチ・ポイントと一致することが確認されます。その場合、タッチIDがリセットされ、処理が完了します。
TOUCH_MOVED
イベントが渡された場合、タッチIDがチェックされ、処理中のタッチ・ポイントと一致することが確認されます。その場合、フォルダが新しいタッチ・ポイントの位置に移動されます。タッチIDがタッチ・ポイントと一致しない場合は、フォルダ上に複数の接触ポイントが存在する可能性があります。同じフォルダが複数回移動されないように、イベントは無視されます。
タッチ・ポイントのターゲットの変更
タッチ・ポイントのターゲットになるノードは、通常、タッチ・アクション全体を通して同じになります。ただし、状況によっては、タッチ・アクションの実行中にタッチ・ポイントのターゲットを変更できます。
タッチ・イベントの例では、1本目の指で円にタッチし、2本目の指で四角形にタッチすることによって、四角形間で円を移動できます。移動後、2本目の指を円上に置いたまま、1本目の指を持ち上げて別の四角形にタッチすると、円が再び移動されます。このアクションは、2つ目のタッチ・ポイントのターゲットを変更した場合にのみ実行できます。
円は、Ball
クラスのインスタンスです。Ball
クラスを使用して、円を作成し、TOUCH_PRESSED
、TOUCH_RELEASED
、TOUCH_MOVED
およびTOUCH_STATIONARY
イベントに対するイベント・ハンドラを追加します。TOUCH_MOVED
およびTOUCH_STATIONARY
イベントにも同じハンドラを使用します。例6-2に、このクラスの定義を示します。
例6-2 Ballクラスの定義
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); } }
TOUCH_PRESSED
イベントが渡された場合、タッチ・ポイントの数がチェックされ、タッチされているのがBall
クラスのインスタンスのみであることが確認されます。その場合、タッチ・ポイントの位置が保存され、円が選択されていることを示すためにライティング効果が追加されます。
TOUCH_RELEASED
イベントが渡された場合、円がもう選択されていないことを示すためにライティング効果が削除されます。
TOUCH_MOVED
またはTOUCH_STATIONARY
イベントが渡された場合、移動に必要な次の条件がチェックされます。
-
タッチ・カウントは2である必要があります。
該当するイベントに関連付けられているタッチ・ポイントが移動の始点とみなされます。このイベントはタッチ・アクションのすべてのタッチ・ポイントにアクセスできます。タッチ・ポイント・セット内の2つ目のタッチ・ポイントが移動の終点とみなされます。
-
2つ目のタッチ・ポイントの状態は
PRESSED
です。円は、2つ目の接触ポイントが作成された場合にのみ移動されます。2つ目のタッチ・ポイントの状態がそれ以外の場合は無視されます。
-
2つ目のタッチ・ポイントのターゲットは四角形です。
円は、長方形間または長方形内でのみ移動できます。2つ目のタッチ・ポイントのターゲットがそれ以外の場合は、円は移動されません。
移動の条件を満たしている場合、円は2つ目のタッチ・ポイントの位置に移動されます。再び移動するには、1つ目の接触ポイントを解放し、円の移動先にする3つ目の位置にタッチします。ただし、1つ目の接触ポイントを解放すると、円をターゲットとしていたタッチ・ポイントがなくなり、円にタッチ・イベントが渡されなくなります。2回目の移動は、両方の指を持ち上げ、新しい移動を開始しないかぎり実行できません。
2本目の指を円上に置いたまま新しい位置にタッチすることによって2回目の移動を行うためには、grab()
メソッドを使用して円を2つ目のタッチ・ポイントのターゲットにします。grabを実行すると、2つ目のタッチ・ポイントのイベントが四角形(元のターゲット)ではなく円に送信されるようになります。その後、円は、新しいタッチ・ポイントが作成されたときに再び移動されます。