Android実現メカニズム(三)-Viewイベント配信メカニズム
12640 ワード
Viewのイベントとは、タッチスクリーンにtouchが到達すると、システムが効果を生み出すプロセスを指し、このプロセスでは主に3つの方法が含まれます. dispatchTouchEvent(MotionEvent ev) onInterceptTouchEvent(MotionEvent ev) onTouchEvent(MotionEvent ev)
これらの方法に応答するベクターは、View、View Group、またはActivityであってもよい.ここで、ViewGroupはこの3つの方法のすべてに応答することができ、ActivityはonInterceptTouchEventを処理することができず、Viewは最も原子のコントロールとしてonTouchEventに対応するしかない.
次にTouchイベントの簡単な分析を行います
イベント配信:public boolean dispatchTouchEvent(MotionEvent ev)
Touchイベントが発生したときにActivityのdispatchTouchEvent(MotionEvent ev)メソッドは、ルート要素から最内層のサブ要素に順次伝達されるか、中間のある要素の中である条件によって伝達が停止されるまで、イベントを最外層ViewのdispatchTouchEvent(MotionEvent ev)メソッドに伝達します.イベントは、ビューのdispatchTouchEvent(MotionEvent ev)メソッドによって配布される.dispatchTouchEventのイベント配布ロジックは次のとおりです. return trueの場合、イベントは現在のViewに配布され、dispatchTouchEventメソッドによって消費され、イベントは下への伝達を停止します. return falseの場合、イベント配布は2つのケースに分けられます. 現在のViewが取得したイベントが直接Activityから来た場合、イベントはActivityのonTouchEventに戻されて消費される. 現在のViewが取得したイベントが外部の親コントロールから来た場合、イベントは親ViewのonTouchEventに戻されて消費されます.
システムのデフォルトのsuperを返すと.dispatchTouchEvent(ev)では、現在のViewのonInterceptTouchEventメソッドにイベントが自動的に配布されます.
イベントブロック:public boolean onInterceptTouchEvent(MotionEvent ev)
外層ビューのdispatchTouchEvent(MotionEvent ev)メソッドはシステムのデフォルトのsuperを返す.dispatchTouchEvent(ev)の場合、イベントは現在のViewのonInterceptTouchEventメソッドに自動的に配布されます.onInterceptTouchEventのイベントブロックロジックは次のとおりです. onInterceptTouchEventがtrueを返すと、イベントがブロックされ、ブロックされたイベントが現在のViewのonTouchEventに渡されて処理されることを示す. onInterceptTouchEventがfalseに戻ると、イベントがローにされ、現在のView上のイベントがサブViewに渡され、サブViewのdispatchTouchEventによってこのイベントの配布が開始されることを示す. onInterceptTouchEventがsuperに戻る場合.onInterceptTouchEvent(ev)では、イベントはデフォルトでブロックされ、ブロックされたイベントは現在のViewのonTouchEventに渡されます.
イベントレスポンス:public boolean onTouchEvent(MotionEvent ev)
dispatchTouchEventでsuperを返す.dispatchTouchEvent(ev)、onInterceptTouchEventはtrueまたはsuperを返す.onInterceptTouchEvent(ev)の場合onTouchEventが呼び出されます.onTouchEventのイベント応答ロジックは次のとおりです.イベントが現在のViewのonTouchEventメソッドに渡され、このメソッドがfalseを返すと、このイベントは現在のViewから上に渡され、上位のViewのonTouchEventによって受信され、上に渡されたonTouchEventもfalseに戻ると、このイベントは「消失」し、次のイベントは受信されません. trueが返されると、イベントが受信され消費される. superを返すと.onTouchEvent(ev)のデフォルト処理イベントの論理はfalseを返すときと同じです.
ここまで,Touchイベントに関する3つのアプローチを解析した.次に、ソースコードについて、ViewとView Groupがなぜ上記の結果を得たのかを分析します.
Viewイベント配信メカニズム
まずViewのdispatchTouchEventメソッドを見てみましょう
このビューがonTouchListenerに設定され、このビューがenableであり、onTouchListenerの戻り値がtrueである場合、ViewのonTouchEventは呼び出されません.
そしてViewのonTouchEventメソッドを見てみましょう現在のViewがDisabled状態であり、クリック可能である場合、イベント(return true)が消費される.無視できますが、私たちのポイントではありません. mTouchDelegateが設定されている場合、イベントはエージェントに渡され、直接return trueになります.自分のViewがtouchの範囲を増やすことを望んでいる場合は、TouchDelegateを使ってみてください.ここでもポイントではありません.無視できます. このビューをクリックまたは長押しすることができる場合、trueに戻ってこのイベントを消費したことを示し、イベントタイプを順次判断してどのように操作するかを決定し、イベントタイプにはDOWN、MOVE、UPの3種類の がある.
まとめ全体のViewのイベント転送プロセスは:View.dispatchEvent->View.setOnTouchListener->View.onTouchEventはdispatchTouchEventでOnTouchListenerの判断を行い、OnTouchListenerがnullではなくtrueに戻るとイベントが消費され、onTouchEventは実行されないことを示す.そうでない場合はonTouchEventを実行します. onTouchEventのDOWN,MOVE,UP DOWNの場合:まず設定フラグはPREPRESSEDで、mHasPerformedLongPress=falseを設定します.そして115 ms後のmPendingCheckForTapを発行します. 115 ms内にUPがトリガされていない場合は、フラグをPRESSEDに設定し、PREPRESSEDフラグをクリアするとともに、500-115 msの遅延を発行し、長押しタスクメッセージを検出する. 500 ms以内(DOWNトリガから計算開始)であればLongClickListenerがトリガーされる:このときLongClickListenerがnullでない場合はコールバックが実行され、LongClickListenerである.onClickはtrueを返し、mHasPerformedLongPressをtrueに設定します.そうでなければmHasPerformedLongPressはfalseのままです.
MOVE時:は主にユーザーがコントロールを切り出したかどうかを検出することであり、もし切り出した場合:115 ms内で、直接mPendingCheckForTapを削除する.115 ms後、フラグのPRESSEDを除去し、同時に長押しのチェックを除去する:removeLongPressCallback()
UP時: 115 ms内でUPがトリガーされ、このときPREPRESSEDがマークされると、UnsetPressedState,setPressed(false)が実行される.setPressは転送され、ViewでdispatchSetPressedメソッドを複写して受信できます. 115 ms-500 ms間、すなわち長押しが発生していない場合、まず長押し検出を除去し、onClickコールバックを実行する. が500 ms以降である場合、a.onLongClickListenerが設定、onLongClickListenerが設定されている.onClickがtrueを返すと、クリックイベントOnClickイベントはトリガーできません.b.onLongClickListenerまたはonLongClickListenerは設定.onClickがfalseに戻ると、イベントOnClickイベントをクリックしてもトリガーできます. 最後にmUnsetPressedStateを実行する.run()は、setPressedを渡し、PRESSED IDを除去します.
これらの方法に応答するベクターは、View、View Group、またはActivityであってもよい.ここで、ViewGroupはこの3つの方法のすべてに応答することができ、ActivityはonInterceptTouchEventを処理することができず、Viewは最も原子のコントロールとしてonTouchEventに対応するしかない.
次にTouchイベントの簡単な分析を行います
イベント配信:public boolean dispatchTouchEvent(MotionEvent ev)
Touchイベントが発生したときにActivityのdispatchTouchEvent(MotionEvent ev)メソッドは、ルート要素から最内層のサブ要素に順次伝達されるか、中間のある要素の中である条件によって伝達が停止されるまで、イベントを最外層ViewのdispatchTouchEvent(MotionEvent ev)メソッドに伝達します.イベントは、ビューのdispatchTouchEvent(MotionEvent ev)メソッドによって配布される.dispatchTouchEventのイベント配布ロジックは次のとおりです.
イベントブロック:public boolean onInterceptTouchEvent(MotionEvent ev)
外層ビューのdispatchTouchEvent(MotionEvent ev)メソッドはシステムのデフォルトのsuperを返す.dispatchTouchEvent(ev)の場合、イベントは現在のViewのonInterceptTouchEventメソッドに自動的に配布されます.onInterceptTouchEventのイベントブロックロジックは次のとおりです.
イベントレスポンス:public boolean onTouchEvent(MotionEvent ev)
dispatchTouchEventでsuperを返す.dispatchTouchEvent(ev)、onInterceptTouchEventはtrueまたはsuperを返す.onInterceptTouchEvent(ev)の場合onTouchEventが呼び出されます.onTouchEventのイベント応答ロジックは次のとおりです.
ここまで,Touchイベントに関する3つのアプローチを解析した.次に、ソースコードについて、ViewとView Groupがなぜ上記の結果を得たのかを分析します.
Viewイベント配信メカニズム
まずViewのdispatchTouchEventメソッドを見てみましょう
public boolean dispatchTouchEvent(MotionEvent event) {
if (!onFilterTouchEventForSecurity(event)) {
return false;
}
if (mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED &&
mOnTouchListener.onTouch(this, event)) {
return true;
}
return onTouchEvent(event);
}
このビューがonTouchListenerに設定され、このビューがenableであり、onTouchListenerの戻り値がtrueである場合、ViewのonTouchEventは呼び出されません.
そしてViewのonTouchEventメソッドを見てみましょう
public boolean onTouchEvent(MotionEvent event) {
final int viewFlags = mViewFlags;
if ((viewFlags & ENABLED_MASK) == DISABLED) {
// A disabled view that is clickable still consumes the touch
// events, it just doesn't respond to them.
return (((viewFlags & CLICKABLE) == CLICKABLE ||
(viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE));
}
if (mTouchDelegate != null) {
if (mTouchDelegate.onTouchEvent(event)) {
return true;
}
}
if (((viewFlags & CLICKABLE) == CLICKABLE ||
(viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)) {
switch (event.getAction()) {
case MotionEvent.ACTION_UP:
boolean prepressed = (mPrivateFlags & PREPRESSED) != 0;
if ((mPrivateFlags & PRESSED) != 0 || prepressed) {
// take focus if we don't have it already and we should in
// touch mode.
boolean focusTaken = false;
if (isFocusable() && isFocusableInTouchMode() && !isFocused()) {
focusTaken = requestFocus();
}
if (!mHasPerformedLongPress) {
// This is a tap, so remove the longpress check
removeLongPressCallback();
// Only perform take click actions if we were in the pressed state
if (!focusTaken) {
// Use a Runnable and post this rather than calling
// performClick directly. This lets other visual state
// of the view update before click actions start.
if (mPerformClick == null) {
mPerformClick = new PerformClick();
}
if (!post(mPerformClick)) {
performClick();
}
}
}
if (mUnsetPressedState == null) {
mUnsetPressedState = new UnsetPressedState();
}
if (prepressed) {
mPrivateFlags |= PRESSED;
refreshDrawableState();
postDelayed(mUnsetPressedState,
ViewConfiguration.getPressedStateDuration());
} else if (!post(mUnsetPressedState)) {
// If the post failed, unpress right now
mUnsetPressedState.run();
}
removeTapCallback();
}
break;
case MotionEvent.ACTION_DOWN:
if (mPendingCheckForTap == null) {
mPendingCheckForTap = new CheckForTap();
}
mPrivateFlags |= PREPRESSED;
mHasPerformedLongPress = false;
postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());
break;
case MotionEvent.ACTION_CANCEL:
mPrivateFlags &= ~PRESSED;
refreshDrawableState();
removeTapCallback();
break;
case MotionEvent.ACTION_MOVE:
final int x = (int) event.getX();
final int y = (int) event.getY();
// Be lenient about moving outside of buttons
int slop = mTouchSlop;
if ((x < 0 - slop) || (x >= getWidth() + slop) ||
(y < 0 - slop) || (y >= getHeight() + slop)) {
// Outside button
removeTapCallback();
if ((mPrivateFlags & PRESSED) != 0) {
// Remove any future long press/tap checks
removeLongPressCallback();
// Need to switch from pressed to not pressed
mPrivateFlags &= ~PRESSED;
refreshDrawableState();
}
}
break;
}
return true;
}
return false;
}
まとめ
MOVE時:
UP時: