LoginSignup
16
20

More than 5 years have passed since last update.

Androidでマルチタッチを使う

Last updated at Posted at 2015-12-06

はじめに

Androidでマルチタッチを使う方法を記載します。

タッチイベントの取得

タッチパネルを触るとonTouchEvent()がコールされます。
Activity or Viewでこの関数をoverrideすることでイベントを取得できます。
イベントはonTouchEvent()の引数MotionEventに格納されます。

イベントには種別があり、これをアクションといいます。
アクションはgetActionMasked()で取得します。

アクションの種類

アクションには次のものがあります。

タッチしていない状態でタッチしたとき

  • ACTION_DOWN
  • 0x00000000

タッチしている状態でタッチしなくなったとき

  • ACTION_UP
  • 0x00000001

すでにタッチしている状態で追加タッチしたとき
例えばある指でタッチしている状態で、別の指でタッチしたとき

  • ACTION_POINTER_DOWN
  • 0x00000005

複数の指でタッチしている状態で、そのうち1つの指を離したときのイベント

  • ACTION_POINTER_UP
  • 0x00000006

タッチ中に指を動かした場合

  • ACTION_MOVE
  • 0x00000002

何らかの要因でキャンセルされた場合、ACTION_UPと同じ扱いにした方がいい。

  • ACTION_CANCEL
  • 0x00000003

複数タッチの識別方法

Pointer indexとData index

複数のタッチがあるときに、それぞれのタッチを識別するために、
タッチには識別子Pointer indexがついています。
タッチ開始時(触った時)に付与されて、タッチ終了時(離したとき)になくなります。

一方でMotionEvent内に格納されたデータには配列のindexがあります。
これをData indexということにします。

少しわかりにくいので例で説明します。

例:
image

Data indexは配列のindexなので、0から始まります、1つずつ増加して、(Data配列サイズ-1)で終わります。
Pointer indexはタッチの識別子です。複数の指で触ったり、離したりすることを考えると、順番はバラバラになることがあります。
また、値が(Data配列サイズ-1)を超える場合もあります。x,yは各タッチの座標値です。

Pointer index, 座標値の取得

Pointer index,座標値の取得方法は次の通りです。

// 注意 : 説明のための必要最小限のソースコードに切り出しているため、実行はできません。
MotionEvent event;

int count = event.getPointerCount();

for (int i=0; i<count; i++) {
    int pid = event.getPointerId(i);
    float x = event.getX(i);
    float y = event.getY(i);
}

タッチしている数(Data配列のサイズ)をgetPointerCount()で取得します。Data indexでループします。
Pointer indexは引数をData indexとしてgetPointerId()で取得します。
座標値はgetX(),getY()で取得します。

Pointer indexを指定してData indexを取得する

あるPointer indexが、どのData indexに対応しているかを知るにはfindPointerIndex(Pointer index)を使います。
引数にPointer indexを指定すると戻り値にData indexが返ってきます。

蓄積されたイベント

処理負荷が増加すると、あるonTouchEvent()と次のonTouchEvent()の間に、
複数回タッチイベントが発生することがあります。下図参照。

例:

image

この時、複数のタッチイベントはMotionEvent内に蓄積されます。
後ろ側のonTouchEventで蓄積された(例ではA)タッチイベントと現在のタッチイベント(例ではB)が取得できます。

蓄積されたタッチイベントの個数はgetHistorySize()で取得できます。
例ではタッチイベントAが1つ蓄積されるので値1が取得されます。

蓄積された座標データはgetHistoricalX(Data index, index), getHistoricalY(Data index, index)で取得できます。
第2引数は0~(getHistorySize()-1)の値です。index値が小さいほど古いデータになります。

@Override
public boolean onTouchEvent(MotionEvent event) {
    int count = event.getPointerCount();

    for (int i=0; i<count; i++) {
        float x = event.getX(i);
        float y = event.getY(i);
        int hsize = event.getHistorySize();

        for (int j=0; j<hsize; j++) {
            float hx = event.getHistoricalX(i, j);
            float hy = event.getHistoricalY(i, j);
        }
    }

    return true;
}

サンプルソースコード

@Override
public boolean onTouchEvent(MotionEvent event) {
    int action1 = event.getAction();
    int action2 = event.getActionMasked();
    int count = event.getPointerCount();

    Log.e("", "getAction=0x" + String.format("%04x", action1) + ",getActionMasked=0x" + String.format("%04x", action2)
        + ",getPointerCount=" + count);

    for (int i=0; i<count; i++) {
        int pid = event.getPointerId(i);
        int id = event.findPointerIndex(pid);
        if (id == -1)
            continue;
        float x = event.getX(id);
        float y = event.getY(id);
        float p = event.getPressure(id);
        float s = event.getSize(id);
        int hsize = event.getHistorySize();

        String line = "\t";
        line += "getPointerId=" + pid + ",";
        line += "getHistorySize=" + hsize + ",";

        for (int j=0; j<hsize; j++) {
            float hx = event.getHistoricalX(id, j);
            float hy = event.getHistoricalY(id, j);
            float hp = event.getHistoricalPressure(id, j);
            float hs = event.getHistoricalSize(id, j);
            line += "x=" + String.format("%.2f", hx) + ",";
            line += "y=" + String.format("%.2f", hy) + ",";
            line += "p=" + String.format("%.4f", hp) + ",";
            line += "s=" + String.format("%.4f", hs) + ",";
        }

        line += "x=" + String.format("%.2f", x) + ",";
        line += "y=" + String.format("%.2f", y) + ",";
        line += "p=" + String.format("%.4f", p) + ",";
        line += "s=" + String.format("%.4f", s) + ",";

        Log.e("", line);
    }

    return true;
    //return super.onTouchEvent(event);
}

Appendix

getAction()とgetActionMasked()の違い

getAction()とgetActionMasked()の違いを説明します。

シングルタッチの場合、両者で取得されるイベントは同じになります。
マルチタッチの場合、違いがあります。
getAction()で取得されるイベントにはPointer indexが付加されます。
getActionMasked()で取得されるイベントはPointer indexがマスクされたものです。

具体例で説明します。

シングルタッチでタップしたときのアクションです。
双方同じ値が取得されます。

D/: getAction=0x0000,getActionMasked=0x0000
D/: getAction=0x0002,getActionMasked=0x0002
D/: getAction=0x0002,getActionMasked=0x0002
D/: getAction=0x0002,getActionMasked=0x0002
D/: getAction=0x0001,getActionMasked=0x0001

マルチタッチの例として、
1本目の指でタッチ後、2本目の指でタッチし、指2→指1の順番で指を離したときのアクションで説明します。

2本目をタッチしたときと、2本目を離したとき(*箇所)、
getAction()とgetActionMasked()で取得されるアクションに違いがあることが分かります。
getAction()で取得されるアクションにはPointer indexも一緒に渡されます。
具体的には2本目の指をタッチしたとき、アクションは0x0105となり0x01の部分は2本目を表します。
getActionMasked()が返すアクションはPointer indexがマスクされたものになります。

D/: getAction=0x0000,getActionMasked=0x0000
D/: getAction=0x0002,getActionMasked=0x0002
D/: getAction=0x0002,getActionMasked=0x0002
D/: getAction=0x0105,getActionMasked=0x0005(*)
D/: getAction=0x0002,getActionMasked=0x0002
D/: getAction=0x0002,getActionMasked=0x0002
D/: getAction=0x0106,getActionMasked=0x0006(*)
D/: getAction=0x0001,getActionMasked=0x0001
16
20
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
16
20