Android 事件分發(fā)一直都是一個難點刻坊,讓人很模糊好爬,感覺自己知道點,但又不是很清楚奶卓。最重要的是我知道了理論一疯,怎么沒感覺在實際開發(fā)中用到呢撼玄?今天決定來好好研究一下這個事件分發(fā)機制夺姑,如有不正確的地方,請多多指教掌猛。
首先看上圖盏浙,我們要知道這個層次關(guān)系,你可以把每一層理解為一張紙放在桌子上荔茬,那就是Activity這張紙在最下面废膘,ViewGroup在Activity的上面,View在ViewGroup的上面慕蔚。
好了丐黄,理解完層次關(guān)系,我們可以點一下View內(nèi)任意一點孔飒。這時View是接受了一個事件(先不用管是什么事件灌闺,隨便你就理解為一個ACTION_DOWN事件)。到這里我要說下我原來的錯誤認(rèn)識坏瞄,做個反面教程桂对,讓大家更好的理解。我原來認(rèn)為鸠匀,View最先接收到到事件蕉斜,所以事件的傳遞肯定是View---ViewGroup---Activity這樣由外往里傳遞的。
其實不然缀棍,你可以這樣理解宅此。Activity、ViewGroup爬范、View是一個部門的三個同事诽凌,只不過Activity是部門主任,ViewGroup是主管坦敌,View是一個職員侣诵。
突然有一天View接受到一個任務(wù)(觸摸事件)痢法,那我們要想一下這個任務(wù)是怎么來的呢,無論這個任務(wù)是你自己部門內(nèi)部的杜顺,還是兄弟部門的協(xié)作任務(wù)财搁。只要給你分配任務(wù),那都要必須經(jīng)過你的主任和主管的同意躬络,因為他們是你的頂頭上司尖奔,而你是他們的干將。所以說這個任務(wù)也就是這個事件穷当,在傳到View之前提茁,早已經(jīng)經(jīng)過了你的領(lǐng)導(dǎo)的同意,也就是早就經(jīng)過了Activity和ViewGroup馁菜。如果領(lǐng)導(dǎo)不同意茴扁,或是半路領(lǐng)導(dǎo)就一句話的事幫你把事給解決了。那這個任務(wù)也就不會傳遞到View這來了汪疮。
總結(jié):雖然我們是對View發(fā)起了觸摸事件峭火,但是事件的傳遞開始于Activity。由Activiy----ViewGroup---View智嚷。
好了卖丸,到這里大體知道了事件傳遞流程了。但是我們還要知道幾個重要的方法盏道。如下圖所示稍浆,除了ViewGroup多了一個onInterceptTouchEvent方法外,他們都有dispatchTouchEvent和onTouchEvent猜嘱。
接下來我們看看這幾個方法都是什么意思衅枫,還是說下我原來的認(rèn)識誤區(qū)。我原來認(rèn)為dispatchTouchEvent是事件分發(fā)的方法泉坐,所以它返回true代表向下分發(fā)事件为鳄,false代表不向下分發(fā),如果return super. dispatchTouchEvent()腕让,我就認(rèn)為交給父類處理孤钦。
我當(dāng)時的認(rèn)知是不完全正確的,那說下這幾個方法分別返回 true或false或是return super.xxx到底是什么意思纯丸。
- dispatchTouchEvent
true:代表消費掉事件偏形,不再向下傳遞
false:代表不向下分發(fā)事件,不處理觉鼻,再把事件返還到上一層
super:表示繼續(xù)向下傳遞 - onInterceptTouchEvent
true: 代表攔截事件俊扭,不向下傳遞,交給自己的onTouchEvent處理
false和super:都表示不攔截坠陈,繼續(xù)向下傳遞事件 - onTouchEvent:
true:表示消費掉事件
false:Activity中表示丟棄事件不處理萨惑,而如果是在ViewGroup和View中表示自己不處理捐康,交給上級
super:交給上級處理
這里還要區(qū)分是誰的dispatchTouchEvent方法,Activity和View的這個方法同樣的返回值庸蔼,所做的操作是有區(qū)別的解总。詳細(xì)可以看下面的流程圖
是時候展示真正的技術(shù)了:我看了好多類似的事件分發(fā)流程圖,乍眼一看都有點蒙圈姐仅,覺得好凌亂復(fù)雜花枫。所以我也畫了一個圖,個人認(rèn)為還算清晰簡潔掏膏。
你不需要太仔細(xì)看這個圖劳翰,先簡單的看下。然后就是需要自己擼代碼驗證的時刻了馒疹,這時會加深你的理解佳簸。擼代碼回來再仔細(xì)看,你就會恍然大明白了行冰。
那怎樣寫代碼溺蕉,很簡單:
- 先自定義一個ViewGroup繼承LinearLayout伶丐,重寫需要的幾個方法
- 自定義View繼承TextView悼做,重寫需要的方法
- 在Activity的布局代碼中加入你自定義的ViewGroup,ViewGroup里面是的自定義的TextView哗魂。Activity中重寫需要的方法肛走。
//自定義ViewGroup
public class MyViewGroup extends LinearLayout {
public MyViewGroup(Context context) {
super(context);
}
public MyViewGroup(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public MyViewGroup(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
/*************下面是我們需要的方法*************/
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
Log.e("ViewGroup", "dispatchTouchEvent");
return super.dispatchTouchEvent(ev);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
Log.e("ViewGroup", "onInterceptTouchEvent");
return super.onInterceptTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.e("ViewGroup", "onTouchEvent");
return super.onTouchEvent(event);
}
}
//自定義TextView
public class MyTextView extends android.support.v7.widget.AppCompatTextView {
public MyTextView(Context context) {
super(context);
}
public MyTextView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public MyTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
/*************下面是我們需要的方法*************/
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
Log.e("View", "dispatchTouchEvent");
return super.dispatchTouchEvent(event);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.e("View", "onTouchEvent");
return super.onTouchEvent(event);
}
}
//Activity中的代碼
public class Main6Activity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main6);
}
// 這里只打印ACTION_DOWN的傳遞,不然會有移動和抬起的事件录别,在一起比較亂
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction()==MotionEvent.ACTION_DOWN){
Log.e("Activity", "dispatchTouchEvent");
return super.dispatchTouchEvent(ev);
}
return false;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.e("Activity", "onTouchEvent");
return super.onTouchEvent(event);
}
}
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.zsl.aa.Main6Activity">
<com.zsl.aa.MyViewGroup
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.zsl.aa.MyTextView
android:id="@+id/tv_view"
android:layout_width="120dp"
android:layout_height="130dp"
android:background="#ff00ff" />
</com.zsl.aa.MyViewGroup>
</LinearLayout>
ok朽色,到這里準(zhǔn)備工作就完事了,非常簡單的代碼组题。運行程序后葫男,點下View看看打印結(jié)果。然后再分不同的情況崔列,分別更改返回的值為true或false或是super.xxx梢褐。分別看看打印情況,你就會知道了傳遞流程了赵讯。