ソース解析 一、Activityによるクリックイベントの配布過程 クリック操作が発生すると、イベントは現在のActivityに最初に渡され、ActivityのdispatchTouchEventメソッドによってイベント配信が行われ、具体的な作業はActivity内部のWindowによって行われる.WindowはイベントをDecorViewに渡します.Activity#dispatchTouchEvent:
public boolean dispatchTouchEvent(MotionEvent ev) {
if(ev.getAction() == MotionEvent.ACTION_DOWN) {
onUserInteraction();
}
if(getWindow().superDispatchTouchEvent(ev)) {
return true;
}
return onTouchEvent(ev);
}
まずイベントがActivityに付属するWindowに渡されて配布され、trueを返すとイベントループ全体が終了し、falseを返すことはイベントが誰も処理していないことを意味し、すべてのViewのonTouchEventメソッドがfalseを返すと、ActivityのonTouchEventメソッドが呼び出されます.
PhoneWindow#superDispatchTouchEvent:
public boolean superDispatchTouchEvent(MotionEvent event) {
return mDecor.superDispatchTouchEvent(event);
}
PhoneWindowはイベントをDecorViewに直接渡した.DecorViewは、FrameLayoutを継承し、トップレベルのViewとして、一般的には内部に縦方向のLinearLayoutが含まれています.このLinearLayoutには上下2つの部分(具体的にはAndroidバージョンとテーマに関係しています)があり、上はタイトルバーで、下は内容欄です.ActivityでsetContentViewで設定したレイアウトファイルは、実はコンテンツバーに追加され、コンテンツバーのidはcontentです.getWindow().getDecorView()はDecorViewを取得できます.(ViewGroup)getWindow()を介して.getDecorView().findViewById(android.R.id.content)).getChildAt(0)は、Activityによって設定されたViewを得ることができる.例:
//
//
private void getParams() {
DisplayMetrics displayMetrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
Logger.d(" , width: " + displayMetrics.widthPixels + ", height: " + displayMetrics.heightPixels);
int statusBarId = getResources().getIdentifier("status_bar_height", "dimen", "android");
int statusBarHeight = 0;
if (statusBarId > 0) {
statusBarHeight = getResources().getDimensionPixelSize(statusBarId);
}
Logger.d(" , height: " + statusBarHeight);
ActionBar actionBar = getSupportActionBar();
Logger.d(" , height: " + actionBar.getHeight());
Logger.d("Activity main_layout , width: " + mMainLayout.getMeasuredWidth() + ", height: " + mMainLayout.getMeasuredHeight());
View decorView = getWindow().getDecorView();
Logger.d("DecorView , width: " + decorView.getMeasuredWidth() + ", height: " + decorView.getMeasuredHeight());
View contentView = ((ViewGroup)decorView.findViewById(android.R.id.content)).getChildAt(0);
Logger.d("DecorView content , width: " + contentView.getMeasuredWidth() + ", height: " + contentView.getMeasuredHeight());
}
//log
03-25 11:40:55.568 3256-3256/com.tomorrow.androidtest2 D/zwm: ╔════════════════════════════════════════════════════════════════════════════════════════
03-25 11:40:55.568 3256-3256/com.tomorrow.androidtest2 D/zwm: ║ Thread: main
03-25 11:40:55.568 3256-3256/com.tomorrow.androidtest2 D/zwm: ╟────────────────────────────────────────────────────────────────────────────────────────
03-25 11:40:55.568 3256-3256/com.tomorrow.androidtest2 D/zwm: ║ MainActivity.onWindowFocusChanged (MainActivity.java:63)
03-25 11:40:55.568 3256-3256/com.tomorrow.androidtest2 D/zwm: ║ MainActivity.getParams (MainActivity.java:38)
03-25 11:40:55.568 3256-3256/com.tomorrow.androidtest2 D/zwm: ╟────────────────────────────────────────────────────────────────────────────────────────
03-25 11:40:55.568 3256-3256/com.tomorrow.androidtest2 D/zwm: ║ , width: 720, height: 1280
03-25 11:40:55.568 3256-3256/com.tomorrow.androidtest2 D/zwm: ╚════════════════════════════════════════════════════════════════════════════════════════
03-25 11:40:55.568 3256-3256/com.tomorrow.androidtest2 D/zwm: ╔════════════════════════════════════════════════════════════════════════════════════════
03-25 11:40:55.568 3256-3256/com.tomorrow.androidtest2 D/zwm: ║ Thread: main
03-25 11:40:55.568 3256-3256/com.tomorrow.androidtest2 D/zwm: ╟────────────────────────────────────────────────────────────────────────────────────────
03-25 11:40:55.568 3256-3256/com.tomorrow.androidtest2 D/zwm: ║ MainActivity.onWindowFocusChanged (MainActivity.java:63)
03-25 11:40:55.568 3256-3256/com.tomorrow.androidtest2 D/zwm: ║ MainActivity.getParams (MainActivity.java:45)
03-25 11:40:55.568 3256-3256/com.tomorrow.androidtest2 D/zwm: ╟────────────────────────────────────────────────────────────────────────────────────────
03-25 11:40:55.568 3256-3256/com.tomorrow.androidtest2 D/zwm: ║ , height: 48
03-25 11:40:55.568 3256-3256/com.tomorrow.androidtest2 D/zwm: ╚════════════════════════════════════════════════════════════════════════════════════════
03-25 11:40:55.568 3256-3256/com.tomorrow.androidtest2 D/zwm: ╔════════════════════════════════════════════════════════════════════════════════════════
03-25 11:40:55.568 3256-3256/com.tomorrow.androidtest2 D/zwm: ║ Thread: main
03-25 11:40:55.568 3256-3256/com.tomorrow.androidtest2 D/zwm: ╟────────────────────────────────────────────────────────────────────────────────────────
03-25 11:40:55.568 3256-3256/com.tomorrow.androidtest2 D/zwm: ║ MainActivity.onWindowFocusChanged (MainActivity.java:63)
03-25 11:40:55.568 3256-3256/com.tomorrow.androidtest2 D/zwm: ║ MainActivity.getParams (MainActivity.java:48)
03-25 11:40:55.568 3256-3256/com.tomorrow.androidtest2 D/zwm: ╟────────────────────────────────────────────────────────────────────────────────────────
03-25 11:40:55.568 3256-3256/com.tomorrow.androidtest2 D/zwm: ║ , height: 112
03-25 11:40:55.568 3256-3256/com.tomorrow.androidtest2 D/zwm: ╚════════════════════════════════════════════════════════════════════════════════════════
03-25 11:40:55.568 3256-3256/com.tomorrow.androidtest2 D/zwm: ╔════════════════════════════════════════════════════════════════════════════════════════
03-25 11:40:55.568 3256-3256/com.tomorrow.androidtest2 D/zwm: ║ Thread: main
03-25 11:40:55.568 3256-3256/com.tomorrow.androidtest2 D/zwm: ╟────────────────────────────────────────────────────────────────────────────────────────
03-25 11:40:55.568 3256-3256/com.tomorrow.androidtest2 D/zwm: ║ MainActivity.onWindowFocusChanged (MainActivity.java:63)
03-25 11:40:55.568 3256-3256/com.tomorrow.androidtest2 D/zwm: ║ MainActivity.getParams (MainActivity.java:50)
03-25 11:40:55.568 3256-3256/com.tomorrow.androidtest2 D/zwm: ╟────────────────────────────────────────────────────────────────────────────────────────
03-25 11:40:55.568 3256-3256/com.tomorrow.androidtest2 D/zwm: ║ Activity , width: 720, height: 1120
03-25 11:40:55.568 3256-3256/com.tomorrow.androidtest2 D/zwm: ╚════════════════════════════════════════════════════════════════════════════════════════
03-25 11:40:55.568 3256-3256/com.tomorrow.androidtest2 D/zwm: ╔════════════════════════════════════════════════════════════════════════════════════════
03-25 11:40:55.568 3256-3256/com.tomorrow.androidtest2 D/zwm: ║ Thread: main
03-25 11:40:55.568 3256-3256/com.tomorrow.androidtest2 D/zwm: ╟────────────────────────────────────────────────────────────────────────────────────────
03-25 11:40:55.568 3256-3256/com.tomorrow.androidtest2 D/zwm: ║ MainActivity.onWindowFocusChanged (MainActivity.java:63)
03-25 11:40:55.568 3256-3256/com.tomorrow.androidtest2 D/zwm: ║ MainActivity.getParams (MainActivity.java:53)
03-25 11:40:55.568 3256-3256/com.tomorrow.androidtest2 D/zwm: ╟────────────────────────────────────────────────────────────────────────────────────────
03-25 11:40:55.568 3256-3256/com.tomorrow.androidtest2 D/zwm: ║ DecorView , width: 720, height: 1280
03-25 11:40:55.568 3256-3256/com.tomorrow.androidtest2 D/zwm: ╚════════════════════════════════════════════════════════════════════════════════════════
03-25 11:40:55.568 3256-3256/com.tomorrow.androidtest2 D/zwm: ╔════════════════════════════════════════════════════════════════════════════════════════
03-25 11:40:55.568 3256-3256/com.tomorrow.androidtest2 D/zwm: ║ Thread: main
03-25 11:40:55.568 3256-3256/com.tomorrow.androidtest2 D/zwm: ╟────────────────────────────────────────────────────────────────────────────────────────
03-25 11:40:55.568 3256-3256/com.tomorrow.androidtest2 D/zwm: ║ MainActivity.onWindowFocusChanged (MainActivity.java:63)
03-25 11:40:55.568 3256-3256/com.tomorrow.androidtest2 D/zwm: ║ MainActivity.getParams (MainActivity.java:56)
03-25 11:40:55.568 3256-3256/com.tomorrow.androidtest2 D/zwm: ╟────────────────────────────────────────────────────────────────────────────────────────
03-25 11:40:55.568 3256-3256/com.tomorrow.androidtest2 D/zwm: ║ DecorView content , width: 720, height: 1120
03-25 11:40:55.578 3256-3256/com.tomorrow.androidtest2 D/zwm: ╚════════════════════════════════════════════════════════════════════════════════════════
二、DecorViewのクリックイベントの配布過程
点击事件到达DecorView以后,会调用ViewGroup的dispatchTouchEvent方法,然后按照事件分发机制去分发事件。 ViewGroup#dispatchTouchEvent:
final boolean intercepted;
if(actionMasked == MotionEvent.ACTION_DOWN
|| mFirstTouchTarget != null) { //ACTION_DOWN View ACTION_DOWN
final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
if(!disallowIntercept ) {
intercepted = onInterceptTouchEvent(ev);
ev.setAction(action);
} else {
intercepted = false;
}
} else {
intercepted = true; //ViewGroup ACTION_DOWN,
}
ViewGroupでは、現在のイベントをブロックするかどうかを判断します.1.ACTION_DOWN、すなわち現在配布ACTION_DOWNイベントの場合、ViewGroupはonInterceptTouchEventメソッドを呼び出して現在のイベントをブロックする必要があるかどうかを判断します.2.mFirstTouchTarget != null、すなわちサブエレメントがACTION_の処理に成功したDOWNイベントの場合、現在フラグビットFLAG_が設定されていない場合DISALLOW_INTERCEPT(ブロックを許可しない、すなわちブロックを許可しない)では、onInterceptTouchEventメソッドが呼び出され、現在のイベントをブロックする必要があるかどうかを判断します.
そのうちFLAG_DISALLOW_INTERCEPTフラグビットは、ViewGroup#r e q u s t D o l l o w InterceptTouchEventメソッドによって設定され、一般的にサブViewで使用されますが、いったん設定すると、ViewGroupはACTION_をブロックできませんDOWN以外の他のクリックイベント(ACTION_DOWNイベントであれば、ViewGroupはFLAG_DISALLOW_INTERCEPTフラグビットをリセットします).
結論:ViewGroupがイベントをブロックすることを決定すると、後続のクリックイベントはデフォルトで処理され、onInterceptTouchEventメソッドは呼び出されません.
ViewGroupがイベントをブロックしない場合、イベントはサブViewに配布されて処理されます.まず、View Groupのすべてのサブエレメントを巡回し、クリックイベントを受信できるサブエレメントにイベントを渡し、サブエレメントのdispatchTouchEventメソッドを呼び出して、イベントの配布を完了します.すべてのサブエレメントを巡回した後にイベントが適切に処理されていない場合、これは2つのケースを含む:1.View Groupにはサブ要素がありません.2.サブエレメントはクリックイベントを処理したが、dispatchTouchEventでfalseが返された.これは、一般に、サブエレメントがonTouchEventでfalseを返したためである.どちらの場合も、ViewGroupはクリックイベントを自分で処理します.
三、ビューのクリックイベントに対する処理過程 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)) { //OnTouchListener#onTouch
return = true;
}
if(!result && onTouchEvent(event)) { //onTouchEvent
return true;
}
}
...
return result;
}
Viewはクリックイベントに対する処理が簡単です.View(ここではView Groupを含まない)は個別の要素であり、サブ要素がないためイベントを下に渡すことができないため、自分でイベントを処理するしかありません.まず、OnTouchListenerが設定されているかどうかを判断し、OnTouchListenerのonTouchメソッドがtrueを返すと、onTouchEventは呼び出されず、OnTouchListenerの優先度がonTouchEventより高いことがわかり、外部でドットインパクトイベントを処理しやすいという利点がある.次にonTouchEvent法を解析し,ViewCLICKABLEとLONG_CLICKABLEにはtrueがあり、DISABLE状態であるかどうかにかかわらず、onTouchEventメソッドがtrueを返すイベントが消費されます.そして、ACTION_UPイベントが発生するとperformClickメソッドがトリガーされ、ViewOnClickListenerが設定されている場合、performClickメソッドの内部でonClickメソッドが呼び出されます.ViewのLONG_CLICKABLE属性のデフォルトはfalseであり、CLICKABLE属性がfalseであるかどうかは具体的なViewと関係があり、正確にはクリック可能なViewはCLICKABLEがtrueであり、クリックできないViewはCLICKABLEがfalseであり、例えばButtonはクリック可能であり、TextViewはクリックできない.setClickableとsetLongClickableでそれぞれViewのCLICKABLEとLONG_を変更できますCLICKABLEプロパティ.またsetOnClickListenerではViewのCLICKABLEが自動的にtrueに設定され、setOnLongClickListenerではViewのLONG_が自動的に設定されますCLICKABLEをtrueに設定します.