◆PROCESSING 逆引きリファレンス
カテゴリー:スマホ(AndroidMode)
タップを検知するには(AndroidMode編)
【概要】
PROCESSINGにAndroidMode を導入する事で、PROCESSINGで開発したプログラムをAndroid端末上で動かす事ができるようになります。
AndroidModeの導入については「PROCESSINGをAndroid端末で動かすには(4.0版)」記事を参照してください。
タップとは、指先でポンと画面をたたく操作です。スマホの最も基本的な操作方法になります。
「シングルタップ」は1本の指だけで画面をたたく操作、「マルチタップ」は複数の指で同時に画面をたたく操作です。
(画像URL:illust-AC 様:かみたまさん、amiyaoさん)
AndroidModeで画面のタップを検知するには、以下の2種類の方法があります。
- タッチ系イベント関数を利用する
- surfaceTouchEvent 関数を利用する
タッチ系イベント関数には、以下の3種類があります。
- タップの開始を検知:touchStarted
- タップの終了を検知:touchEnded
- タップの中止を検知:touchCancelled
これ以外にも touchMoved という関数がありますが、これはスワイプやフリックなど、画面の上で指が動かされたことを検知する関数になります。
surfaceTouchEvent 関数はタッチ系イベント関数のように、操作毎に処理が分かれておらず、タップ開始、終了などのイベントが全てこの関数に通知されてきます。
surfaceTouchEvent は、タッチ系イベント関数に比べて詳細な情報を得ることができます。
またダブルタップやフリックなど、より高度な操作を検知したい場合も、surfaceTouchEvent 関数を利用する事になります。
マルチタップなどを考慮するなら、タッチ系イベント関数よりも surfaceTouchEvent 関数を利用する事をオススメします。
【詳細】
タップ開始
タップ開始イベントvoid touchStarted( TouchEvent touchEvent ) ;
touchEvent : イベント内容
画面をタップした際に呼び出されます。シングルタップ、マルチタップのどちらでも、同じイベント関数が呼ばれます。
渡されてくる TouchEvent クラスのインスタンス変数を利用する事で、タップされた指の数や座標を知ることができます。
float x = touchEvent . getPointer( int index ).x ;
float y = touchEvent . getPointer( int index ).y ;
tapNum : タップされた数
index : 何番目のタップ情報かを示す指標(0以上 tapNum-1 まで)
x : タップされた横座標
y : タップされた縦座標
TouchEvent には様々なメソッドやプロパティがありますが、よく使うのは上記3つではないかと思います。
getNumPointers が1ならシングルタップ、2以上ならマルチタップされたという事です。
getNumPointers でタップされた数(渡されてくる Pointer 情報の数)を取得し、該当Pointerからタップされた座標を拾う形になります。
マルチタップの場合、(ほぼ同時に、全ての指でタップしたとしても) getNumPointers の数が1→ 2→3と変化しながら、touchStarted 関数が複数回呼ばれてきます。
タップ終了
タップ終了イベントvoid touchEnded( TouchEvent touchEvent ) ;
touchEvent : イベント内容
タップが終了した(画面から指が離れた)際に呼び出されます。
渡されてくる TouchEvent クラスのインスタンス変数を利用する事で、指が離れる直前の数や、その座標を知ることができます。
float x = touchEvent . getPointer( int index ).x ;
float y = touchEvent . getPointer( int index ).y ;
tapNum : 指が離れる直前の数
index : 何番目のタップ情報かを示す指標(0以上 tapNum-1 まで)
x : タップされた横座標
y : タップされた縦座標
使い方は touchStarted イベント関数の場合と、ほぼ同じです。
getNumPointersで渡される数は、指が離れる直前の指の数です。4本でタップしている状態から指を1本離すと、4が渡ってきます。現在の指の数(3)ではないので注意して下さい。
マルチタップの場合、(ほぼ同時に、全ての指を離したとしても) getNumPointers の数が3→ 2→0と変化しながら、touchEnded 関数が複数回呼ばれてきます。
touchEnded イベント関数は全ての指が離されると、getNumPointersで得られる数が 0で呼ばれる事に注意してください。
(画像URL:illust-AC 様:かみたまさん、casa4さん)
最後に touchCancelled イベント関数についてですが、普通に操作していても、なかなか呼び出されるタイミングに遭遇しません。
(おそらくですが)タップ開始と終了が同時に行われた際に発生するようです。もしも touchCancelled が投げられたら、touchEnded と同じ扱いにするのが良いと思います。
surfaceTouchEventを使う
タップイベントboolean surfaceTouchEvent( MotionEvent motionEvent ) ;
motionEvent : イベント内容
渡されてくる情報はTouchEvent ではなく、MotionEvent クラスのインスタンス変数です。
MotionEvent は、Android 標準のonTouchEventに渡されてくるものと同じものになります。
Androidのタッチイベントについては、下記サイト様などが非常に参考となりました。ありがとうございます。
- Android Studioでいこう 様
- 雑食プログラミング備忘録 様
- TECHBOOSTER 様
- Qiita 様:Androidでマルチタッチを使う @tobira-codeさん
int pIndex = motionEvent . getActionIndex() ;
int eAction= motionEvent . getActionMasked() ;
float x = motionEvent . getX( int pIndex ) ;
float y = motionEvent . getY( int pIndex ) ;
pcount: Pointer 配列数(タップされている指の数)
pIndex : イベントが発生したPointer配列の位置(0 ~ pcount – 1 まで)
eAction: イベントの種類
x : タップされた横座標
y : タップされた縦座標
getPointerCount で渡される数は、タップ開始系イベント(ACTION_DOWN、ACTION_POINTER_DOWN)では、タップされている指の数(Pointer情報の数)です。マルチタップが行われると、getPointerCount は2以上の数を戻します。
タップ終了系イベント(ACTION_POINTER_UP、ACTION_UP)では、指が離される直前の数になります(2本から1本になったら2が渡される)。
複数の指でタップした場合、それぞれのタップ位置はPointer 配列に格納されます。getActionIndex で得られる値は、イベントが発生したPointer配列の位置を示しています。
(画像URL:illust-AC 様:かみたまさん、casa4さん)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
public boolean surfaceTouchEvent(MotionEvent event) { int eAction = event.getActionMasked(); //イベントの種類 int pIndex = event.getActionIndex(); //Pointer配列の位置 int pcount = event.getPointerCount(); //Pointer配列の数 switch (eAction) { case MotionEvent.ACTION_DOWN: case MotionEvent.ACTION_POINTER_DOWN: //タップイベントが発生した座標 float x = event.getX( pIndex ); float y = event.getY( pIndex ); println( "Pointer Array Count=" + pcount ); println( "Action Locate=" + x + "/" + y ); break; } return super.surfaceTouchEvent(event); } |
プログラムで書くとこんな感じです。上記例では、イベントが発生した座標と、その時Pointer配列に覚えている情報数を表示します。
surfaceTouchEvent は画面をタップした時だけではなく、指を動かしたり離した時にも呼び出されます。
ですのでどんな操作が行われたかを知るには、 上記例のように getActionMasked 命令を使って自分で判定する必要があります。
タップに関連するACTIONには、以下のものがあります。
ACTION | 意味 |
---|---|
ACTION_DOWN (0) | 最初に指を置いた時に発生します。 |
ACTION_POINTER_DOWN (5) | 2本め以降の指を置いた時に発生します。 |
ACTION_MOVE(2) | 指を動かした時に発生します。 |
ACTION_POINTER_UP (6) | 複数の指のうち、1つを離した時に発生します。 |
ACTION_UP (1) | 全ての指を離した時に発生します。 |
ACTION_CANCEL (3) | キャンセル時(DOWN+UP ?)に発生。 |
各ACTIONは、MotionEventクラスのstatic定数として定義されています。
int pIndex= motionEvent.findPointerIndex ( int pId ) ;
pId: タップに付与された一意の ID
pIndex: Pointer 配列の位置(0 ~ pcount – 1 まで)
あと覚えておいたほうが良いのは、Pointer IDについてです。
複数の指でタップした場合、それぞれのタップ位置はPointer 配列に格納されます。この Pointer 配列にアクセスするために用いられるのがPointer Indexだというのは説明した通りです。
ですがこのPointer Index はPointer 配列の添字でしかないため、タップ情報(指)を一意に特定する事ができません。
たとえば人差し指(A)と中指(B)で画面にタップします。この時発生するsurfaceTouchEvent イベントで、Pointer 配列の0番にA、Pointer 配列の1番にBの座標が格納されたとしましょう。
ところが、この指が動いた時に送られるsurfaceTouchEvent イベントでは、Pointer 配列の0番にAの指座標が格納されているとは限りません。
(画像URL:illust-AC 様:かみたまさん、casa4さん)
では指を特定するにはどうすればよいかというと、Pointer ID を利用します。
Pointer ID には、タップが開始されてから終了するまで、タップ毎(指毎)に一意の番号が割り振られるためです。
ある指(たとえば最初に画面にタップした指)のイベントだけを処理したいといった場合は、Pointer ID を見ながら処理する必要があります。
getPointerId 命令を使うと、イベントが発生したPointer 配列に格納されている Pointer ID を知る事が可能です。
その逆で、Pointer ID から Pointer 配列の格納位置(Pointer Index)を知るには、findPointerIndex 命令を使います。
surfaceTouchEventの全般的な注意
surfaceTouchEventを利用する際は、関数の戻り値に注意してください。
surfaceTouchEvent関数は boolean 型の値を戻す必要がありますが、false を戻すと、今処理したイベントに続くタッチイベントが全て破棄されます。
例えば、指を置いて動かして離した場合、指をおいた際に発生するsurfaceTouchEvent関数で false を戻すと、全ての指が離されるまでイベントが発生しなくなります。
またPROCESSINGが内部で保持している mouseX、mouseYなどの変数を更新させたい場合は、surfaceTouchEvent関数の最後で
1 |
super.surfaceTouchEvent(motionEvent); |
というように、上位のタッチイベントを呼び出す必要があります。
surfaceTouchEvent関数を利用した際にこれを行わないと、タッチ系イベント関数も呼び出されなくなります。
これらの事を考慮すると、本関数は
1 2 3 4 5 6 7 |
public boolean surfaceTouchEvent(MotionEvent motionEvent) { //なにかの処理 … //最後は上位のタッチイベントを呼び出して終わる(Trueが戻る) return super.surfaceTouchEvent(motionEvent); } |
といった形式で利用するのが良いでしょう。
【関連記事】
- ダブルタップを検知するには(AndroidMode編)
- 長押しを検知するには(AndroidMode編)
- スワイプを検知するには(AndroidMode編)
- フリック検知するには(AndroidMode編)
- ピンチを検知するには(AndroidMode編)
- ローテートを検知するには(AndroidMode編)
- 様々なタッチ操作を検知するには(Ke:tai.lib 編)
サンプルプログラム
タッチイベントを利用する例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
import android.util.Log; public static final String TAG = "AM-MOUSE"; /** * Android Mode タッチ Sample * @author MSLABO * @version 1.0 */ PFont font; //フォント int touchCount; //タッチ数 public void setup() { fullScreen(); //フォントを読み込む font = createFont("ipag.ttf", 48 ); textSize( 64 ); textFont( font ); textAlign( LEFT, TOP ); //現在のタッチ数を初期化 touchCount = 0; } public void touchStarted(TouchEvent touchEvent) { Log.d( TAG, "touchStarted " + touchEvent.getNumPointers() ); //タッチされている数を加算 touchCount++; } public void touchEnded(TouchEvent touchEvent) { Log.d( TAG, "touchEnded " + touchEvent.getNumPointers() ); //タッチされている数を減算 touchCount = touchCount - 1; //0が渡されたらすべての指が離れたと考える if( touchEvent.getNumPointers() == 0 ){ touchCount = 0; } } public void draw() { background(255); fill( 0 ); text( "タッチ数:" + touchCount, 0, 0 ); } |
タッチ系イベントを利用して、現在画面に触れている指の数を表示します。
特定の指を追従する例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 |
import android.util.Log; import android.view.MotionEvent; public static final String TAG = "AM-MOUSE"; /** * Android Mode タッチ Sample * @author MSLABO * @version 1.1 */ PFont font; //フォント int touchCount; //タッチ数 int mPointerID1, mPointerID2; // ポインタID記憶用 PVector p1, p2; //座標記憶用 public void setup() { fullScreen(); //フォントを読み込む font = createFont("ipag.ttf", 80 ); textSize( 80 ); textFont( font ); textAlign( LEFT, TOP ); //座標初期化 p1 = new PVector(0,0); p2 = new PVector(0,0); } public boolean surfaceTouchEvent(MotionEvent event) { int eAction = event.getActionMasked(); //イベントの種類 int pIndex = event.getActionIndex(); //Pointer配列の位置 int pId = event.getPointerId( pIndex );//Pointer ID //イベントが発生した座標 float px = event.getX( pIndex ); float py = event.getY( pIndex ); switch (eAction) { case MotionEvent.ACTION_DOWN: //最初にタップされた指のIDと座標を記録 p1.x = px; p1.y = py; mPointerID1 = pId; mPointerID2 = -1; break; case MotionEvent.ACTION_POINTER_DOWN: //MAX2本までタップされた指のIDと座標を記録する if( mPointerID1 == -1 ){ //1本目が、改めてタップされた場合 p1.x = px; p1.y = py; mPointerID1 = pId; } else if( mPointerID2 == -1 ){ //2本目が、タップされた場合 p2.x = px; p2.y = py; mPointerID2 = pId; } break; case MotionEvent.ACTION_MOVE: //タップ中の指の座標を取得して記録する if( mPointerID1 > -1 ){ //指ID1から配列添え字を得て、座標取得 int ptrIndex = event.findPointerIndex(mPointerID1); p1.x = event.getX(ptrIndex); p1.y = event.getY(ptrIndex); } if( mPointerID2 > -1 ){ //指ID2から配列添え字を得て、座標取得 int ptrIndex = event.findPointerIndex(mPointerID2); p2.x = event.getX(ptrIndex); p2.y = event.getY(ptrIndex); } break; case MotionEvent.ACTION_POINTER_UP: //離れた指と同じIDをもつ情報をクリアする if( mPointerID1 == pId ){ mPointerID1 = -1; p1.x = p1.y = 0; } else if( mPointerID2 == pId ){ mPointerID2 = -1; p2.x = p2.y = 0; } break; case MotionEvent.ACTION_UP: //全部の情報をクリアする mPointerID1 = -1; mPointerID2 = -1; p1.x = p1.y = p2.x = p2.y = 0; break; } return super.surfaceTouchEvent(event); } public void draw() { background(255); fill(0); //座標を表示する text( "1:" + p1.x + "/" + p1.y, 0, 0 ); text( "2:" + p2.x + "/" + p2.y, 0, 80 ); } |
最大2箇所まで、画面の上に置かれたタップ位置を追跡し、座標をリアルタイムに表示します。
タップの特定に、getPointerIdで得たIDを利用しています。またタップ位置に対応するPointer配列の添字を得るために、findPointerIndex 命令を使っています。
なお、本プログラムは 「雑食プログラミング備忘録 」様に紹介されていたものを、AndroidMode用に改編して掲載させて頂きました。ありがとうございます。
本ページで利用しているアイコン画像は、下記サイト様より拝借しております。各画像の著作権は、それぞれのサイト様および作者にあります。