2010/11/28

OnGestureListener の処理順

Android の Activity には onTouchEvent というのがあって、シングルタップを捕まえることができます。しかし、ダブルタップのような複雑なイベントを捕まえる機能はデフォルトでは備わっていません。

そういった複雑な動作を捕まえるためには、Activity に OnGestureListener, OnDoubleTapListener というインターフェースを実装してやります。
このとき、どのイベントがどのタイミングで呼ばれるのか調べるために、以下のようなコードを書いてみました。

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.GestureDetector.OnDoubleTapListener;
import android.view.GestureDetector.OnGestureListener;
import android.view.GestureDetector;
import android.view.MotionEvent;

public class Main extends Activity implements OnGestureListener, OnDoubleTapListener {

    private GestureDetector gestureDetector;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        gestureDetector = new GestureDetector(this, this);
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent e) {
        super.dispatchTouchEvent(e);
        gestureDetector.onTouchEvent(e);
        return onTouchEvent(e);
    }

    public boolean onSingleTapConfirmed(MotionEvent e) {
        Log.i("test", "onSingleTapConfirmed");
        return false;
    }

    public boolean onDoubleTap(MotionEvent e) {
        Log.i("test", "onDoubleTap");
        return false;
    }

    public boolean onDoubleTapEvent(MotionEvent e) {
        switch (e.getAction()) {
        case MotionEvent.ACTION_DOWN:
            Log.i("test", "onDoubleTapEvent DOWN");
            break;
        case MotionEvent.ACTION_MOVE:
            Log.i("test", "onDoubleTapEvent MOVE");
            break;
        case MotionEvent.ACTION_UP:
            Log.i("test", "onDoubleTapEvent UP");
            break;
        default:
            Log.i("test", "onDoubleTapEvent OTHER");
            break;
        }
        return false;
    }

    public boolean onDown(MotionEvent e) {
        Log.i("test", "onDown");
        return false;
    }

    public void onShowPress(MotionEvent e) {
        Log.i("test", "onShowPress");
    }

    public boolean onSingleTapUp(MotionEvent e) {
        Log.i("test", "onSingleTapUp");
        return false;
    }

    public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,
            float distanceY) {
        Log.i("test", "onScroll");
        return false;
    }

    public void onLongPress(MotionEvent e) {
        Log.i("test", "onLongPress");

    }

    public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
            float velocityY) {
        Log.i("test", "onFling");
        return false;
    }
}

Xperia (Android 2.1) で試した結果、以下のようになりました。



短いシングルタップ
11-28 13:51:12.715: INFO/test(330): onDown
11-28 13:51:12.832: INFO/test(330): onSingleTapUp
11-28 13:51:13.022: INFO/test(330): onSingleTapConfirmed

ちょっとだけ長いシングルタップ
11-28 13:53:50.372: INFO/test(330): onDown
11-28 13:53:50.474: INFO/test(330): onShowPress
11-28 13:53:50.572: INFO/test(330): onSingleTapUp
11-28 13:53:50.677: INFO/test(330): onSingleTapConfirmed

ロングタップ
11-28 13:54:36.522: INFO/test(330): onDown
11-28 13:54:36.622: INFO/test(330): onShowPress
11-28 13:54:37.122: INFO/test(330): onLongPress

スクロール
11-28 13:59:04.617: INFO/test(330): onDown
11-28 13:59:04.715: INFO/test(330): onShowPress
11-28 13:59:04.795: INFO/test(330): onScroll
11-28 13:59:04.824: INFO/test(330): onScroll
11-28 13:59:04.853: INFO/test(330): onScroll
11-28 13:59:04.878: INFO/test(330): onScroll

弾いたとき(フリック入力のような感じ)
11-28 13:59:29.652: INFO/test(330): onDown
11-28 13:59:29.712: INFO/test(330): onScroll
11-28 13:59:29.743: INFO/test(330): onScroll
11-28 13:59:29.773: INFO/test(330): onScroll
11-28 13:59:29.802: INFO/test(330): onScroll
11-28 13:59:29.802: INFO/test(330): onFling

ダブルタップ
11-28 13:58:16.013: INFO/test(330): onDown
11-28 13:58:16.093: INFO/test(330): onSingleTapUp
11-28 13:58:16.177: INFO/test(330): onDoubleTap
11-28 13:58:16.183: INFO/test(330): onDoubleTapEvent DOWN
11-28 13:58:16.183: INFO/test(330): onDown
11-28 13:58:16.278: INFO/test(330): onShowPress
11-28 13:58:16.292: INFO/test(330): onDoubleTapEvent UP

ダブルタップムーブ
11-28 14:01:33.363: INFO/test(330): onDown
11-28 14:01:33.438: INFO/test(330): onSingleTapUp
11-28 14:01:33.533: INFO/test(330): onDoubleTap
11-28 14:01:33.533: INFO/test(330): onDoubleTapEvent DOWN
11-28 14:01:33.533: INFO/test(330): onDown
11-28 14:01:33.637: INFO/test(330): onShowPress
11-28 14:01:33.732: INFO/test(330): onDoubleTapEvent MOVE
11-28 14:01:33.766: INFO/test(330): onDoubleTapEvent MOVE
11-28 14:01:33.793: INFO/test(330): onDoubleTapEvent MOVE
11-28 14:01:33.822: INFO/test(330): onDoubleTapEvent MOVE
11-28 14:01:34.133: INFO/test(330): onLongPress
11-28 14:01:34.182: INFO/test(330): onDoubleTapEvent UP

onShowPress が出る場合と出ない場合があるので、注意してください。
onShowPress ですが、
The user has performed a down MotionEvent and not performed a move or up yet. This event is commonly used to provide visual feedback to the user to let them know that their action has been recognized i.e. highlight an element.
と書いてあります。訳すと
ダウンイベントが発生して、MOVE や UP などがすぐに発生しなかった場合に起こります。このイベントは、一般的に、ユーザにタッチが認識されていることを通知するために使います。たとえば、ボタンをハイライトさせる等です。
といった感じでしょうか。ちょっと意訳ですが。。。

3 件のコメント:

イノエスタ さんのコメント...

ViewクラスにOnGestureListenerを実装して
フリックをするとなぜか2回目から
onFling→onLongPress
となってしまって困ってます。
こちらの検証ではなってないですねぇ。
むー

イノエスタ さんのコメント...

すみません。設計の問題でした。
お騒がせしました><;

Yusuke Miura さんのコメント...

おー、解決したようで良かったです。
よかったら、どんな設計の問題だったのか教えてもらえたら嬉しいです。