一点の見解:Androidイベント配信メカニズム(二)
9450 ワード
点见解:Androidイベント配信メカニズム(一)-基本概念解釈点见解:Androidイベント配信メカニズム(二)-分析ViewGroup一点见解:Androidイベント配信メカニズム(三)-分析View
本文は主にイベント配布メカニズムの伝達経路と伝達規則を分析し、
ソースコードの分析について、みんなはいつも具体的なソースコードを見つけることができると仮定して、だから肝心な部分だけを貼って分析します.
配布元の論理分析
最初から一番はっきりしている.
イベントは最初にシステムによって
抜粋したソースコードから結論が得られる
システムは、イベントを
ソースBonusは、 は
ViewGroupから
上記の分析から分かるように、最初にイベントを受信した
コントロール間イベント転送の開始方法は
指摘に値するのは、
方法の命名から分かるように、この方法の役割はイベントを配布することであるため、イベント配布メカニズムの実現論理はこの方法の中にあり、この部分のコードは200行以上あり、その中の多くのコードはコントロールの状態を一致させたり、多点タッチの問題を処理したりしている.本文はこの部分の実現に関心がないので、分析前に明確に分析する鍵が必要である.イベントを次のコントロールにどのように渡すか、前に述べたブロックなど を含む.戻り値はイベントが消費されたかどうかを識別するので、戻り値をどのように決定するかは、コードをより明確にするために、ソースコードをセグメントごとに分析し、以下のソースコードはすべてViewGroup#dispatchTouchEvent から来ている.
伝達規則
イベントをサブコントロールに配布するため、このメソッドでは必ずサブコントロールを巡回するので、まずこの部分の巡回コードを見つけます.以下のようにします.
以上、遍歴の過程で重要な判断の中で
サブコントロールに渡された後、
この遍歴コードは1つの3重条件判断に含まれているので、実行されない可能性があると言って、判断の条件を見てみましょう.
第1の再判断は、命名に基づいて
このコードは常に実行され、重要な判断根拠は
サブコントロールが
空でない場合、
ここまで分析すると,イベント配布メカニズムがどのようにコントロール間でイベントを伝達するかが分かった.
親コントロールは、すべての子コントロールが
ブロックイベント
一般に
次に
しかし、遮断方法には条件判断がある.は、 を呼び出すことはありません.は、
これで
ソースBonusは、 は、サブコントロールに のデフォルトの遍歴順序は、レイアウトにおけるサブコントロールのZ軸値に基づいて決定するが、
本明細書では、サブコントロールがイベントを受信するかどうかにかかわらず、イベントは
本文は主にイベント配布メカニズムの伝達経路と伝達規則を分析し、
ViewGroup
の分析に重点を置いた.ソースコードの分析について、みんなはいつも具体的なソースコードを見つけることができると仮定して、だから肝心な部分だけを貼って分析します.
配布元の論理分析
最初から一番はっきりしている.
イベントは最初にシステムによって
Activity#dispatchTouchEvent(MotionEvent ev)
に配布されます.Activity
はView
ではないため、コントロール間のイベント配信ロジックにはまだ入っていないことに注意する.では、Activity
はどのように事件を最初のView
に伝えたのか、誰に伝えたのか、ソースコードを見てみましょう.// Activity#dispatchTouchEventpublic
boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
onUserInteraction();
}
if (getWindow().superDispatchTouchEvent(ev)) {//
return true;
}
return onTouchEvent(ev);
}
Activity
は単なる中継局であるため、コードは多くなく、キーコードはgetWindow().superDispatchTouchEvent(ev)
である.getWindow()
はWindow
の抽象クラスを返します.Androidでは、この抽象クラスを継承している唯一のクラスはPhoneWindow
なので、ここで実際に呼び出されたのはPhoneWindow#superDispatchTouchEvent(MotionEvent ev)
で、同じPhoneWindow
もView
ではありません.だから、PhoneWindowソースコードも見なければなりません.// PhoneWindow.javaprivate DecorView mDecor;
@Overridepublic boolean superDispatchTouchEvent(MotionEvent event) {
return mDecor.superDispatchTouchEvent(event);
}
// PhoneWindow.DecorView
private final class DecorView extends FrameLayout implements RootViewSurfaceTaker {
public boolean superDispatchTouchEvent(MotionEvent event) {
return super.dispatchTouchEvent(event);
}
}
抜粋したソースコードから結論が得られる
システムは、イベントを
Activity
に配布する、PhoneWindow
に伝達し、PhoneWindow
の例のDecorView
に伝達する、DecorView
はView
(FrameLayout
を継承)であり、その後、イベントはコントロール間伝達ロジックに入る.ソースBonus
Activity#dispatchTouchEvent(MotionEvent ev)
がイベント配信の開始点であるため、この方法を書き換えるPhoneWindow#superDispatchTouchEvent(MotionEvent ev)
を呼び出すことなく、Activity
内のコントロール全体がイベントを受信ことができないようにすることができる.実際には、キーボードのクリックイベントなどをブロックできるdispatchXXXEvent()
の方法がいくつかある.Activity#dispatchTouchEvent(MotionEvent ev)
の中にonUserInteraction()
が現れ、この方法はコールバックと見なすことができ、任意のユーザ操作が開始する前にキーボード操作を含めてこの方法が呼び出されるので、この方法を書き換えてユーザ操作の開始ノードを傍受することができる.もう一つの対応方法onUserLeaveHint()
Activity#dispatchTouchEvent(MotionEvent ev)
においてPhoneWindow#superDispatchTouchEvent(MotionEvent ev)
がこのイベントを消費すると、Activity#onTouchEvent(MotionEvent event)
が消費イベントを試みるように呼び出される.ViewGroupから
上記の分析から分かるように、最初にイベントを受信した
View
の方法はDecorView#superDispatchTouchEvent(MotionEvent event)
で、中にはsuper.dispatchTouchEvent()
が直接呼び出され、DecorView
は直接FrameLayout
を継承し、追跡して結論を得ることができる.コントロール間イベント転送の開始方法は
ViewGroup#dispatchTouchEvent()
です.指摘に値するのは、
View
にもdispatchTouchEvent()
があり、後で言う.方法の命名から分かるように、この方法の役割はイベントを配布することであるため、イベント配布メカニズムの実現論理はこの方法の中にあり、この部分のコードは200行以上あり、その中の多くのコードはコントロールの状態を一致させたり、多点タッチの問題を処理したりしている.本文はこの部分の実現に関心がないので、分析前に明確に分析する鍵が必要である.
伝達規則
イベントをサブコントロールに配布するため、このメソッドでは必ずサブコントロールを巡回するので、まずこの部分の巡回コードを見つけます.以下のようにします.
for (int i = childrenCount - 1; i >= 0; i--) {
// child ,
children[childIndex] final View child = (preorderedList == null)
? children[childIndex] : preorderedList.get(childIndex);
//
if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {//
// ...
newTouchTarget = addTouchTarget(child, idBitsToAssign);//
// ... break;
}
}
以上、遍歴の過程で重要な判断の中で
ViewGroup#dispatchTransformedTouchEvent
の方法を実行すると、コードが貼られず、一連の判断があるが、結局childパラメータが空であるか否かを判断し、空であるsuper.dispatchTouchEvent()
を実行し、空でないchild.dispatchTouchEvent()
を実行し、ここでchildは必ず空ではないので、ここでイベントをサブコントロールに伝達する.サブコントロールに渡された後、
true
に戻って、サブコントロールがこのイベントを受信したことを証明します.注意してください.ここには、現在のこの受信イベントのサブコントロールをViewGroup#addTouchTarget
オブジェクトに変換し、TouchTarget
に値を付与する別のキーメソッドmFirstTouchTarget
があります.なぜですか.この遍歴コードは1つの3重条件判断に含まれているので、実行されない可能性があると言って、判断の条件を見てみましょう.
if (!canceled && !intercepted) {
// ...
if (actionMasked == MotionEvent.ACTION_DOWN || (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN) || actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
// ...
if (newTouchTarget == null && childrenCount != 0) {
//
}
}
}
第1の再判断は、命名に基づいて
ACTION_CANCEL
イベントと現在のコントロールによってブロックされたイベントと推定され、その後に議論される.第3の判断は、サブコントロールが存在する限りtrue
である.重要なのは第2の判断であり、イベントがACTION_DOWN
、ACTION_POINTER_DOWN
またはACTION_HOVER_MOVE
でなければ遍歴コードに入ることができないことを制限している(配布メカニズムについてはACTION_DOWN
イベントにしか注目していないので、後で他の2つを省略する)、つまりイベントがACTION_MOVE
などの中間イベントである場合、遍歴コードを直接実行することはなく、サブコントロールにイベントを配布することもなく、ViewGroup#dispatchTransformedTouchEvent
を呼び出し、残りのコードを探して見つける場所もあります.if (mFirstTouchTarget == null) {
handled = dispatchTransformedTouchEvent(ev, canceled, null, TouchTarget.ALL_POINTER_IDS);
} else {
// ...
TouchTarget target = mFirstTouchTarget;
while (target != null) {
// ...
final TouchTarget next = target.next;
if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {
handled = true;
} else {
final boolean cancelChild = resetCancelNextUpFlag(target.child) || intercepted;
if (dispatchTransformedTouchEvent(ev, cancelChild, target.child, target.pointerIdBits)) {
handled = true;
}
// ...
}
// ...
target = next;
}
}
このコードは常に実行され、重要な判断根拠は
mFirstTouchTarget
が空であるかどうかであり、空である場合、最後にView#dispatchTouchEvent
が呼び出されます.そうしないと、mFirstTouchTarget
に対応するサブコントロールにイベントが渡されます.上記の分析と結びつけて、サブコントロールがACTION_DOWN
などのイベントを受信した場合にのみ、空ではありません.つまり、サブコントロールが
ACTION_DOWN
を受信(すなわち、View#dispatchTouchEvent
対ACTION_DOWN
がtrue
を返さない)場合、後続のイベントはこのサブコントロールに配布されない.空でない場合、
mFirstTouchTarget
は実際にはチェーンテーブルであり、チェーンテーブルのすべてのサブコントロールにイベントを配布します.これはマルチタッチの処理であり、本稿で注目している問題ではありません.分析しないで、他のACTION_DOWN
以外のイベントの伝達がすべてのサブコントロールを再遍歴しないことを知っておく必要があります.ACTION_DOWN
は操作全体(一連のイベント)の起点です.このとき、後続のイベントが伝達するサブコントロールが決定される.ここまで分析すると,イベント配布メカニズムがどのようにコントロール間でイベントを伝達するかが分かった.
親コントロールは、すべての子コントロールが
ACTION_DOWN
のイベントを受信するかどうかを尋ねるサブコントロールをチェーンテーブルに保存し、後続のイベントの配布対象を決定し、他のイベントが親コントロールに渡されたときにチェーンテーブルのサブコントロールに直接イベントを渡す.サブコントロール受信ACTION_DOWN
がない場合、View#dispatchTouchEvent
が実行されるブロックイベント
ViewGroup
のイベント配信のもう一つのポイントは、上述の遍歴の第1の再判断におけるintercepted
変数である、この変数がtrue
であれば、ACTION_DOWN
イベントであってもサブコントロールに問い合わせを遍歴することはない、このときmFirstTouchTarget
チェーンテーブルは必ず空であり、後続のすべてのイベントはView#dispatchTouchEvent
に伝達され、サブコントロールに伝達されない.つまり、親コントロールは、渡されたイベントをブロックします.final boolean intercepted;
if (actionMasked == MotionEvent.ACTION_DOWN || mFirstTouchTarget != null) {
final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
if (!disallowIntercept) {
intercepted = onInterceptTouchEvent(ev);
// ...
} else {
intercepted = false;
}
} else {
intercepted = true;
}
一般に
intercepted
の値はViewGroup#onInterceptTouchEvent
によって決定するが、View
にはこの方法はないことが指摘され、View
には他のサブコントロールがないため、イベントをブロックする必要がないため理解しやすい.次に
ViewGroup#onInterceptTouchEvent
を見て、中は直接false
に戻って、デフォルトはイベントをブロックしないので、ViewGroup#onInterceptTouchEvent
を書き換えることによって、特定のイベントをブロックすることができるしかし、遮断方法には条件判断がある.
ACTION_DOWN
イベント、またはmFirstTouchTarget
が空ではなく、mFirstTouchTarget
が最初の消費イベントのサブコントロールである必要があるため、サブコントロールがイベントを消費した場合、その後、ViewGroup#onInterceptTouchEvent
が呼び出され、親コントロールはイベントをブロックする機会があり、親コントロール自身がACTION_DOWN
イベントを消費した場合、ViewGroup#onInterceptTouchEvent
がFLAG_DISALLOW_INTERCEPT
のフラグビットを設ける必要があり、関連する方法ViewGroup#requestDisallowInterceptTouchEvent
、すなわち、この方法を呼び出すことによってブロック機構を無効にすることができる.これで
ViewGroup
の配布メカニズムに関する方法の大まかな分析が完了した.ソースBonus
ViewGroup#requestDisallowInterceptTouchEvent
によってブロッキング機構を無効にすることができる.AccessibilityFocused
を設定ことにより、サブコントロールを巡回する際に、そのサブコントロールがACTION_DOWN
イベントを受信するか否かを優先的に問い合わせることができる.ViewGroup#getChildDrawingOrder
を書き換えてデフォルトのサブコントロール遍歴順序を変更することができる.本明細書では、サブコントロールがイベントを受信するかどうかにかかわらず、イベントは
View#dispatchTouchEvent
に伝達されることが分かるので、次編ではView
クラスを分析する