◆PROCESSING 逆引きリファレンス
カテゴリー:スマホ(AndroidMode)
ダブルタップを検知するには(AndroidMode編)
【概要】
PROCESSINGにAndroidMode を導入する事で、PROCESSINGで開発したプログラムをAndroid端末上で動かす事ができるようになります。
AndroidModeの導入については「PROCESSINGをAndroid端末で動かすには(4.0版)」記事を参照してください。
ダブルタップとは、指でトントンと素早く2回画面をたたく操作です。叩いている途中で指を動かしてはいけません。同じ箇所を素早く連続してたたくのがポイントです。
(画像URL:illust-AC 様:かみたまさん、amiyaoさん)
AndroidModeで画面のダブルタップを検知するには、surfaceTouchEvent 関数とAndroid SDKの GestureDetector # SimpleOnGestureListener を併用して判別します。
surfaceTouchEvent 関数やタッチ系イベント関数( touchStarted など)だけを利用しても頑張れば検知可能ですが、GestureDetector クラスを併用した方が楽ちんです(笑)。
GestureDetector クラスの詳細については「スワイプを検知するには(AndroidMode編)」記事を参照して下さい。
GestureDetector クラスの利用方法について、ポイントだけ紹介すると
- SimpleOnGestureListener を継承したクラス(A)を用意する
- setup()関数で Aクラスのインスタンスを生成する
- surfaceTouchEventでAクラスのonTouchEventを呼び出す
といった処理を行うことになります。
SimpleOnGestureListener を継承したクラス内で onDoubleTapメソッドやonDoubleTapEvent メソッドをオーバライドする事で、ダブルタップを検知することができるようになります。
【詳細】
ダブルタップイベント
boolean onDoubleTapEvent( MotionEvent e) { }
e : 発生したMotionEvent
SimpleOnGestureListener を継承したクラスで、それぞれのメソッドをオーバライドします。
オーバライドするので、上位(super)のイベントを呼び出し、かつ Trueでリターンするのが一般的です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
//ジェスチャー検知クラス //SimpleOnGestureListener クラスを継承した専用のクラスを用意する class MyGestureListener extends GestureDetector.SimpleOnGestureListener { @Override public boolean onDoubleTap(MotionEvent e) { //あれやこれや、行いたい処理を記述 return super.onDoubleTap(e); } @Override public boolean onDoubleTapEvent(MotionEvent e) { //あれやこれや、行いたい処理を記述 return super.onDoubleTapEvent(e); } } |
こんな感じです。
2つあるメソッドの違いですが、onDoubleTap はダブルタップが発生した瞬間に1回だけ呼び出されます。onDoubleTapEvent は、ダブルタップの発生時、その後で指を動かした場合、離した場合に呼び出されます。
注意点は、これらのイベントはマルチタップに対応していない事です。2本以上の指で同時にダブルタップするとイベントが呼び出されません。
複数の指によるダブルタップを検知したい場合は、surfaceTouchEvent 関数やタッチ系イベント関数( touchStarted など)を使って、自力で処理する必要があります。
【関連記事】
- タップを検知するには(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 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 |
import android.app.Activity; import android.content.Context; import android.os.Handler; import android.os.Looper; import android.view.GestureDetector; import android.view.MotionEvent; /** * PROCESSING AndroidMode DoubleTap Sample * @author MSLABO * @version 1.0 */ Activity act; //Activity Context con; //Context GestureDetector gestureDetector; //ジェスチャーOBJ //ジェスチャー検知クラス //SimpleOnGestureListener クラスを継承した専用のクラスを用意する class MyGestureListener extends GestureDetector.SimpleOnGestureListener { //ダブルタップ検知1 @Override public boolean onDoubleTap(MotionEvent e) { println( "onDoubleTap RUN" ); return super.onDoubleTap(e); } //ダブルタップ検知2 @Override public boolean onDoubleTapEvent(MotionEvent e) { int action = e.getActionMasked(); switch(action){ case MotionEvent.ACTION_DOWN: println( "onDoubleTapEvent ACTION_DOWN" ); break; case MotionEvent.ACTION_MOVE: println( "onDoubleTapEvent ACTION_MOVE" ); break; case MotionEvent.ACTION_UP: println( "onDoubleTapEvent ACTION_UP" ); break; default: println( "onDoubleTapEvent action =" + action ); break; } return super.onDoubleTapEvent(e); } } public void setup() { fullScreen(); //ActivityとContextを取得 act = getActivity(); con = act.getApplicationContext(); //UIスレッドのLooperと連携したHandlerを作成して、GestureDetectorに与える Handler mHandler = new Handler(Looper.getMainLooper()); gestureDetector = new GestureDetector(con, new MyGestureListener(), mHandler); } public void draw() { background( 200 ); } @Override public boolean surfaceTouchEvent(MotionEvent motionEvent) { //GestureDetectorにイベントを渡す gestureDetector.onTouchEvent(motionEvent); return super.surfaceTouchEvent(motionEvent); } |
ダブルタップを検知して、コンソール領域に情報を表示します。
マルチタップでダブルタップを検知する例:
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 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 |
import android.app.Activity; import android.content.Context; import android.graphics.Point; import android.view.MotionEvent; import java.util.Iterator; /** * PROCESSING AndroidMode DoubleTap Sample * @author MSLABO * @version 1.1 */ Activity act; //Activity Context con; //Context TapMng tapMng; //タップ管理クラス /** * Tap管理クラス */ public class TapMng{ final float FIRE_DISTANCE = 60; //反応距離 final long FIRE_SPEED = 250 ; //反応速度 final long REMOVE_MILLS = 1200; //削除時間(ms) ArrayList<Point> tapPoint; //タップ座標 ArrayList<Long> startMills; //タップ開始時間(ms) ArrayList<Integer> tapCount; //タップ回数 Object lock; //排他オブジェクト /** * コンストラクタ */ public TapMng(){ //タップ座標など初期化 lock = new Object(); tapPoint = new ArrayList<Point>(); startMills = new ArrayList<Long>(); tapCount = new ArrayList<Integer>(); } /** * 反応速度以上経過した位置情報を削除する */ void removeTap(){ long nowMills = System.currentTimeMillis(); //削除作業の一貫性保障 synchronized(lock){ Iterator<Long> it = startMills.iterator(); while(it.hasNext()){ Long stMil = it.next(); if( nowMills - stMil > REMOVE_MILLS ){ //一定時間以上経過した項目を削除する int index = startMills.indexOf( stMil ); it.remove(); //同じ位置にある座標と回数の項目も削除する tapPoint.remove(index); tapCount.remove(index); } } } } /** * タップ座標を記録し、ダブルタップか判定する * @param e タップイベント */ void addTap(MotionEvent e){ int pIndex = e.getActionIndex(); int x = (int)e.getX( pIndex ); //今回のタップ位置X int y = (int)e.getY( pIndex ); //今回のタップ位置Y long mills = System.currentTimeMillis(); //今回のタップ時時刻 boolean newFlg = true; //新規タップだよFLGをON //判定作業の一貫性保障 synchronized(lock){ for( int index = 0; index < tapPoint.size(); index++ ){ Point p = tapPoint.get(index); float sx = p.x; //前回のタップ位置X float sy = p.y; //前回のタップ位置Y //前回と今回で、タップされた位置同士の距離を計算する float dist = sqrt( sq(abs(sx-x)) + sq(abs(sy-y)) ); if( dist < FIRE_DISTANCE ){ //前回タップ位置に近い場所をタップされたので、既存タップとみなす long sm = startMills.get(index); if( (mills - sm) < FIRE_SPEED ){ //反応速度以下で素早くタップされたので、ダブルタップとみなす tapCount.set( index, 2); } newFlg = false; //新規タップだよFLGをOFF break; } } } //新規座標のタップの場合 if( newFlg ){ //追加作業の一貫性保障 synchronized(lock){ //タップ座標、時間などを記録する tapPoint.add(new Point( x, y )); //座標記録 startMills.add(mills); //時間記録 tapCount.add(1); //回数は1回め } } } /** * シングルタップ、ダブルタップの座標位置に青丸を描く */ void dispTap(){ //表示作業の一貫性保障 synchronized(lock){ for( int index = 0; index < tapPoint.size(); index++ ){ int count = tapCount.get(index); if( count == 1 ){ //シングルタップ noFill(); stroke( 0 ); ellipse( tapPoint.get(index).x, tapPoint.get(index).y, 30, 30); } else { //ダブルタップ fill(color(50,50,255)); stroke( 0 ); ellipse( tapPoint.get(index).x, tapPoint.get(index).y, 30, 30); } } } } } public void settings() { //フルスクリーン fullScreen(); } public void setup() { //ActivityとContextを取得 act = getActivity(); con = act.getApplicationContext(); //タップ管理クラスを生成する tapMng = new TapMng(); } public void draw() { background( 200 ); //タップ位置を表示する tapMng.dispTap(); //無効なタップ情報を削除する tapMng.removeTap(); } @Override public boolean surfaceTouchEvent(MotionEvent motionEvent) { int action = motionEvent.getActionMasked(); if( action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_POINTER_UP ){ //指が離れた際のイベント情報を元に、ダブルタップを判定する tapMng.addTap(motionEvent); } return super.surfaceTouchEvent(motionEvent); } |
少し長いですが、自力でマルチタップ時にダブルタップを検出するサンプルを掲載します。
tapPointはタップ座標、startMillsは初回タップ時刻、tapCountはタップ回数の記録用リストです。3つ一組で1つのタップに関する情報を管理しています。
指が離れた際、前回指を離した座標と今回指を離した座標を比較し、近い位置で操作が行われており、かつ反応速度が一定以下の場合、ダブルタップと判定しています。
FIRE_DISTANCE 、FIRE_SPEED 定数を変更する事で、ダブルタップの判定精度を調整してください。
synchronized( lock ) で排他をかけているのは、surfaceTouchEvent と draw が別スレッドで動作している為です。
本ページで利用しているアイコン画像は、下記サイト様より拝借しております。各画像の著作権は、それぞれのサイト様および作者にあります。