Android:onInterceptTouchEventとdispatchTouchEventの違いは?


249

Android onInterceptTouchEventとの違いは何dispatchTouchEventですか?

Android開発者ガイドによると、両方の方法を使用してタッチイベント(MotionEvent)をインターセプトできますが、違いは何ですか?

ビュー()の階層内でどのようonInterceptTouchEventdispatchTouchEventしてonTouchEvent相互作用しますViewGroupか?

回答:


272

これをわかりやすく説明するのに最適な場所はソースコードです。ドキュメントはこれを説明することについてひどく不十分です。

dispatchTouchEventは、実際にはActivity、View、およびViewGroupで定義されています。タッチイベントのルーティング方法を決定するコントローラーと考えてください。

例えば、最も簡単な場合は、そのView.dispatchTouchEventいずれかにタッチイベント意志経路OnTouchListener.onTouchは、それが定義または拡張メソッドにならonTouchEvent

ViewGroup.dispatchTouchEventものは仕方がより複雑です。(child.dispatchTouchEventを呼び出して)子ビューのどれがイベントを取得するかを判別する必要があります。これは基本的にヒットテストアルゴリズムであり、どの子ビューの境界の四角形にタッチポイント座標が含まれているかを調べます。

ただし、イベントを適切な子ビューにディスパッチする前に、親はすべてのイベントをスパイしたりインターセプトしたりできます。これがonInterceptTouchEventの目的です。したがって、ヒットテストを行う前に最初にこのメソッドを呼び出し、イベントがハイジャックされた場合(onInterceptTouchEventからtrueを返すことにより)、ACTION_CANCELを子ビューに送信して、(前のタッチイベントからの)タッチイベント処理を放棄できるようにします。親レベルのすべてのタッチイベントは、onTouchListener.onTouch(定義されている場合)またはonTouchEvent()にディスパッチされます。また、その場合、onInterceptTouchEventが再び呼び出されることはありません。

[Activity | ViewGroup | View] .dispatchTouchEventをオーバーライドしますか?カスタムルーティングを実行しているのでない限り、おそらくそうすべきではありません。

親レベルでタッチイベントをスパイまたはインターセプトする場合の主な拡張メソッドはViewGroup.onInterceptTouchEventで、メインイベント処理用のView.onTouchListener / View.onTouchEventです。

全体的に非常に複雑なデザインのimoですが、android APIはシンプルさよりも柔軟性に重点を置いています。


10
これはすばらしい簡潔な答えです。詳細な例については、トレーニング「ViewGroupでのタッチイベントの管理」
TalkLittleを

1
@numan salati:「それ以降、親レベルのすべてのタッチイベントがonTouchListener.onTouchにディスパッチされます」-ビューグループのディスパッチタッチメソッドをオーバーライドして、イベントをonTouchListenerにディスパッチさせます。しかし、それがどのように行われるのかわかりません。View.getOnTouchListener()。onTouch()のようなAPIはありません。setOnTouchListener()メソッドはありますが、getOnTouchListener()メソッドはありません。では、どうすればそれができるでしょうか?
アシュウィン2014

@Ashwin私の考えは正確に、setOnInterceptTouchEventもありません。サブクラスビューをオーバーライドして、レイアウトで使用したり、コードに追加したりできますが、フラグメント/アクティビティ自体をサブクラス化しないとこれらのビューをサブクラス化できないため(ほとんどの互換性と同様)、フラグメント/アクティビティレベルのrootViewをいじることはできません。 ProgressActivitiyの実装)。単純化するために、APIにはsetOnInterceptTouchEventが必要です。誰もが半複雑なアプリのある時点でrootViewsでinterceptTouchを使用
leRobot

244

これはGoogleでの最初の結果です。Youtubeでデイブ・スミスによる素晴らしいトークをお伝えしたいと思います。Androidタッチシステムの習得とスライドはこちらから入手できます。Androidタッチシステムについてよく理解できました。

アクティビティがタッチを処理する方法:

  • Activity.dispatchTouchEvent()
    • 常に最初に呼び出される
    • ウィンドウにアタッチされたルートビューにイベントを送信します
    • onTouchEvent()
      • ビューがイベントを消費しない場合に呼び出されます
      • 常に最後に呼び出される

ビューがタッチを処理する方法:

  • View.dispatchTouchEvent()
    • イベントが存在する場合は、最初にリスナーに送信します
      • View.OnTouchListener.onTouch()
    • 消費されない場合、タッチ自体を処理します
      • View.onTouchEvent()

