一般的な操作を検出する

Compose をお試しください
Jetpack Compose は、Android で推奨される UI ツールキットです。Compose でタッチと入力を使用する方法をご覧ください。

ユーザーがタッチ スクリーンに指を 1 本以上置くと「タップ操作」が発生し、アプリはそのタップのパターンを操作として解釈します。 操作の検出には 2 つのフェーズがあります。

  1. タップイベント データを収集する。
  2. データを解釈して、アプリがサポートする操作の基準を満たしているかどうかを判断する。

AndroidX のクラス

このドキュメントの例では、GestureDetectorCompat クラスと MotionEventCompat クラスを使用します。これらのクラスは AndroidX ライブラリ にあります。可能な限り AndroidX クラスを使用して、以前のデバイスとの互換性を確保してください。 MotionEventCompatnot a replacement for the MotionEvent class. むしろ、そのイベントに関連付けられたアクションを受け取るために、MotionEvent オブジェクトを渡す静的ユーティリティ メソッドを提供します。

データの収集

ユーザーが画面に指を 1 本以上置くと、タップイベントを受け取ったビューで コールバック onTouchEvent() がトリガーされます。最終的に操作として識別されるタップイベントの各シーケンス(位置、圧力、サイズ、別の指の追加など)に対して onTouchEvent() が数回発生します。

操作は、ユーザーが最初に画面に触れたときに始まり、システムがユーザーの指の位置を追跡している間継続し、最後のイベント(ユーザーの指が画面を離れる)をキャプチャしたときに終わります。 このインタラクション全体を通して、onTouchEvent() に配信される MotionEvent にすべてのインタラクションの詳細が示されます。アプリは、MotionEvent によって提供されるデータを使用して、処理すべき操作が発生したかどうかを判断できます。

Activity または View のタップイベントをキャプチャする

Activity または View のタップイベントをインターセプトするには、onTouchEvent() コールバックをオーバーライドします。

次のコード スニペットは、getAction() を使用して、ユーザーが実行したアクションを event パラメータから抽出します。 これにより、処理すべき操作が発生したかどうかを判断するために必要な RAW データが得られます。

Kotlin

class MainActivity : Activity() {
    ...
    // This example shows an Activity. You can use the same approach if you are 
    // subclassing a View.
    override fun onTouchEvent(event: MotionEvent): Boolean {
        return when (event.action) {
            MotionEvent.ACTION_DOWN -> {
                Log.d(DEBUG_TAG, "Action was DOWN")
                true
            }
            MotionEvent.ACTION_MOVE -> {
                Log.d(DEBUG_TAG, "Action was MOVE")
                true
            }
            MotionEvent.ACTION_UP -> {
                Log.d(DEBUG_TAG, "Action was UP")
                true
            }
            MotionEvent.ACTION_CANCEL -> {
                Log.d(DEBUG_TAG, "Action was CANCEL")
                true
            }
            MotionEvent.ACTION_OUTSIDE -> {
                Log.d(DEBUG_TAG, "Movement occurred outside bounds of current screen element")
                true
            }
            else -> super.onTouchEvent(event)
        }
    }
}

Java

