Androidイベント配信メカニズムの探索
8937 ワード
まず,クリックイベントの配信とは,実際にはMotionEventイベントを配信する過程であることを理解する.MotionEventが生成されると、システムはこのイベントを特定のViewに渡す必要があります.この伝達プロセスは配布プロセスです.それ以外に、同じイベントシーケンスとは、指がスクリーンに触れた瞬間から、指がスクリーンから離れた瞬間に終わることを意味し、この過程で発生した一連のイベントであり、この時間シーケンスはdownイベントで始まり、中には数の不定のmoveイベントが含まれ、最終的にupイベントで終わる.
Activityによるクリックイベントの配布プロセス
クリック操作が発生すると、イベントは現在のActivityに最初に渡され、Activityは
Window(PhoneWindow)のsuperDispatchTouchEvent()メソッドを見てみましょう
DecorViewでは、最上位の
トップレベルのView(View Group)によるクリックイベントの配布プロセス
この部分の擬似コードは以下のように、基本的にこの過程の思想を含んでいる.
上のコードは、MotionEventイベントが渡された後、まず
次にソースコードによって、主にViewGroupの最初のステップは、現在のViewがクリックイベントの下のコードのFLAG_をブロックするか否かを判断するDISALLOW_INTERCEPTタグビットは 第2のステップは、第1のステップで判断された場合、 第3のステップでは、ViewGroupがイベントをブロックしない場合、イベントはサブviewによって処理されます.理解を容易にするために、以下では、配布に関連する主なプロセスを擬似コードで表す.
次はすべての要素を巡回した後、イベントが適切に処理されていない場合、この2つの状況に分けられます.1つ目は、ViewGroupがイベントを受け入れることができるサブViewがないことです.2つ目のケースは、サブViewがクリックイベントを処理したが、
3番目のパラメータはchildがnullに変更されたため、
ビューのクリックイベントの処理手順
現在議論されているViewには、Viewを継承したView Groupは含まれていません.次はViewの
ビューは、クリックイベントをサブビュー(サブビューなし)に渡す必要がないため、ビューの
以下、
Viewにエージェントが設定されている場合、
次に、一般的な
ビューに
viewの
Activityによるクリックイベントの配布プロセス
クリック操作が発生すると、イベントは現在のActivityに最初に渡され、Activityは
dispatchTouchEvent()
を通過します.public boolean dispatchTouchEvent(MotionEvent ev) {
...
if (getWindow().superDiapatchTouchEvent(ev)) { // window superDispatchTouchEvent() , , true,
return true;
}
return onTouchEvent(ev); // window superDispatchTouchEvent() , false, Activity onTouchEvent(ev);
}
Window(PhoneWindow)のsuperDispatchTouchEvent()メソッドを見てみましょう
public boolean superDispatchTouchEvent(MotionEvent ev){
return mDecor.superDispatchTouchEvent(ev); // DecorView superDispatchTouchEvent(ev)
}
DecorViewでは、最上位の
dispatchTouchEvent(ev)
メソッドを呼び出してMotionEventイベントを最上位のViewに渡します.トップレベルのView(View Group)によるクリックイベントの配布プロセス
この部分の擬似コードは以下のように、基本的にこの過程の思想を含んでいる.
public boolean dispatchTouchEvent(MotionEvent ev){
boolean consume = false;
if (onInterceptTouchEvent(ev)){
consume = onTouchEvent(ev)
} else {
consume = child.dispatchTouchEvent(ev)
}
return consume;
}
上のコードは、MotionEventイベントが渡された後、まず
onInterceptTouchEvent(ev)
の方法でイベントをブロックするかどうかを判断し、ブロックする場合はonTouchEvent(ev)
の処理にMotionEventイベントを渡し、戻り結果を変数に保存する処理の考え方を明確に示している.ブロックしない場合、MotionEventイベントはサブview、すなわちchild.dispatchTouchEvent(ev)
に渡され、戻り結果は変数に保存される.最後に結果変数を返します.結果変数のデフォルトはfalseです.次にソースコードによって、主にViewGroupの
dispatchTouchEvent()
メソッドで発生するMotionEventがViewGroupで配布されるプロセスを探索する.requestDisallowInterceptTouchEvent()
の方法で設定され、一般的にサブviewに用いられる.FLAG_DISALLOW_INTERCEPTが設定されると、ViewGroupはすでに子Viewの「ディスク」(mFirstTouchTarget ! = null
)があるMove,Upイベントをブロックできません.ViewGroupでACTION_を受信しているのでDOWNイベント後、FLAG_がリセットされますDISALLOW_INTERCEPTというフラグビット.//check for interception.
final boolean intercepted; // View
if ( actionMasked == MotionEvent.ACTION_DOWN || mFirstTouchTarget != null) {
//mFirstTouchTarget != null , , dispatchTouchEvent(event) true
final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
if (!disallowIntercept){
intercepted = onInterceptTouchEvent(ev);
ev.setAction(action);
} else {
intercepted = false;
}
} else { // move up, view (mFirstTouchTarget == 0)
intercepted = true;
}
ntercepted == true
であり、現在のViewは、イベントをブロックして処理する準備をしている.onTouchListener()
が設定されている場合、onTouchListener()
のonTouch()
メソッドがコールバックされます.このとき、イベントがどのように処理を継続するかはonTouch()
の戻り値にも依存し、falseが返されると、現在のviewのonTouchEvent()
メソッドが呼び出される.trueが返されると、onTouchEvent()
メソッドは呼び出されません.onTouchEvent()
メソッドでは、現在onClickListener
が設定されている場合、onClick()
メソッドが呼び出されます.for ( childview){
if(! childview ) { // view view
continue; // , continue, view
}
if(dispatchTransformedTouchEvent(ev,false,child,idBitsToAssign)){ // View dispatchTouchEvent() , view dispatchTouchEvent() true,
// view dispatchTouchEvent() fasle,
...
newTouchTarget = addTouchTarget(child, isBitsToAssign); // mFirstTouchTarget
alreadyDispatchedToNewTouchTarget = true;
break; //
}
}
次は
dispatchTransformedTouchEvent(ev,false,child,idBitsToAssign)
のコードの一部ですif(child == null){
handle = super.dispatchTouchEvent(event);
} else {
handle = child.dispatchTouchEvent(event);
}
addTouchTarget()
メソッドのコードを次に示します.private TouchTarget addTouchTarget(View child, int pointerIdBits){
TouchTarget target = TouchTarget.obtain(child,pointerIdBits);
target.next = mFirstTouchTarget;
mFirstTouchTarget = target;
return target;
}
dispatchTouchEvent
でfalseが返され、一般的にはonTouchEvent()
でfalseが返されたためである.どちらの場合も、ViewGroupはクリックイベントを自分で処理します.if (mFirstTouchTarget == null) {
handle = dispatchTransformedTouchEvent(en,canceled,null,TouchTarget.ALL_POINTER_IDS);
}
3番目のパラメータはchildがnullに変更されたため、
super.dispatchTouchEvent(event);
、すなわちViewGroup親ViewのdispatchTouchEvent()
メソッドが呼び出されます.ViewのdispatchTouchEvent()
の方法は以下の分析を参照してください.ビューのクリックイベントの処理手順
現在議論されているViewには、Viewを継承したView Groupは含まれていません.次はViewの
dispatchTouchEvent()
メソッドですpublic boolean dispatchTouchEvent(MotionEvent event){
boolean result = false;
...
if (onFilterTouchEventForSecurity(event)){
ListenerInfo li = mListenerInfo;
if(li != null && li.mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED
&&li.mOnTouchListener.onTouch(this,event)){
result = true;
}
if (!result && onTouchEvent(event)){
result = true;
}
}
...
return result;
}
ビューは、クリックイベントをサブビュー(サブビューなし)に渡す必要がないため、ビューの
dispatchTouchEvent()
の方法は比較的簡単である.第1のステップは、onTouchListener
が設定されているかどうかを判断し、そのうちのonTouch()
メソッドがtrueを返す場合、onTouchEvent()
メソッドは呼び出されない.このように、onTouchListener
の優先度はonTouchEvent
より高く、クリックイベントの外部処理に有利である.以下、
onToucEvent()
のコードの一部であり、ビューが使用不可能な状態でイベントをクリックする処理手順である.ビューが使用不可能な状態であっても、ビューのCLICKABLE
およびLONG_CLICKABLE
がtrueである限り、このイベントは消費され、trueに戻る(onTouchEvent()
メソッドが終了する).// View onTouchEvent()
if ((viewFlags & ENABLED_MASK) == DISABLED) {
if(event.getAction() == MotionEvent.ACTION_UP && (mPrivateFlags & PFLAG_PRESSED) != 0){
setPressed(false);
}
return ((viewFlags & CLICKABLE) == CLICKABLE ||
(viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE));
}
Viewにエージェントが設定されている場合、
TouchDelegate
のonTouchEvent
メソッドも実行され、このonTouchEventの動作メカニズムはOnTouchListener
と類似しているように見えます.コードは、mTouchDelegate
が設定されているか否かを判断し、mTouchDelegate.onTouchEvent(event);
を実行してメソッドがtrueを返すと、viewのonTouchEvent()
もtrueを返す(つまり、メソッドが終了し、=イベントが消費される).// View onTouchEvent()
if (mTouchDelegate = ! null){
if (mTouchDelegate.onTouchEvent(event)){
return true;
}
}
次に、一般的な
onTouchEvent()
およびCLICKABLE
イベントに対するLONG_CLICKABLE
メソッドの処理を参照する.viewのCLICKABLE
およびLONG_CLICKABLE
がtrueである限り、onTouchEvent()
メソッドはtrueを返す(このメソッドが終了し、イベントが消費されることを意味する).そしてACTION_UPイベントが発生すると、performClick()
メソッドがトリガーされます.// View onTouchEvent() , 。
if ((viewFlags & CLICKABLE) == CLICKABLE || (viewFlags & LONG_CLICKALBE)){
switch (event.getAciont()){
case MotionEvent.ACTION_UP:
...
if(...) performClick();
break;
}
return true;
}
ビューに
onClickListener
が設定されている場合、performClick
メソッドの内部でonClick()
メソッドが呼び出されます.コードは次のとおりです.public boolean performClick(){
final boolean result;
final ListenerInfo li = mListenerInfo;
if ( li != null && li.mOnClickListener != null) {
...
li.mOnClickListener.onClick(this);
result = true;
} else {
result = fasle;
}
...
return result;
}
viewの
LONG_CLICKABLE
のデフォルト属性はfalseであり、CLICKABLEのデフォルト値は特定のviewに関連している.また、2つの属性は、setClickable()
およびsetLongClickable()
によってそれぞれtrueとすることができることに注意されたい.