ViewGroupがタッチを処理する方法:

  • ViewGroup.dispatchTouchEvent()
    • onInterceptTouchEvent()
      • 子供に取って代わるべきかどうかを確認する
      • ACTION_CANCEL アクティブな子供に渡す
      • trueが1回返されると、ViewGroupは後続のすべてのイベントを消費します
    • 子ビューごと(逆の順序で追加された)
      • タッチに関連する場合(ビュー内)、 child.dispatchTouchEvent()
      • 前で処理されていない場合は、次のビューにディスパッチします
    • 子供がイベントを処理しない場合、リスナーはチャンスを取得します
      • OnTouchListener.onTouch()
    • リスナーがいない、または処理されていない場合
      • onTouchEvent()
  • インターセプトされたイベントが子ステップを飛び越える

彼はgithub.com/devunwired/にカスタムタッチのサンプルコードも提供しています。

回答: 基本的にdispatchTouchEvent()は、が進行中のジェスチャーに関心があるViewかどうかを判断するために、すべてのレイヤーで呼び出されViewます。で、彼にタッチイベントを盗む能力があることが呼ぶだろう前に、-method 子供たちには。場合にのみディスパッチを停止する-method trueを返します。違いは、つまり派遣されかつそれが傍受するかどう伝えます(ディスパッチない子供たちに)かない(子供たちにディスパッチ)ViewGroupViewGroupdispatchTouchEvent()dispatchTouchEvent()ViewGroupViewGroup onInterceptTouchEvent()dispatchTouchEvent()MotionEventsonInterceptTouchEventMotionEvent

これを多かれ少なかれ行うViewGroupコードを想像できます(非常に単純化されています)。

public boolean dispatchTouchEvent(MotionEvent ev) {
    if(!onInterceptTouchEvent()){
        for(View child : children){
            if(child.dispatchTouchEvent(ev))
                return true;
        }
    }
    return super.dispatchTouchEvent(ev);
}

64

補足回答

ここに他の答えの視覚的な補足があります。私の完全な答えはここにあります

ここに画像の説明を入力してください

ここに画像の説明を入力してください

dispatchTouchEvent()メソッドは、タッチイベントを(で)すぐに処理するか、その子のメソッドに通知し続けるかを選択するためにViewGroup使用onInterceptTouchEvent()します。onTouchEvent()dispatchTouchEvent()


アクティビティでもonInterceptTouchEventを呼び出すことができますか?ViewGroupでのみ可能だと思いますか、それとも間違っていますか?@スラグ
フェデリコリッツォ

@FedericoRizzo、そうだね!どうもありがとうございました!図と私の答えを更新しました。
Suragch

20

これらの方法については多くの混乱がありますが、実際にはそれほど複雑ではありません。混乱のほとんどは次の理由によります:

  1. あなたの場合はView/ViewGroupその子のまたはいずれかが真では返さない onTouchEventdispatchTouchEventonInterceptTouchEventONLYのために呼び出されますMotionEvent.ACTION_DOWN。trueのfrom onTouchEventがない場合、親ビューはビューにMotionEventsが必要でないと想定します。
  2. onTouchEventでViewGroupの子がどれもtrueを返さMotionEvent.ACTION_DOWNない場合、たとえViewGroupがtrueを返す場合でも、onInterceptTouchEventはに対してのみ呼び出されますonTouchEvent

処理順序は次のとおりです。

  1. dispatchTouchEvent と呼ばれます。
  2. onInterceptTouchEventMotionEvent.ACTION_DOWNViewGroupの子のいずれかが呼び出されたとき、またはでtrueが返されたときに呼び出されonTouchEventます。
  3. onTouchEvent最初にViewGroupの子で呼び出され、子がどれもtrueを返さない場合、で呼び出されます View/ViewGroup

TouchEvents/MotionEventsお子様のイベントを無効にせずにプレビューする場合は、次の2つのことを行う必要があります。

  1. dispatchTouchEventイベントをプレビューして戻るためにオーバーライドし super.dispatchTouchEvent(ev)ます。
  2. オーバーライドonTouchEventしてtrueを返し、そうでない場合はあなたがいずれかを取得することはできません MotionEvent を除いてMotionEvent.ACTION_DOWN

スワイプイベントなどのジェスチャーを検出したい場合は、ジェスチャーを検出しなかった限り、子供の他のイベントを無効にすることなく、次のように実行できます。

  1. 上記のようにMotionEventをプレビューし、ジェスチャーを検出したときにフラグを設定します。
  2. onInterceptTouchEventフラグが子供によるMotionEvent処理をキャンセルするように設定されている場合は、trueを返します。onInterceptTouchEventは次のまで呼び出されないので、これはフラグをリセットするのにも便利な場所MotionEvent.ACTION_DOWNです。

