2017年5月6日土曜日

View.OnTouchListenerでのマルチタッチ処理

やりたいことはシンプルなのに実装が色々と面倒臭かったので、備忘録を兼ねてメモ。

(やりたいこと)
・1つのViewでマルチタッチを処理したい
・タッチされている座標だけ把握できればおk

(やりかた)
final SparseArray<Point> touching = new SparseArray<>();
view.setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View view, MotionEvent motionEvent) {
final int actionIndex = motionEvent.getActionIndex();
final int pointerId = motionEvent.getPointerId(actionIndex);
final int pointerIndex = motionEvent.findPointerIndex(pointerId);
switch (motionEvent.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_POINTER_DOWN:
touching.append(pointerId, new Point((int) motionEvent.getX(pointerIndex), (int) motionEvent.getY(pointerIndex)));
break;
case MotionEvent.ACTION_MOVE:
for (int i = 0; i < motionEvent.getPointerCount(); i++) {
touching.get(motionEvent.getPointerId(i)).set((int) motionEvent.getX(i), (int) motionEvent.getY(i));
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_POINTER_UP:
touching.delete(pointerId);
break;
case MotionEvent.ACTION_CANCEL:
touching.clear();
break;
}
for (int i = 0; i < touching.size(); i++) {
final Point point = touching.get(touching.keyAt(i));
// point.x, point.y がviewの現在のタッチされている座標
}
}
});

(解説)
・Android SDK単品では無理そうなので、SparseArrayのテーブル(touching)を準備
・touchingテーブルは、タッチIDを主キーにタッチされている座標を記憶する
(主キーがint型なのでHashMapではなくSparseArrayを使った方が処理効率が良い)
・タッチ開始(ACTION_DOWNなど)を検出した時にテーブルにPointをadd
・移動(ACTION_MOVE)を検出した時にPointを更新
・タッチ終了(ACTION_UPなど)を検出した時にテーブルからPointをdelete

(若干疑問点)
タッチ開始(DOWN)とタッチ終了(UP)は1タッチID毎にイベント(※ここで言うイベントとはonTouchのコールバックのこと)が発生するが移動(MOVE)は2本指同時に動かした時、両方同時にイベントが発生した。
これが恒常的なもの(保証されているもの)なのかが若干疑問。
一瞬、「DOWNとUPの場合、非MASKのactionIdの9〜16bitにタッチIDが付加され、1回のイベントで取得できるactionIdは1つだから保証されているのでは?」と思ったけど、それならMOVEが1回のイベントで複数処理されるのがおかしい。
MotionEventのactionIdなんですが、明らかに設計ミスっぽい気がする(ただし、当然ですがここはプリミティブ過ぎて今更直せないという感じだろうか)。

(追記)
CANCELの場合もMOVEと同様、複数指分が纏めて走るらしいので、CANCELの時はtouchingをclearするように修正。

0 件のコメント:

コメントを投稿

注: コメントを投稿できるのは、このブログのメンバーだけです。

合理的ではないものを作りたい

ここ最近、実機版の東方VGSの開発が忙しくて、東方VGSの曲追加が滞っています。 東方VGS(実機版)のデザインを作りながら検討中。基本レトロUIベースですがシークバーはモダンに倣おうかな…とか pic.twitter.com/YOYprlDsYD — SUZUKI PLAN (...