向那些曾經沒法跨越的鴻溝敬上----吾王已至
git
1.有三東西挺長的,又長得挺像,看着晃眼且心煩,文中如下簡寫爲:
|-- 分發 dispatchTouchEvent = d16t
|-- 截斷 onInterceptTouchEvent = o19t
|-- 消費 onTouchEvent = o10t
2.事件分發機制的參與者與各自擁有的回調方法:
|-- 灰色 Activity: o10t d16t
|-- 紫色 ViewGroup: o10t d16t o19t
|-- 橙色 單體View: o10t d16t
3.MotionEvent的幾種常見時間
|-- MotionEvent.ACTION_DOWN = 0; 按下
|-- MotionEvent.ACTION_UP = 1; 擡起
|-- MotionEvent.ACTION_MOVE=2; 移動
|-- MotionEvent.ACTION_CANCEL=3; 取消
複製代碼
下面表示:觸點在Activity上,
按下事件(即0)觸發了 d16t(即dispatchTouchEvent)
而後按下事件(即0)觸發了 o10t( 即onTouchEvent)
擡起事件(即1)觸發了 d16t(即dispatchTouchEvent)
而後擡起事件(即1)觸發了 o10t( 即onTouchEvent) ---我想這樣應該表述的淋漓盡致了
複製代碼
佈局樹以下:如今自定義一個單體View,一個ViewGroupgithub
/**
* 做者:張風捷特烈<br/>
* 時間:2019/2/21/021:9:11<br/>
* 郵箱:1981462002@qq.com<br/>
* 說明:事件測試Activity
*/
public class EventActivity extends AppCompatActivity {
private static final String TAG = "EventTest";
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_event_test);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.e(TAG, "onTouchEvent:--" + event.getAction() + " --卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍");
return super.onTouchEvent(event);
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
Log.e(TAG, "dispatchTouchEvent:--" + ev.getAction() + "--卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍");
return super.dispatchTouchEvent(ev);
}
}
/**
* 做者:張風捷特烈<br/>
* 時間:2019/2/21/021:9:06<br/>
* 郵箱:1981462002@qq.com<br/>
* 說明:事件測試View單體
*/
public class SonView extends View {
private static final String TAG = "EventTest";
public SonView(Context context) {
super(context);
}
public SonView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
setBackgroundColor(0xffE58F46);
}
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
Log.e(TAG, "SonView--dispatchTouchEvent:" + event.getAction() + "-- ††††††††††††††††††††††††††††††††††††††††");
return super.dispatchTouchEvent(event);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.e(TAG, "SonView--onTouchEvent:--" + event.getAction() + "-- ††††††††††††††††††††††††††††††††††††††††");
return super.onTouchEvent(event);
}
}
/**
* 做者:張風捷特烈<br/>
* 時間:2019/2/21/021:9:06<br/>
* 郵箱:1981462002@qq.com<br/>
* 說明:事件測試ViewGroup
*/
public class FatherViewGroup extends FrameLayout {
private static final String TAG = "EventTest";
public FatherViewGroup(Context context) {
super(context);
}
public FatherViewGroup(Context context, AttributeSet attrs) {
super(context, attrs);
setBackgroundColor(0xff9869B7);
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
Log.e(TAG, "FatherViewGroup--dispatchTouchEvent:--" + ev.getAction() + " --☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯ ");
return super.dispatchTouchEvent(ev);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
Log.e(TAG, "FatherViewGroup--onInterceptTouchEvent:--" + ev.getAction() + " -- ☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯ ");
return super.onInterceptTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.e(TAG, "FatherViewGroup--onTouchEvent:--" + event.getAction() + " --☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯ ");
return super.onTouchEvent(event);
}
}
複製代碼
圖例部分說過了,這裏不廢話了編程
2019-02-21 10:13:25.773 : dispatchTouchEvent:--0--卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍
2019-02-21 10:13:25.775 : onTouchEvent:--0 --卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍
2019-02-21 10:13:25.805 : dispatchTouchEvent:--1--卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍
2019-02-21 10:13:25.805 : onTouchEvent:--1 --卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍
複製代碼
在Activity的
d16t
時,以後,事件到了ViewGroup裏bash
2019-02-21 10:36:34.040 : dispatchTouchEvent:--0--卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍
2019-02-21 10:36:34.040 : FatherViewGroup--dispatchTouchEvent:--0 --☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯
2019-02-21 10:36:34.040 : FatherViewGroup--onInterceptTouchEvent:--0 -- ☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯
2019-02-21 10:36:34.041 : FatherViewGroup--onTouchEvent:--0 --☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯
2019-02-21 10:36:34.041 : onTouchEvent:--0 --卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍
2019-02-21 10:36:34.064 : dispatchTouchEvent:--1--卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍
2019-02-21 10:36:34.064 : onTouchEvent:--1 --卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍
複製代碼
在ViewGroup的
o18t
時,以後,事件到了View裏,
這裏感受像是...一根鏈條。萬一哪塊掉鏈子了會怎麼樣?微信
2019-02-21 10:46:15.721 : dispatchTouchEvent:--0--卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍
2019-02-21 10:46:15.722 : FatherViewGroup--dispatchTouchEvent:--0 --☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯
2019-02-21 10:46:15.722 : FatherViewGroup--onInterceptTouchEvent:--0 -- ☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯
2019-02-21 10:46:15.722 : SonView--dispatchTouchEvent:0-- ††††††††††††††††††††††††††††††††††††††††
2019-02-21 10:46:15.723 : SonView--onTouchEvent:--0-- ††††††††††††††††††††††††††††††††††††††††
2019-02-21 10:46:15.724 : FatherViewGroup--onTouchEvent:--0 --☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯
2019-02-21 10:46:15.726 : onTouchEvent:--0 --卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍
2019-02-21 10:46:15.772 : dispatchTouchEvent:--1--卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍
2019-02-21 10:46:15.773 : onTouchEvent:--1 --卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍
複製代碼
d16t
即dispatchTouchEvent
:口號:
寧爲玉碎不爲瓦全,我得不到的,你也別想獲得!
dom
d16t
掉鏈子:寧爲玉碎不爲瓦全
Activity說:
哥不爽了,事件不給大家玩!d16t 返回 false.
說完把event扔了
這鏈子一斷...就算點擊View單體,事件也傳不下去了。ide
---->[EventActivity#dispatchTouchEvent]------------------------------
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
Log.e(TAG, "dispatchTouchEvent:--" + ev.getAction() + "--卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍");
return false;
}
2019-02-21 11:01:19.109 29054-29054/com.toly1994.analyzer E/EventTest: dispatchTouchEvent:--0--卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍
2019-02-21 11:01:19.159 29054-29054/com.toly1994.analyzer E/EventTest: dispatchTouchEvent:--1--卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍
複製代碼
d16t
掉鏈子如今回到
初始狀況
,Activity把事件給了ViewGroup玩,ViewGroup說:爺也不爽了!
而後一丟
測試中看來:在ViewGroup的d16t
返回false以後會回調Activity的o10T
佈局
---->[FatherViewGroup#dispatchTouchEvent]--------------------
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
Log.e(TAG, "FatherViewGroup--dispatchTouchEvent:--" + ev.getAction() + " --☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯ ");
return false;
}
2019-02-21 11:27:07.487: dispatchTouchEvent:--0--卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍
2019-02-21 11:27:07.488: FatherViewGroup--dispatchTouchEvent:--0 --☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯
2019-02-21 11:27:07.489: onTouchEvent:--0 --卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍
2019-02-21 11:27:07.538: dispatchTouchEvent:--1--卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍
2019-02-21 11:27:07.538: onTouchEvent:--1 --卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍
複製代碼
d16t
掉鏈子如今回到
初始狀況
post
---->[SonView#dispatchTouchEvent]--------------------
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
Log.e(TAG, "SonView--dispatchTouchEvent:" + event.getAction() + "-- ††††††††††††††††††††††††††††††††††††††††");
return false;
}
2019-02-21 11:28:32.080: dispatchTouchEvent:--0--卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍
2019-02-21 11:28:32.081: FatherViewGroup--dispatchTouchEvent:--0 --☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯
2019-02-21 11:28:32.081: FatherViewGroup--onInterceptTouchEvent:--0 -- ☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯
2019-02-21 11:28:32.081: SonView--dispatchTouchEvent:0-- ††††††††††††††††††††††††††††††††††††††††
2019-02-21 11:28:32.082: FatherViewGroup--onTouchEvent:--0 --☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯
2019-02-21 11:28:32.083: onTouchEvent:--0 --卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍
2019-02-21 11:28:32.121: dispatchTouchEvent:--1--卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍
2019-02-21 11:28:32.121: onTouchEvent:--1 --卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍
複製代碼
d16t
從測試上看到的結論:測試
dispatchTouchEvent的做用在於分發事件,源頭出發不走,後面都白忙活
只要發走了,後面哪裏斷掉,就會觸發上一級的 o10t
複製代碼
o18t
對事件的影響做爲ViewGroup獨有的方法,onInterceptTouchEvent能夠決定事件是否打斷
---->[FatherViewGroup#onInterceptTouchEvent]--------------------
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
Log.e(TAG, "FatherViewGroup--onInterceptTouchEvent:--" + ev.getAction() + " -- ☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯ ");
return true;
}
2019-02-21 11:54:46.648 : dispatchTouchEvent:--0--卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍
2019-02-21 11:54:46.649 : FatherViewGroup--dispatchTouchEvent:--0 --☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯
2019-02-21 11:54:46.649 : FatherViewGroup--onInterceptTouchEvent:--0 -- ☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯
2019-02-21 11:54:46.650 : FatherViewGroup--onTouchEvent:--0 --☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯
2019-02-21 11:54:46.652 : onTouchEvent:--0 --卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍
2019-02-21 11:54:46.697 : dispatchTouchEvent:--1--卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍
2019-02-21 11:54:46.697 : onTouchEvent:--1 --卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍
複製代碼
o10t
掉鏈子默認狀況下由最頂層消費事件,這裏只有讓當ViewGroup消費事件,事件就不會往下傳遞了
---->[FatherViewGroup#onTouchEvent]--------------------
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.e(TAG, "FatherViewGroup--onTouchEvent:--" + event.getAction() + " --☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯ ");
return true;
}
2019-02-21 12:09:06.605 11221-11221/com.toly1994.analyzer E/EventTest: dispatchTouchEvent:--0--卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍
2019-02-21 12:09:06.606 11221-11221/com.toly1994.analyzer E/EventTest: FatherViewGroup--dispatchTouchEvent:--0 --☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯
2019-02-21 12:09:06.606 11221-11221/com.toly1994.analyzer E/EventTest: FatherViewGroup--onInterceptTouchEvent:--0 -- ☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯
2019-02-21 12:09:06.606 11221-11221/com.toly1994.analyzer E/EventTest: SonView--dispatchTouchEvent:0-- ††††††††††††††††††††††††††††††††††††††††
2019-02-21 12:09:06.607 11221-11221/com.toly1994.analyzer E/EventTest: SonView--onTouchEvent:--0-- ††††††††††††††††††††††††††††††††††††††††
2019-02-21 12:09:06.607 11221-11221/com.toly1994.analyzer E/EventTest: FatherViewGroup--onTouchEvent:--0 --☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯
2019-02-21 12:09:06.621 11221-11221/com.toly1994.analyzer E/EventTest: dispatchTouchEvent:--1--卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍
2019-02-21 12:09:06.621 11221-11221/com.toly1994.analyzer E/EventTest: FatherViewGroup--dispatchTouchEvent:--1 --☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯
2019-02-21 12:09:06.622 11221-11221/com.toly1994.analyzer E/EventTest: FatherViewGroup--onTouchEvent:--1 --☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯
複製代碼
---->[SonView#onTouchEvent]--------------------
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.e(TAG, "SonView--onTouchEvent:--" + event.getAction() + "-- ††††††††††††††††††††††††††††††††††††††††");
return true;
}
2019-02-21 12:10:05.682 : dispatchTouchEvent:--0--卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍
2019-02-21 12:10:05.683 : FatherViewGroup--dispatchTouchEvent:--0 --☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯
2019-02-21 12:10:05.683 : FatherViewGroup--onInterceptTouchEvent:--0 -- ☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯
2019-02-21 12:10:05.683 : SonView--dispatchTouchEvent:0-- ††††††††††††††††††††††††††††††††††††††††
2019-02-21 12:10:05.683 : SonView--onTouchEvent:--0-- ††††††††††††††††††††††††††††††††††††††††
2019-02-21 12:10:05.724 : dispatchTouchEvent:--1--卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍
2019-02-21 12:10:05.724 : FatherViewGroup--dispatchTouchEvent:--1 --☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯
2019-02-21 12:10:05.724 : FatherViewGroup--onInterceptTouchEvent:--1 -- ☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯
2019-02-21 12:10:05.724 : SonView--dispatchTouchEvent:1-- ††††††††††††††††††††††††††††††††††††††††
2019-02-21 12:10:05.724 : SonView--onTouchEvent:--1-- ††††††††††††††††††††††††††††††††††††††††
複製代碼
o10t
從測試上看到的結論:
onTouchEvent的做用在於消費事件,消費以後不會再往下傳遞
這裏仍是想強調一下[d16t]和[o10t]的區別,一者消費,一者分發
因爲消費在分發以前,消費是不會阻礙分發的,但分發會影響消費
老婆(Activity):給你100塊當作一月生活費,這叫分發dispatchTouchEvent,生活費至關MotionEvent
你(ViewGroup):拿到這100塊,能夠決定是否把這100塊給兒子(View)當生活費(dispatchTouchEvent)
當決定給了,可是中途還能夠經過onInterceptTouchEvent打斷給的念頭...
|--兒子拿到錢花了,錢就沒了
|--兒子拿到錢不花,就被老爸沒收了,老爸花了,錢就沒了
|--兒子拿到錢不花,就被老爸沒收了,老爸沒花,就被老婆沒收了,老婆花了,錢就沒了
差很少就是這個理,默認下你和兒子都是不敢花的,但都在手裏過了一遍
後面人有沒有得花,首先要看老婆給不給,不給,後面就沒戲了...
複製代碼
上面若是理清楚,使用方面應該就沒問題了
dispatchTouchEvent
---->[Activity#dispatchTouchEvent]----------------------------
public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
onUserInteraction();
}
--->if (getWindow().superDispatchTouchEvent(ev)) {
return true;
}
--->return onTouchEvent(ev);
}
|---這裏能夠很清楚的看清Activity的onTouchEvent回調的時機即
getWindow().superDispatchTouchEvent(ev) 返回false時觸發
|---getWindow這裏就不廢話了,前面都說過,是PhoneWindow對象,直接進
---->[PhoneWindow#dispatchTouchEvent]----------------------------
|--- 調用的是mDecor的方法
@Override
public boolean superDispatchTouchEvent(MotionEvent event) {
return mDecor.superDispatchTouchEvent(event);
}
---->[PhoneWindow$DecorView#superDispatchTouchEvent]----------------------------
|--- 這皮球踢得...mDecor是DecorView對象,繼承自FrameLayout,在上一輩即是ViewGroup
|--- 因此這樣看來,Activity的dispatchTouchEvent的本質也是ViewGroup觸發的
public boolean superDispatchTouchEvent(MotionEvent event) {
return super.dispatchTouchEvent(event);
}
---->[ViewGroup#superDispatchTouchEvent]----------------------------
|---這個方法大概100多行
/**
* {@inheritDoc}
*/
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
...
---> final boolean intercepted;//是否打斷
if (actionMasked == MotionEvent.ACTION_DOWN
|| mFirstTouchTarget != null) {
final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
if (!disallowIntercept) {
---> intercepted = onInterceptTouchEvent(ev);//觸發onInterceptTouchEvent,默認false
ev.setAction(action); // restore action in case it was changed
} else {
intercepted = false;
}
} else {
intercepted = true;
}
if (intercepted || mFirstTouchTarget != null) {
ev.setTargetAccessibilityFocus(false);
}
// Check for cancelation.
final boolean canceled = resetCancelNextUpFlag(this)
|| actionMasked == MotionEvent.ACTION_CANCEL;
final boolean split = (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) != 0;
TouchTarget newTouchTarget = null;
boolean alreadyDispatchedToNewTouchTarget = false;
---> if (!canceled && !intercepted) {//未被取消而且未被打斷
...
if (actionMasked == MotionEvent.ACTION_DOWN
|| (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)
|| actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
...
final int childrenCount = mChildrenCount;
if (newTouchTarget == null && childrenCount != 0) {
...
//這裏接了一下mChildren,mChildren的初始化及添加操做稍後分析
final View[] children = mChildren;
---> for (int i = childrenCount - 1; i >= 0; i--) {//這裏開始遍歷全部的孩子
final int childIndex = customOrder
? getChildDrawingOrder(childrenCount, i) : i;
final View child = (preorderedList == null)
? children[childIndex] : preorderedList.get(childIndex);
...
newTouchTarget = getTouchTarget(child);
...
resetCancelNextUpFlag(child);
//dispatchTransformedTouchEvent會觸發子View的d16t方法
---> if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
// Child wants to receive touch within its bounds.---孩子想要在他的範圍內接受觸摸
...
---> newTouchTarget = addTouchTarget(child, idBitsToAssign);
alreadyDispatchedToNewTouchTarget = true;
break;
}
...
// Dispatch to touch targets. ---- 分發到多個觸摸點?...
if (mFirstTouchTarget == null) {//mFirstTouchTarget在addTouchTarget方法中被賦值
---> handled = dispatchTransformedTouchEvent(ev, canceled, null,
TouchTarget.ALL_POINTER_IDS);
} else {
TouchTarget predecessor = null;
TouchTarget target = mFirstTouchTarget;
//下面遍歷target的鏈表,取出鏈表中的View執行dispatchTransformedTouchEvent
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;
}
if (cancelChild) {
if (predecessor == null) {
mFirstTouchTarget = next;
} else {
predecessor.next = next;
}
target.recycle();
target = next;
continue;
}
}
predecessor = target;
target = next;
}
}
....
return handled;
}
---->[ViewGroup#dispatchTransformedTouchEvent]----------------------------
|--總的來講就是觸發super或是child的d16t方法,ViewGroup的super是誰?
|--答:View 。child 若是不爲空走本身的d16t,若是child仍是ViewGroup,就再走一圈上面的
private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
View child, int desiredPointerIdBits) {
...
if (cancel || oldAction == MotionEvent.ACTION_CANCEL) {//取消時
event.setAction(MotionEvent.ACTION_CANCEL);
if (child == null) {
---> handled = super.dispatchTouchEvent(event);//孩子爲空觸發super的d16t,
} else {
handled = child.dispatchTouchEvent(event);//孩子不爲空,觸發d16t
}
event.setAction(oldAction);
return handled;
}
if (newPointerIdBits == oldPointerIdBits) {
if (child == null || child.hasIdentityMatrix()) {
if (child == null) {
---> handled = super.dispatchTouchEvent(event);
} else {
...
---> handled = child.dispatchTouchEvent(event);
}
return handled;
}
transformedEvent = MotionEvent.obtain(event);
} else {
transformedEvent = event.split(newPointerIdBits);
}
if (child == null) {
---> handled = super.dispatchTouchEvent(transformedEvent);
} else {
...
---> handled = child.dispatchTouchEvent(transformedEvent);
}
transformedEvent.recycle();
return handled;
}
---->[ViewGroup#addTouchTarget]----------------------------
private TouchTarget addTouchTarget(View child, int pointerIdBits) {
TouchTarget target = TouchTarget.obtain(child, pointerIdBits);
target.next = mFirstTouchTarget;
mFirstTouchTarget = target;
return target;
}
---->[TouchTarget]----------------------------
|---描述觸摸的View以及手指的id們
|---TouchTarget是ViewGroup的一個內部類,看樣子是一個單鏈表
|---有一個next的TouchTarget字段,鏈表承載數據類型是View
private static final class TouchTarget {
...
public View child;
public TouchTarget next;
...
}
---->[ViewGroup$TouchTarget#obtain]----------------------------
|-- 這裏很明顯是第一個元素出鏈表
public static TouchTarget obtain(View child, int pointerIdBits) {
final TouchTarget target;
synchronized (sRecycleLock) {
if (sRecycleBin == null) {
target = new TouchTarget();
} else {
target = sRecycleBin;
sRecycleBin = target.next;
sRecycledCount--;
target.next = null;
}
}
target.child = child;
target.pointerIdBits = pointerIdBits;
return target;
}
---->[ViewGroup#onInterceptTouchEvent]---------------------------
public boolean onInterceptTouchEvent(MotionEvent ev) {
return false;
}
複製代碼
dispatchTouchEvent
看這個鬆了一口氣...
public boolean dispatchTouchEvent(MotionEvent event) {
if (event.isTargetAccessibilityFocus()) {
if (!isAccessibilityFocusedViewOrHost()) {
return false;
}
event.setTargetAccessibilityFocus(false);
}
boolean result = false;
if (mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onTouchEvent(event, 0);
}
final int actionMasked = event.getActionMasked();
if (actionMasked == MotionEvent.ACTION_DOWN) {
stopNestedScroll();
}
if (onFilterTouchEventForSecurity(event)) {
//注意,這四個條件知足,直接返回true,就不會觸發onTouchEvent了
//而且會觸發mOnTouchListener的onTouch回調
ListenerInfo li = mListenerInfo;
if (li != null && li.mOnTouchListener != null
&& (mViewFlags & ENABLED_MASK) == ENABLED
---> && li.mOnTouchListener.onTouch(this, event)) {
result = true;
}
---> if (!result && onTouchEvent(event)) {//這裏觸發了View的onTouchEvent!!!,感動...
result = true;
}
}
if (!result && mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onUnhandledEvent(event, 0);
}
if (actionMasked == MotionEvent.ACTION_UP ||
actionMasked == MotionEvent.ACTION_CANCEL ||
(actionMasked == MotionEvent.ACTION_DOWN && !result)) {
stopNestedScroll();
}
return result;
}
複製代碼
onInterceptTouchEvent
也許你沒注意,剛纔已經被消滅了...它只是用來控制的boolean而已
---->[ViewGroup#onInterceptTouchEvent]---------------------------
public boolean onInterceptTouchEvent(MotionEvent ev) {
return false;
}
複製代碼
onTouchEvent
---->[Activity#onTouchEvent]-----------------------
|--少得可憐,基本上就是false了
public boolean onTouchEvent(MotionEvent event) {
if (mWindow.shouldCloseOnTouch(this, event)) {
finish();
return true;
}
return false;
}
---->[ViewGroup#onTouchEvent]-----------------------
|--咦,個人onTouchEvent呢?居然沒有!!!!
|--這讓我挺意外,也就是ViewGroup徹底使用View的onTouchEvent
---->[View#onTouchEvent]-----------------------
|--就這個有點說頭...這裏追蹤一下點擊事件
public boolean onTouchEvent(MotionEvent event) {
final float x = event.getX();
final float y = event.getY();
final int viewFlags = mViewFlags;
final int action = event.getAction();
if ((viewFlags & ENABLED_MASK) == DISABLED) {//表示不可用
if (action == MotionEvent.ACTION_UP && (mPrivateFlags & PFLAG_PRESSED) != 0) {
setPressed(false);
}
return (((viewFlags & CLICKABLE) == CLICKABLE //直接滾回
|| (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)
|| (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE);
}
if (mTouchDelegate != null) {//有了觸摸代理
if (mTouchDelegate.onTouchEvent(event)) {//執行代理人的onTouchEvent
return true;////直接滾回
}
}
if (((viewFlags & CLICKABLE) == CLICKABLE ||
(viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) ||
(viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE) {//知足上面一堆狀況
switch (action) {//咱們最熟悉的 switch (action)
case MotionEvent.ACTION_UP://擡起時
boolean prepressed = (mPrivateFlags & PFLAG_PREPRESSED) != 0;
if ((mPrivateFlags & PFLAG_PRESSED) != 0 || prepressed) {
boolean focusTaken = false;
...
if (prepressed) {//被按下
setPressed(true, x, y);//標true
}
if (!mHasPerformedLongPress && !mIgnoreNextUpEvent) {
// This is a tap, so remove the longpress check--這是一個短點擊,因此去掉長按檢查
removeLongPressCallback();
if (!focusTaken) {
if (mPerformClick == null) {
mPerformClick = new PerformClick();
}
if (!post(mPerformClick)) {
performClick();//執行點擊
}
}
}
...
break;
case MotionEvent.ACTION_DOWN:
...
break;
case MotionEvent.ACTION_CANCEL:
...
break;
case MotionEvent.ACTION_MOVE:
...
break;
}
return true;
}
return false;
}
---->[View#performClick]----------------------
public boolean performClick() {
final boolean result;
final ListenerInfo li = mListenerInfo;
if (li != null && li.mOnClickListener != null) {
playSoundEffect(SoundEffectConstants.CLICK);
li.mOnClickListener.onClick(this);
result = true;
} else {
result = false;
}
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
return result;
}
複製代碼
---->[ViewGroup對子View的添加]---------------------------
private View[] mChildren;
mChildren = new View[ARRAY_INITIAL_CAPACITY];//默認12
---->[ViewGroup#addInArray]---------------------------
private void addInArray(View child, int index) {
View[] children = mChildren;
final int count = mChildrenCount;
final int size = children.length;
if (index == count) {
if (size == count) {
mChildren = new View[size + ARRAY_CAPACITY_INCREMENT];
System.arraycopy(children, 0, mChildren, 0, size);
children = mChildren;
}
---> children[mChildrenCount++] = child;
} else if (index < count) {
if (size == count) {
mChildren = new View[size + ARRAY_CAPACITY_INCREMENT];
System.arraycopy(children, 0, mChildren, 0, index);
System.arraycopy(children, index, mChildren, index + 1, count - index);
children = mChildren;
} else {
System.arraycopy(children, index, children, index + 1, count - index);
}
---> children[index] = child;
mChildrenCount++;
if (mLastTouchDownIndex >= index) {
mLastTouchDownIndex++;
}
} else {
throw new IndexOutOfBoundsException("index=" + index + " count=" + count);
}
}
|--- 關於ViewGroup添加View,追蹤了一下:
addView(一參)-->addView(兩參)-->addView(三參)-->addViewInner-->addInArray
複製代碼
總的來講源碼看下來,感受view事件分發機制也並不像我想像中的那麼難
在自定義View中至多也就是ViewGroup+子View的觸摸事件協調,Activity通常不參和
Activity的事件分發實質上是DecorView的事件分發,因此都是View家的,Activity打了波醬油
最後我想強調一下d10t
和o18t
返回false時的不一樣點,這也是我之前比較迷惑的:見下圖
o18t
來控制事件響應行爲若是父View左滑了,那麼就不截斷子View的滑動(孩子消費)
若是父View右滑了,那麼就截斷子View的滑動(本身消費)
/**
* 做者:張風捷特烈<br/>
* 時間:2019/2/21/021:9:06<br/>
* 郵箱:1981462002@qq.com<br/>
* 說明:事件測試ViewGroup
* 左滑動 ViewGroup 響應
* 右滑動 View單體 響應
*/
public class FatherViewGroup extends FrameLayout {
private static final String TAG = "EventTest";
boolean isLeft = false;
public FatherViewGroup(Context context) {
super(context);
}
public FatherViewGroup(Context context, AttributeSet attrs) {
super(context, attrs);
setBackgroundColor(0xff9869B7);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
return isLeft;
}
float x;
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
x = event.getX();
break;
case MotionEvent.ACTION_MOVE:
float curX = event.getX();
float dx = curX - this.x;
isLeft = dx < 0;
x = curX;
setBackgroundColor(ColUtils.randomColor());
break;
}
return true;
}
}
/**
* 做者:張風捷特烈<br/>
* 時間:2019/2/21/021:9:06<br/>
* 郵箱:1981462002@qq.com<br/>
* 說明:事件測試View單體
*/
public class SonView extends View {
private static final String TAG = "EventTest";
public SonView(Context context) {
super(context);
}
public SonView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
setBackgroundColor(0xffE58F46);
}
float x;
@Override
public boolean onTouchEvent(MotionEvent event) {
setBackgroundColor(ColUtils.randomColor());
return true;
}
}
複製代碼
項目源碼 | 日期 | 附錄 |
---|---|---|
V0.1-- | 2018-2-21 | 無 |
發佈名:
Android事件分發機制[源碼級]
捷文連接:juejin.im/post/5c6e5f…
筆名 | 微信 | |
---|---|---|
張風捷特烈 | 1981462002 | zdl1994328 |
個人github:github.com/toly1994328
個人簡書:www.jianshu.com/u/e4e52c116…
個人掘金:juejin.im/user/5b42c0…
我的網站:www.toly1994.com
1----本文由張風捷特烈原創,轉載請註明
2----歡迎廣大編程愛好者共同交流
3----我的能力有限,若有不正之處歡迎你們批評指證,一定虛心改正
4----看到這裏,我在此感謝你的喜歡與支持