のオーバーライドの例FrameLayout(私の例はXamarin AndroidでプログラミングしているC#ですが、ロジックはJavaでも同じです):

public override bool DispatchTouchEvent(MotionEvent e)
{
    // Preview the touch event to detect a swipe:
    switch (e.ActionMasked)
    {
        case MotionEventActions.Down:
            _processingSwipe = false;
            _touchStartPosition = e.RawX;
            break;
        case MotionEventActions.Move:
            if (!_processingSwipe)
            {
                float move = e.RawX - _touchStartPosition;
                if (move >= _swipeSize)
                {
                    _processingSwipe = true;
                    _cancelChildren = true;
                    ProcessSwipe();
                }
            }
            break;
    }
    return base.DispatchTouchEvent(e);
}

public override bool OnTouchEvent(MotionEvent e)
{
    // To make sure to receive touch events, tell parent we are handling them:
    return true;
}

public override bool OnInterceptTouchEvent(MotionEvent e)
{
    // Cancel all children when processing a swipe:
    if (_cancelChildren)
    {
        // Reset cancel flag here, as OnInterceptTouchEvent won't be called until the next MotionEventActions.Down:
        _cancelChildren = false;
        return true;
    }
    return false;
}

3
なぜこれ以上の賛成票がないのかわかりません。これは良い答え(IMO)であり、非常に役に立ちました。
Mark Ormesher、2014

8

私はこのウェブページhttp://doandroids.com/blogs/tag/codeexample/で非常に直感的な説明に出くわしました。そこから撮影:

  • boolean onTouchEvent(MotionEvent ev)-このビューをターゲットとするタッチイベントが検出されるたびに呼び出されます
  • boolean onInterceptTouchEvent(MotionEvent ev)-このViewGroupまたはその子をターゲットとしてタッチイベントが検出されるたびに呼び出されます。この関数がtrueを返す場合、MotionEventはインターセプトされます。つまり、子に渡されるのではなく、このビューのonTouchEventに渡されます。

2
質問は、onInterceptTouchEventとdispatchTouchEventについてです。どちらもonTouchEventの前に呼び出されます。しかし、その例では、dispatchTouchEventは表示されません。
Dayerman 2012

8

dispatchTouchEventは、onInterceptTouchEventの前に処理します。

この簡単な例を使用して:

   main = new LinearLayout(this){
        @Override
        public boolean onInterceptTouchEvent(MotionEvent ev) {
            System.out.println("Event - onInterceptTouchEvent");
            return super.onInterceptTouchEvent(ev);
            //return false; //event get propagated
        }
        @Override
        public boolean dispatchTouchEvent(MotionEvent ev) {
            System.out.println("Event - dispatchTouchEvent");
            return super.dispatchTouchEvent(ev);
            //return false; //event DONT get propagated
        }
    };

    main.setBackgroundColor(Color.GRAY);
    main.setLayoutParams(new LinearLayout.LayoutParams(320,480));    


    viewA = new EditText(this);
    viewA.setBackgroundColor(Color.YELLOW);
    viewA.setTextColor(Color.BLACK);
    viewA.setTextSize(16);
    viewA.setLayoutParams(new LinearLayout.LayoutParams(320,80));
    main.addView(viewA);

    setContentView(main);

ログは次のようになります。

I/System.out(25900): Event - dispatchTouchEvent
I/System.out(25900): Event - onInterceptTouchEvent

したがって、これら2つのハンドラーを使用している場合は、dispatchTouchEventを使用して、最初のインスタンスでイベントを処理します。イベントは、onInterceptTouchEventに送られます。

もう1つの違いは、dispatchTouchEventが「false」を返す場合、イベントは子(この場合はEditText)に伝播されませんが、onInterceptTouchEventでfalseを返す場合、イベントはEditTextにディスパッチされます。


5

短い答え: 最初dispatchTouchEvent()呼び出されます。

簡単なアドバイス:dispatchTouchEvent()制御が難しいため、オーバーライドしないでください。パフォーマンスが低下する場合があります。私見、私はオーバーライドすることをお勧めしますonInterceptTouchEvent()


ほとんどの回答は、アクティビティ/ビューグループ/ビューでのフロータッチイベントについてかなり明確に言及しているため、これらのメソッドのコードの詳細をViewGroup(を無視してdispatchTouchEvent())追加するだけです。

onInterceptTouchEvent()最初に呼び出され、ACTIONイベントはそれぞれ下へ->移動->上へ呼び出されます。2つのケースがあります。

  1. 3つのケース(ACTION_DOWN、ACTION_MOVE、ACTION_UP)でfalse返す場合、親はこのタッチイベントを必要としないためonTouch()、親の呼び出しは行われずonTouch()、子の呼び出しが代わりに呼び出されます。ただし注意してください:

    • onInterceptTouchEvent()まだ限り、その子が呼び出さないように、タッチイベントを受信し続けますrequestDisallowInterceptTouchEvent(true)
    • そのイベントを受け取る子がない場合(ユーザーがタッチした位置に子がない場合、または子はあるがACTION_DOWNでfalseを返す場合)、親はそのイベントをonTouch()親に送り返します。
  2. 逆にtrue返すと、親はこのタッチイベントをすぐに盗み、すぐにonInterceptTouchEvent()停止しonTouch()ます。親が呼び出されるのではなく、すべてonTouch()の子が最後のアクションイベントACTION_CANCEL(つまり、親を意味します)を受け取りますタッチイベントを盗み、子供たちはそれ以降それを処理できません)。onInterceptTouchEvent()false を返すフローは正常ですが、trueを返す場合と少し混乱するため、ここにリストします。

    • 、ACTION_DOWNにtrueを返しますonTouch()親はACTION_DOWN受け取ると、再び次のアクション(ACTION_MOVE、ACTION_UP)。
    • ACTION_MOVEでtrueを返すonTouch()と、次の ACTION_MOVE(と同じACTION_MOVEではありませんonInterceptTouchEvent())と次のアクション(ACTION_MOVE、ACTION_UP)を受け取ります。
    • ACTION_UPにおける真戻り、onTouch()両親のはなりませそれはタッチイベントを盗むために遅すぎる親のためだからまったく呼び出さ。

