AndroidのonInterceptTouchEvent
とdispatchTouchEvent
の違いは何ですか?
Android開発者ガイドによると、両方の方法を使用してタッチイベント(MotionEvent
)をインターセプトできますが、違いは何ですか?
onInterceptTouchEvent
、dispatchTouchEvent
、およびonTouchEvent
は、ビューの階層(ViewGroup
)内でどのように相互作用しますか?
これを分かりやすく説明するのに最適な場所は、ソースコードです。ドキュメントはこれを説明するのはひどく不十分です。
dispatchTouchEventは、実際にはActivity、View、ViewGroupで定義されています。 タッチイベントのルーティング方法を決定するコントローラーと考えてください。
たとえば、最も単純なケースはView.dispatchTouchEventの場合です。これは、タッチイベントを定義している場合はOnTouchListener.onTouchに、または拡張メソッドonTouchEvent。
ViewGroup.dispatchTouchEventの場合、物事はずっと複雑です。 (child.dispatchTouchEventを呼び出すことにより)イベントを取得する子ビューの1つを把握する必要があります。これは基本的に、どの子ビューの境界矩形にタッチポイント座標が含まれているかを調べるヒットテストアルゴリズムです。
ただし、適切な子ビューにイベントをディスパッチする前に、親はイベントをまとめてスパイおよび/またはインターセプトできます。これがonInterceptTouchEventの目的です。したがって、ヒットテストを行う前にこのメソッドを最初に呼び出し、イベントがハイジャックされた場合(onInterceptTouchEventからtrueを返すことにより)、ACTION_CANCELを子ビューに送信して、タッチイベント処理を破棄できるようにします(前から)タッチイベント)そしてそれ以降、親レベルのすべてのタッチイベントはonTouchListener.onTouch(定義されている場合)またはonTouchEvent()にディスパッチされます。その場合も、onInterceptTouchEventが再び呼び出されることはありません。
[Activity | ViewGroup | View] .dispatchTouchEventをオーバーライドしますか?カスタムルーティングを行っているのでなければ、おそらくすべきではありません。
主な拡張メソッドは、親レベルでタッチイベントをスパイおよび/またはインターセプトする場合はViewGroup.onInterceptTouchEvent、メインイベント処理ではView.onTouchListener/View.onTouchEventです。
全体的に非常に複雑なデザインですが、Android APIは単純さよりも柔軟性に傾いています。
これはGoogleでの最初の結果だからです。 Youtube:マスタリングAndroidタッチシステム とスライドが利用可能です こちら で、Dave Smithによる素晴らしいトークを共有したいと思います。 Android Touch Systemについて深く理解できました。
Activityがタッチを処理する方法:
Activity.dispatchTouchEvent()
- 常に最初に呼び出される
- Windowにアタッチされたルートビューにイベントを送信します
onTouchEvent()
- ビューがイベントを消費しない場合に呼び出されます
- 常に最後に呼び出される
Viewがタッチを処理する方法:
View.dispatchTouchEvent()
- 存在する場合は、最初にイベントをリスナーに送信します
View.OnTouchListener.onTouch()
- 消費されない場合、タッチ自体を処理します
View.onTouchEvent()
ViewGroupがタッチを処理する方法:
ViewGroup.dispatchTouchEvent()
onInterceptTouchEvent()
- 子供に取って代わるかどうかを確認する
ACTION_CANCEL
をアクティブな子に渡します- 一度trueを返し、後続のすべてのイベントを消費します
- 子ビューごとに、逆の順序で追加されました
- タッチが関連する場合(ビュー内)、
child.dispatchTouchEvent()
- 前で処理されなかった場合、次のビューにディスパッチします
- 子がイベントを処理しない場合、リスナーはチャンスを取得します
OnTouchListener.onTouch()
- リスナーがない場合、または処理されていない場合
onTouchEvent()
- 傍受されたイベントは子ステップを飛び越える
また、カスタムタッチのサンプルコードを github.com/devunwired/ で提供しています。
Answer:基本的に、dispatchTouchEvent()
がすべてのView
レイヤーで呼び出され、View
が進行中のジェスチャーに興味があるかどうかを判断します。 ViewGroup
では、ViewGroup
は、dispatchTouchEvent()
- methodでタッチイベントをスチールする前に、子でdispatchTouchEvent()
を呼び出す前に、 ViewGroup
は、ViewGroup
onInterceptTouchEvent()
- methodがtrueを返す場合にのみディスパッチを停止します。 differenceは、dispatchTouchEvent()
がMotionEvents
をディスパッチしていることと、onInterceptTouchEvent
がインターセプトする必要があるかどうかを指示することです(子供にMotionEvent
をディスパッチしない)またはない(子供への発送)。
ViewGroupのコード これを多かれ少なかれ実行することを想像できます(非常に単純化されています):
public boolean dispatchTouchEvent(MotionEvent ev) {
if(!onInterceptTouchEvent()){
for(View child : children){
if(child.dispatchTouchEvent(ev))
return true;
}
}
return super.dispatchTouchEvent(ev);
}
他の回答の視覚的な補足を以下に示します。私の完全な答えは こちら です。
ViewGroup
のdispatchTouchEvent()
メソッドは、onInterceptTouchEvent()
を使用して、(onTouchEvent()
を使用して)タッチイベントを直ちに処理するか、その子のdispatchTouchEvent()
メソッドへの通知を継続するかを選択します。
これらのメソッドには多くの混乱がありますが、実際にはそれほど複雑ではありません。混乱のほとんどは、次の理由によるものです。
View/ViewGroup
またはその子のいずれかがonTouchEvent
でtrueを返さない場合、MotionEvent.ACTION_DOWN
に対してのみdispatchTouchEvent
およびonInterceptTouchEvent
が呼び出されます。 onTouchEvent
のtrueがない場合、親ビューは、ビューがMotionEventsを必要としないと想定します。onTouchEvent
でtrueを返しても、onInterceptTouchEventはMotionEvent.ACTION_DOWN
に対してのみ呼び出されます。処理順序は次のとおりです。
dispatchTouchEvent
が呼び出されます。onInterceptTouchEvent
は、MotionEvent.ACTION_DOWN
に対して、またはViewGroupのいずれかの子がonTouchEvent
でtrueを返したときに呼び出されます。onTouchEvent
は、ViewGroupの子で最初に呼び出され、どの子もtrueを返さない場合、View/ViewGroup
で呼び出されます。お子様のイベントを無効にせずにTouchEvents/MotionEvents
をプレビューするには、次の2つのことを行う必要があります。
dispatchTouchEvent
をオーバーライドしてイベントをプレビューし、super.dispatchTouchEvent(ev)
を返します。onTouchEvent
をオーバーライドしてtrueを返します。そうしないと、MotionEvent.ACTION_DOWN
以外のMotionEvent
を取得できません。スワイプイベントのようなジェスチャを検出したい場合、ジェスチャを検出しなかった限り、子供の他のイベントを無効にせずに、次のようにすることができます。
onInterceptTouchEvent
にtrueを返します。また、次のMotionEvent.ACTION_DOWN
までonInterceptTouchEventが再度呼び出されないため、フラグをリセットするのにも便利な場所です。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;
}
私はこのウェブページで非常に直感的な説明に出会いました http://doandroids.com/blogs/tag/codeexample/ 。そこから撮影:
- boolean onTouchEvent(MotionEvent ev)-このビューをターゲットとするタッチイベントが検出されるたびに呼び出されます
- boolean onInterceptTouchEvent(MotionEvent ev)-このViewGroupまたはその子がターゲットとしてタッチイベントが検出されるたびに呼び出されます。この関数がtrueを返す場合、MotionEventはインターセプトされます。つまり、子に渡されるのではなく、このビューのonTouchEventに渡されます。
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にディスパッチされることです。
このビデオで答えを見つけることができます https://www.youtube.com/watch?v=SYoN-OvdZ3M&list=PLonJJ3BVjZW6CtAMbJz1XD8ELUs1KXaTD&index=19 および次の3つのビデオ。すべてのタッチイベントは非常によく説明されており、非常に明確で例が豊富です。
短い答え:dispatchTouchEvent()
はfirstと呼ばれます。
短いアドバイス:は制御が難しいので、dispatchTouchEvent()
をオーバーライドしないでください。パフォーマンスが低下する場合があります。私見、onInterceptTouchEvent()
をオーバーライドすることをお勧めします。
ほとんどの回答では、アクティビティ/ビューグループ/ビューのフロータッチイベントについて明確に言及しているため、これらのメソッドのコードについては、ViewGroup
に追加するだけです(dispatchTouchEvent()
は無視します)。
onInterceptTouchEvent()
が最初に呼び出され、ACTIONイベントがそれぞれ下に->移動->上に呼び出されます。 2つのケースがあります。
return false 3つの場合(ACTION_DOWN、ACTION_MOVE、ACTION_UP)、親はこのタッチイベントを必要としないと見なすので、onTouch()
の親は決して呼び出しません、しかしonTouch()
は代わりに呼び出します;ただし、注意してください:
onInterceptTouchEvent()
は、子がrequestDisallowInterceptTouchEvent(true)
を呼び出さない限り、引き続きタッチイベントを受け取ります。onTouch()
に送り返します。逆にtrueを返すの場合、親はこのタッチイベントを盗みますすぐに、onInterceptTouchEvent()
はすぐに停止し、代わりにonTouch()
が呼び出されます同様に、すべてのonTouch()
が受け取る最後のアクションイベント-ACTION_CANCEL(したがって、親がタッチイベントを盗んだことを意味し、それ以降、子供はそれを処理できません)。 onInterceptTouchEvent()
がfalseを返すフローは正常ですが、trueを返す場合と少し混乱しているため、ここにリストします。
onTouch()
はACTION_DOWNを受け取ります再びおよび後続のアクション(ACTION_MOVE、ACTION_UP)。onTouch()
はnext ACTION_MOVE(onInterceptTouchEvent()
のACTION_MOVEとは異なります)および後続のアクション(ACTION_MOVE、ACTION_UP)を受け取ります。onTouch()
は、親がタッチイベントを盗むには遅すぎるため、まったく呼び出されませんではなくです。もう1つ重要は、onTouch()
のイベントのACTION_DOWNにより、ビューがそのイベントからさらにアクションを受け取りたいかどうかを決定します。ビューがonTouch()
のACTION_DOWNでtrueを返す場合、それはビューがそのイベントからより多くのアクションを受け取る意思があることを意味します。そうでない場合、onTouch()
のACTION_DOWNでfalseを返すと、ビューはそのイベントからそれ以上のアクションを受け取らないことを意味します。
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;
}
これをカスタムオーバーレイで使用して、バックグラウンドビューがタッチイベントに応答しないようにしました。
主な違い:
•Activity.dispatchTouchEvent(MotionEvent)-これにより、アクティビティはすべてのタッチイベントをウィンドウにディスパッチする前にインターセプトできます。
•ViewGroup.onInterceptTouchEvent(MotionEvent)-これにより、ViewGroupは、子ビューにディスパッチされるイベントを監視できます。
ViewGroupのonInterceptTouchEvent()
は、常に最初に発生するイベントであるACTION_DOWN
イベントのエントリポイントです。
ViewGroupでこのジェスチャを処理する場合は、onInterceptTouchEvent()
からtrueを返します。 trueを返すと、ViewGroupのonTouchEvent()
は、次のACTION_UP
またはACTION_CANCEL
までのすべての後続イベントを受け取ります。ほとんどの場合、ACTION_DOWN
とACTION_UP
またはACTION_CANCEL
の間のタッチイベントはACTION_MOVE
で、通常はスクロール/フリングジェスチャとして認識されます。
onInterceptTouchEvent()
からfalseを返すと、ターゲットビューのonTouchEvent()
が呼び出されます。 onInterceptTouchEvent()
からtrueを返すまで、後続のメッセージに対して繰り返されます。
public boolean dispatchTouchEvent(MotionEvent ev){
boolean consume =false;
if(onInterceptTouchEvent(ev){
consume = onTouchEvent(ev);
}else{
consume = child.dispatchTouchEvent(ev);
}
}
私の意見では、dispatchTouchEvent()
はデバイスのタッチを処理する上で非常に重要な部分です。 Activity
、ViewGroup
、View
には、このメソッドが実装されています。 ViewGroup
のメソッドを考慮すると、dispatchTouchEvent()
がonInterceptTouchEvent()
を呼び出すことがわかります
完全なSO答えは ここ です
ActivityとViewの両方にdispatchTouchEvent()メソッドとonTouchEventメソッドがあります。ViewGroupにもこのメソッドがありますが、onInterceptTouchEventという別のメソッドがあります。これらのメソッドの戻り値の型はブール値であり、戻り値を介してディスパッチルートを制御できます。
Androidのイベントディスパッチは、Activity-> ViewGroup-> Viewから始まります。