回答:
これをわかりやすく説明するのに最適な場所はソースコードです。ドキュメントはこれを説明することについてひどく不十分です。
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はシンプルさよりも柔軟性に重点を置いています。
これは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を返します。違いは、つまり派遣されかつそれが傍受するかどう伝えます(ディスパッチない子供たちに)かない(子供たちにディスパッチ)。ViewGroup
ViewGroup
dispatchTouchEvent()
dispatchTouchEvent()
ViewGroup
ViewGroup
onInterceptTouchEvent()
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);
}
ここに他の答えの視覚的な補足があります。私の完全な答えはここにあります。
のdispatchTouchEvent()
メソッドは、タッチイベントを(で)すぐに処理するか、その子のメソッドに通知し続けるかを選択するためにViewGroup
使用onInterceptTouchEvent()
します。onTouchEvent()
dispatchTouchEvent()
これらの方法については多くの混乱がありますが、実際にはそれほど複雑ではありません。混乱のほとんどは次の理由によります:
View/ViewGroup
その子のまたはいずれかが真では返さない
onTouchEvent
、dispatchTouchEvent
とonInterceptTouchEvent
ONLYのために呼び出されますMotionEvent.ACTION_DOWN
。trueのfrom
onTouchEvent
がない場合、親ビューはビューにMotionEventsが必要でないと想定します。MotionEvent.ACTION_DOWN
ない場合、たとえViewGroupがtrueを返す場合でも、onInterceptTouchEventはに対してのみ呼び出されますonTouchEvent
。処理順序は次のとおりです。
dispatchTouchEvent
と呼ばれます。onInterceptTouchEvent
MotionEvent.ACTION_DOWN
ViewGroupの子のいずれかが呼び出されたとき、またはでtrueが返されたときに呼び出されonTouchEvent
ます。onTouchEvent
最初にViewGroupの子で呼び出され、子がどれもtrueを返さない場合、で呼び出されます
View/ViewGroup
。TouchEvents/MotionEvents
お子様のイベントを無効にせずにプレビューする場合は、次の2つのことを行う必要があります。
dispatchTouchEvent
イベントをプレビューして戻るためにオーバーライドし
super.dispatchTouchEvent(ev)
ます。onTouchEvent
してtrueを返し、そうでない場合はあなたがいずれかを取得することはできません
MotionEvent
を除いてMotionEvent.ACTION_DOWN
。スワイプイベントなどのジェスチャーを検出したい場合は、ジェスチャーを検出しなかった限り、子供の他のイベントを無効にすることなく、次のように実行できます。
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;
}
私はこのウェブページ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にディスパッチされます。
短い答え: 最初にdispatchTouchEvent()
呼び出されます。
簡単なアドバイス:dispatchTouchEvent()
制御が難しいため、オーバーライドしないでください。パフォーマンスが低下する場合があります。私見、私はオーバーライドすることをお勧めしますonInterceptTouchEvent()
。
ほとんどの回答は、アクティビティ/ビューグループ/ビューでのフロータッチイベントについてかなり明確に言及しているため、これらのメソッドのコードの詳細をViewGroup
(を無視してdispatchTouchEvent()
)追加するだけです。
onInterceptTouchEvent()
最初に呼び出され、ACTIONイベントはそれぞれ下へ->移動->上へ呼び出されます。2つのケースがあります。
3つのケース(ACTION_DOWN、ACTION_MOVE、ACTION_UP)でfalseを返す場合、親はこのタッチイベントを必要としないためonTouch()
、親の呼び出しは行われずonTouch()
、子の呼び出しが代わりに呼び出されます。ただし注意してください:
onInterceptTouchEvent()
まだ限り、その子が呼び出さないように、タッチイベントを受信し続けますrequestDisallowInterceptTouchEvent(true)
。onTouch()
親に送り返します。逆にtrueを返すと、親はこのタッチイベントをすぐに盗み、すぐにonInterceptTouchEvent()
停止しonTouch()
ます。親が呼び出されるのではなく、すべてonTouch()
の子が最後のアクションイベントACTION_CANCEL(つまり、親を意味します)を受け取りますタッチイベントを盗み、子供たちはそれ以降それを処理できません)。onInterceptTouchEvent()
false を返すフローは正常ですが、trueを返す場合と少し混乱するため、ここにリストします。
onTouch()
親はACTION_DOWN受け取ると、再び次のアクション(ACTION_MOVE、ACTION_UP)。onTouch()
と、次の ACTION_MOVE(と同じACTION_MOVEではありませんonInterceptTouchEvent()
)と次のアクション(ACTION_MOVE、ACTION_UP)を受け取ります。onTouch()
両親のはなりませそれはタッチイベントを盗むために遅すぎる親のためだからまったく呼び出さ。さらにもう1つ重要なことは、イベントのACTION_DOWNによって、onTouch()
ビューがそのイベントからより多くのアクションを受け取りたいかどうかを決定することです。のACTION_DOWNでビューがtrueを返す場合、ビューはonTouch()
そのイベントからさらにアクションを受け取る用意があることを意味します。それ以外の場合、ACTION_DOWNでfalseを返すonTouch()
と、ビューがそのイベントからそれ以上のアクションを受け取らないことを意味します。
この動画https://www.youtube.com/watch?v=SYoN-OvdZ3M&list=PLonJJ3BVjZW6CtAMbJz1XD8ELUs1KXaTD&index=19と次の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;
}
これをカスタムオーバーレイと一緒に使用して、バックグラウンドビューがタッチイベントに応答しないようにしました。
主な違い:
•Activity.dispatchTouchEvent(MotionEvent)-これにより、アクティビティがウィンドウにディスパッチされる前にすべてのタッチイベントをインターセプトできます。
•ViewGroup.onInterceptTouchEvent(MotionEvent)-これにより、ViewGroupが子ビューにディスパッチされるときにイベントを監視できます。
ViewGroup onInterceptTouchEvent()
は常に、ACTION_DOWN
最初に発生するイベントであるイベントのエントリポイントです。
ViewGroupでこのジェスチャを処理する場合は、trueを返しますonInterceptTouchEvent()
。trueを返すonTouchEvent()
と、ViewGroup は次のACTION_UP
またはまで後続のすべてのイベントを受け取ります。ACTION_CANCEL
ほとんどの場合、ACTION_DOWN
とACTION_UP
またACTION_CANCEL
はの間のタッチイベントはACTION_MOVE
、通常、スクロール/フリングジェスチャとして認識されます。
からfalseを返すonInterceptTouchEvent()
と、ターゲットビューonTouchEvent()
が呼び出されます。からtrueが返されるまで、後続のメッセージに対して繰り返されますonInterceptTouchEvent()
。
ソース:http : //neevek.net/posts/2013/10/13/implementing-onInterceptTouchEvent-and-onTouchEvent-for-ViewGroup.html
public boolean dispatchTouchEvent(MotionEvent ev){
boolean consume =false;
if(onInterceptTouchEvent(ev){
consume = onTouchEvent(ev);
}else{
consume = child.dispatchTouchEvent(ev);
}
}