さらにもう1つ重要なことは、イベントのACTION_DOWNによって、onTouch()ビューがそのイベントからより多くのアクションを受け取りたいかどうかを決定することです。のACTION_DOWNでビューがtrueを返す場合、ビューはonTouch()そのイベントからさらにアクションを受け取る用意があることを意味します。それ以外の場合、ACTION_DOWNでfalseを返すonTouch()と、ビューがそのイベントからそれ以上のアクションを受け取らないことを意味します。



3

ViewGroupサブクラス内の次のコードは、その親コン​​テナーがタッチイベントを受信できないようにします。

  @Override
  public boolean dispatchTouchEvent(MotionEvent ev) {
    // Normal event dispatch to this container's children, ignore the return value
    super.dispatchTouchEvent(ev);

    // Always consume the event so it is not dispatched further up the chain
    return true;
  }

これをカスタムオーバーレイと一緒に使用して、バックグラウンドビューがタッチイベントに応答しないようにしました。


1

主な違い:

•Activity.dispatchTouchEvent(MotionEvent)-これにより、アクティビティがウィンドウにディスパッチされる前にすべてのタッチイベントをインターセプトできます。
•ViewGroup.onInterceptTouchEvent(MotionEvent)-これにより、ViewGroupが子ビューにディスパッチされるときにイベントを監視できます。


1
はい、私はアンドロイド開発者ガイドからのこの答えを知っています-それにもかかわらず不明です。メソッドdispatchTouchEventは、アクティビティだけでなくViewGroupにも存在します。私の質問は、3つのメソッドdispatchTouchEvent、onInterceptTouchEvent、およびonTouchEventが、どのようにViewGroupsの階層(RelativeLayoutsなど)内で相互作用するかでした。
アンドロイド

1

ViewGroup onInterceptTouchEvent()は常に、ACTION_DOWN最初に発生するイベントであるイベントのエントリポイントです。

ViewGroupでこのジェスチャを処理する場合は、trueを返しますonInterceptTouchEvent()。trueを返すonTouchEvent()と、ViewGroup は次のACTION_UPまたはまで後続のすべてのイベントを受け取ります。ACTION_CANCELほとんどの場合、ACTION_DOWNACTION_UPまたACTION_CANCELはの間のタッチイベントはACTION_MOVE、通常、スクロール/フリングジェスチャとして認識されます。

からfalseを返すonInterceptTouchEvent()と、ターゲットビューonTouchEvent()が呼び出されます。からtrueが返されるまで、後続のメッセージに対して繰り返されますonInterceptTouchEvent()

ソース:http : //neevek.net/posts/2013/10/13/implementing-onInterceptTouchEvent-and-onTouchEvent-for-ViewGroup.html


0

ActivityとViewの両方にメソッドdispatchTouchEvent()とonTouchEventがあります。ViewGroupにもこのメソッドがありますが、onInterceptTouchEventと呼ばれる別のメソッドがあります。これらのメソッドの戻り値の型はブール値であり、戻り値を通じてディスパッチルートを制御できます。

Androidのイベントディスパッチは、Activity-> ViewGroup-> Viewから始まります。


0
public boolean dispatchTouchEvent(MotionEvent ev){
    boolean consume =false;
    if(onInterceptTouchEvent(ev){
        consume = onTouchEvent(ev);
    }else{
        consume = child.dispatchTouchEvent(ev);
    }
}

1
説明を追加していただけますか?
ポールフロイド

3
このコードスニペットは問題を解決する可能性がありますが、説明を含めると、投稿の品質を向上させるのに役立ちます。あなたは将来の読者のための質問に答えていることを覚えておいてください、そしてそれらの人々はあなたのコード提案の理由を知らないかもしれません。
ロザリオペレイラ・フェルナンデス

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.