public class MainActivity extends Activity {
...
// This example shows an Activity. You can use the same approach if you are
// subclassing a View.
@Override
public boolean onTouchEvent(MotionEvent event){
    switch(event.getAction()) {
        case (MotionEvent.ACTION_DOWN) :
            Log.d(DEBUG_TAG,"Action was DOWN");
            return true;
        case (MotionEvent.ACTION_MOVE) :
            Log.d(DEBUG_TAG,"Action was MOVE");
            return true;
        case (MotionEvent.ACTION_UP) :
            Log.d(DEBUG_TAG,"Action was UP");
            return true;
        case (MotionEvent.ACTION_CANCEL) :
            Log.d(DEBUG_TAG,"Action was CANCEL");
            return true;
        case (MotionEvent.ACTION_OUTSIDE) :
            Log.d(DEBUG_TAG,"Movement occurred outside bounds of current screen element");
            return true;
        default :
            return super.onTouchEvent(event);
    }
}

ユーザーがタップ、タッチ&ホールド、ドラッグを行うと、このコードにより Logcat に次のようなメッセージが生成されます。

GESTURES D   Action was DOWN
GESTURES D   Action was UP
GESTURES D   Action was MOVE

カスタム操作の場合は、このようなイベントを独自に処理して、処理する必要がある操作を表しているかどうかを判断できます。ただし、ダブルタップ、タッチ&ホールド、フリングなどの一般的な操作をアプリで使用する場合は、GestureDetector クラスを利用できます。GestureDetector を使用すると、個々のタップイベントを独自に処理しなくても、一般的な操作を簡単に検出できます。これについては、操作の検出で 詳しく説明します。

単一のビューのタップイベントをキャプチャする

onTouchEvent() の代わりに、 View.OnTouchListener オブジェクトを任意の View オブジェクトに setOnTouchListener() メソッドを使用してアタッチできます。これにより、次の例に示すように、既存の View をサブクラス化しなくても、タップイベントをリッスンできます。

Kotlin

findViewById<View>(R.id.my_view).setOnTouchListener { v, event ->
    // Respond to touch events.
    true
}

Java

View myView = findViewById(R.id.my_view);
myView.setOnTouchListener(new OnTouchListener() {
    public boolean onTouch(View v, MotionEvent event) {
        // Respond to touch events.
        return true;
    }
});

ACTION_DOWN イベントに対して false を返すリスナーの作成には注意が必要です。これを行うと、後続の ACTION_MOVE イベントと ACTION_UP イベントのシーケンスでリスナーが呼び出されません。これは、ACTION_DOWN がすべてのタップイベントの開始点であるためです。

カスタムビューを作成する場合は、前述のように onTouchEvent() をオーバーライドできます。

操作の検出

Android には、一般的な操作を検出するための GestureDetector クラスが用意されています。サポートされている操作には、onDown()onLongPress()onFling() などがあります。 GestureDetector は、前述の onTouchEvent() メソッドと組み合わせて使用できます。

サポートされている操作をすべて検出する

GestureDetectorCompat オブジェクトをインスタンス化する際は、GestureDetector.OnGestureListener インターフェースを実装するクラスをパラメータの 1 つとして取ります。GestureDetector.OnGestureListener は、特定のタップイベントが発生したときにユーザーに通知します。GestureDetector オブジェクトがイベントを受け取れるようにするには、ビューまたはアクティビティの onTouchEvent() メソッドをオーバーライドし、観測されたすべてのイベントを Detector インスタンスに渡します。

次のスニペットで、個々の on<TouchEvent> メソッドの戻り値 true は、 タップイベントが処理されたことを示します。戻り値 false は、タップが正常に処理されるまでイベントがビュースタックに渡されることを示します。

テストアプリで次のスニペットを実行すると、タッチ スクリーンを操作したときにアクションがどのようにトリガーされるか、各タップイベントに対する MotionEvent の内容は何かを把握できます。単純な操作であっても、いかに多くのデータが生成されるかを理解できます。

Kotlin

private const val DEBUG_TAG = "Gestures"

class MainActivity :
        Activity(),
        GestureDetector.OnGestureListener,
        GestureDetector.OnDoubleTapListener {

    private lateinit var mDetector: GestureDetectorCompat

    // Called when the activity is first created.
    public override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        // Instantiate the gesture detector with the
        // application context and an implementation of
        // GestureDetector.OnGestureListener.
        mDetector = GestureDetectorCompat(this, this)
        // Set the gesture detector as the double-tap
        // listener.
        mDetector.setOnDoubleTapListener(this)
    }

    override fun onTouchEvent(event: MotionEvent): Boolean {
        return if (mDetector.onTouchEvent(event)) {
            true
        } else {
            super.onTouchEvent(event)
        }
    }

    override fun onDown(event: MotionEvent): Boolean {
        Log.d(DEBUG_TAG, "onDown: $event")
        return true
    }

    override fun onFling(
            event1: MotionEvent,
            event2: MotionEvent,
            velocityX: Float,
            velocityY: Float
    ): Boolean {
        Log.d(DEBUG_TAG, "onFling: $event1 $event2")
        return true
    }

    override fun onLongPress(event: MotionEvent) {
        Log.d(DEBUG_TAG, "onLongPress: $event")
    }

    override fun onScroll(
            event1: MotionEvent,
            event2: MotionEvent,
            distanceX: Float,
            distanceY: Float
    ): Boolean {
        Log.d(DEBUG_TAG, "onScroll: $event1 $event2")
        return true
    }

    override fun onShowPress(event: MotionEvent) {
        Log.d(DEBUG_TAG, "onShowPress: $event")
    }

    override fun onSingleTapUp(event: MotionEvent): Boolean {
        Log.d(DEBUG_TAG, "onSingleTapUp: $event")
        return true
    }

    override fun onDoubleTap(event: MotionEvent): Boolean {
        Log.d(DEBUG_TAG, "onDoubleTap: $event")
        return true
    }

    override fun onDoubleTapEvent(event: MotionEvent): Boolean {
        Log.d(DEBUG_TAG, "onDoubleTapEvent: $event")
        return true
    }

    override fun onSingleTapConfirmed(event: MotionEvent): Boolean {
        Log.d(DEBUG_TAG, "onSingleTapConfirmed: $event")
        return true
    }

}

Java

public class MainActivity extends Activity implements
        GestureDetector.OnGestureListener,
        GestureDetector.OnDoubleTapListener{

    private static final String DEBUG_TAG = "Gestures";
    private GestureDetectorCompat mDetector;

    // Called when the activity is first created.
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        // Instantiate the gesture detector with the
        // application context and an implementation of
        // GestureDetector.OnGestureListener.
        mDetector = new GestureDetectorCompat(this,this);
        // Set the gesture detector as the double-tap
        // listener.
        mDetector.setOnDoubleTapListener(this);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event){
        if (this.mDetector.onTouchEvent(event)) {
            return true;
        }
        return super.onTouchEvent(event);
    }

    @Override
    public boolean onDown(MotionEvent event) {
        Log.d(DEBUG_TAG,"onDown: " + event.toString());
        return true;
    }

    @Override
    public boolean onFling(MotionEvent event1, MotionEvent event2,
            float velocityX, float velocityY) {
        Log.d(DEBUG_TAG, "onFling: " + event1.toString() + event2.toString());
        return true;
    }

    @Override
    public void onLongPress(MotionEvent event) {
        Log.d(DEBUG_TAG, "onLongPress: " + event.toString());
    }

    @Override
    public boolean onScroll(MotionEvent event1, MotionEvent event2, float distanceX,
            float distanceY) {
        Log.d(DEBUG_TAG, "onScroll: " + event1.toString() + event2.toString());
        return true;
    }

    @Override
    public void onShowPress(MotionEvent event) {
        Log.d(DEBUG_TAG, "onShowPress: " + event.toString());
    }

    @Override
    public boolean onSingleTapUp(MotionEvent event) {
        Log.d(DEBUG_TAG, "onSingleTapUp: " + event.toString());
        return true;
    }

    @Override
    public boolean onDoubleTap(MotionEvent event) {
        Log.d(DEBUG_TAG, "onDoubleTap: " + event.toString());
        return true;
    }

    @Override
    public boolean onDoubleTapEvent(MotionEvent event) {
        Log.d(DEBUG_TAG, "onDoubleTapEvent: " + event.toString());
        return true;
    }

    @Override
    public boolean onSingleTapConfirmed(MotionEvent event) {
        Log.d(DEBUG_TAG, "onSingleTapConfirmed: " + event.toString());
        return true;
    }
}

サポートされている操作のサブセットを検出する

ごく少数のジェスチャーだけを処理する場合は、GestureDetector.OnGestureListener インターフェースを実装するのではなく、GestureDetector.SimpleOnGestureListener を拡張します。

GestureDetector.SimpleOnGestureListener は、すべての on<TouchEvent> メソッドに対して false を返すことにより、それらのメソッドすべての実装を提供します。これにより、必要なメソッドのみをオーバーライドできます。たとえば、次のコード スニペットは、GestureDetector.SimpleOnGestureListener を拡張して onFling()onDown() をオーバーライドするクラスを作成します。

GestureDetector.OnGestureListener を使用するかどうかにかかわらず、 GestureDetector.SimpleOnGestureListener を返す onDown() メソッドを実装することをおすすめします。trueこれは、すべての操作が onDown() メッセージで始まるためです。GestureDetector.SimpleOnGestureListener のデフォルトの動作と同様に、onDown() から false を返す場合、システムは残りの操作が無視されると想定し、GestureDetector.OnGestureListener の他のメソッドは呼び出されなくなります。これにより、アプリで予期しない問題が発生する可能性があります。onDown() から false を返すのは、操作全体を実際に無視する場合のみにしてください。

Kotlin

private const val DEBUG_TAG = "Gestures"

class MainActivity : Activity() {

    private lateinit var mDetector: GestureDetectorCompat

    public override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        mDetector = GestureDetectorCompat(this, MyGestureListener())
    }

    override fun onTouchEvent(event: MotionEvent): Boolean {
        mDetector.onTouchEvent(event)
        return super.onTouchEvent(event)
    }

    private class MyGestureListener : GestureDetector.SimpleOnGestureListener() {

        override fun onDown(event: MotionEvent): Boolean {
            Log.d(DEBUG_TAG, "onDown: $event")
            return true
        }

        override fun onFling(
                event1: MotionEvent,
                event2: MotionEvent,
                velocityX: Float,
                velocityY: Float
        ): Boolean {
            Log.d(DEBUG_TAG, "onFling: $event1 $event2")
            return true
        }
    }
}

Java

public class MainActivity extends Activity {

    private GestureDetectorCompat mDetector;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mDetector = new GestureDetectorCompat(this, new MyGestureListener());
    }

    @Override
    public boolean onTouchEvent(MotionEvent event){
        if (this.mDetector.onTouchEvent(event)) {
              return true;
        }
        return super.onTouchEvent(event);
    }

    class MyGestureListener extends GestureDetector.SimpleOnGestureListener {
        private static final String DEBUG_TAG = "Gestures";

        @Override
        public boolean onDown(MotionEvent event) {
            Log.d(DEBUG_TAG,"onDown: " + event.toString());
            return true;
        }

        @Override
        public boolean onFling(MotionEvent event1, MotionEvent event2,
                float velocityX, float velocityY) {
            Log.d(DEBUG_TAG, "onFling: " + event1.toString() + event2.toString());
            return true;
        }
    }
}

参考情報