前言
懵懵懂懂搞了幾年安卓,對於事件分發這一塊,總是感覺沒有完全理解清楚。看了很多技術文章,自信感覺已經差不多了,然後一到面試,感覺總是說不出來。然後換了一套方式來理解,然後發現,事件分發也就是那麼回事。
舉例理解
舉一個例子來說明,某天你老闆看到個比較難的效果覺得很炫酷,然後就和產品經理隨口給派發了一個任務,產品經理把這個任務又給派發到組長。組長是個搞技術的,雖然自己有能力解決一些問題,但是發現自己有小弟,當仁不讓,任務給了程序員。然後程序員發現,整個技術部就我最小,我沒有小弟,然後只能苦逼的看任務。然後一看,發現自己搞不了。老老實實和組長說幹不了。然後組長也就一推四五六,就和產品經理說做不了,直接報告給老闆吧,這太難了。算了把,湊合湊合現在的也能用。老闆聽取到產品經理的建議以後放棄了任務。然後這次任務流程可以概括爲下面的圖。(ps:不經過任何攔截的流程)
上述圖是不經過任何攔截的流程,在事實上,也會有很多種可能:
- 老闆發佈的任務太難,產品經理一看,立即勸導老闆不要做這個,然後事件終止
- 組長是個比較會照顧人的或者說知道程序員是個新手幹不了,直接給完成了任務,然後反饋給產品經理,然後事件終止
- 程序員幹不了,又給組長,組長能力強給幹完了,然後反饋給產品經理,事件終止
- 程序員能力比較強直接給做了,然後報告給組長、組長又給產品經理然後然後事件終止了
- 程序員能力特強,完成以後給組長,組長一看直接給老闆,然後事件就到此終止了
上面描述了安卓的事件分發機制和處理機制,然後我們歸類一下方法
activity:
(1)dispatchTouchEvent 該方法用來分發事件,一般不會重寫這個方法 (給任務給組長)
(2)onTouchEvent 用來處理事件,常見重寫(當返回爲true: 勸導老闆)
viewGroup:
(1)dispatchTouchEvent 該方法用來分發事件,一般不會重寫這個方法 (給任務給程序員)
(2)onInterceptTouchEvent 攔截事件(想不想做)
(2)onTouchEvent 用來處理事件,常見重寫(當返回爲true: 自己做 | 接到程序員求助然後做)
view:
(1)dispatchTouchEvent 該方法用來分發事件,一般不會重寫這個方法 (判定自己是否能做)
(2)onTouchEvent 用來處理事件,常見重寫(當返回爲true: 自己做)
代碼驗證
分別編寫activity、viewGroup、view進行驗證:
新建一個項目,先來寫ActivityA,代碼如下:
public class ActivityA extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test);
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) { //判定是否爲點擊
Log.e("測試--Down----->", "ActivityA:dispatchTouchEvent");
} else if (ev.getAction() == MotionEvent.ACTION_UP) { //判定是否爲擡起
Log.e("測試--UP------->", "ActivityA:dispatchTouchEvent");
} else {
Log.e("測試--Other---->", "ActivityA:dispatchTouchEvent");
}
return super.dispatchTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
Log.e("測試--Down----->", "ActivityA:onTouchEvent");
} else if (event.getAction() == MotionEvent.ACTION_UP) {
Log.e("測試--UP------->", "ActivityA:onTouchEvent");
} else {
Log.e("測試--Other---->", "ActivityA:onTouchEvent");
}
return super.onTouchEvent(event);
}
}
同理,編寫ViewGroupB,如下
public class ViewGroupB extends LinearLayout {
public ViewGroupB(Context context) {
super(context);
}
public ViewGroupB(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public ViewGroupB(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
Log.e("測試--Down----->","ViewGroupB:dispatchTouchEvent");
} else if (ev.getAction() == MotionEvent.ACTION_UP) {
Log.e("測試--UP------->","ViewGroupB:dispatchTouchEvent");
} else {
Log.e("測試--Other---->","ViewGroupB:dispatchTouchEvent");
}
return super.dispatchTouchEvent(ev);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
Log.e("測試--Down----->","ViewGroupB:onInterceptTouchEvent");
} else if (ev.getAction() == MotionEvent.ACTION_UP) {
Log.e("測試--UP------->","ViewGroupB:onInterceptTouchEvent");
} else {
Log.e("測試--Other---->","ViewGroupB:onInterceptTouchEvent");
}
return super.onInterceptTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
Log.e("測試--Down----->","ViewGroupB:onTouchEvent");
} else if (event.getAction() == MotionEvent.ACTION_UP) {
Log.e("測試--UP------->","ViewGroupB:onTouchEvent");
} else {
Log.e("測試--Other---->","ViewGroupB:onTouchEvent");
}
return super.onTouchEvent(event);
}
}
再編寫ViewC,代碼如下
public class ViewC extends View {
public ViewC(Context context) {
super(context);
}
public ViewC(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
Log.e("測試--Down----->","ViewC:dispatchTouchEvent");
} else if (event.getAction() == MotionEvent.ACTION_UP) {
Log.e("測試--UP------->","ViewC:dispatchTouchEvent");
} else {
Log.e("測試--Other---->","ViewC:dispatchTouchEvent");
}
return super.dispatchTouchEvent(event);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
Log.e("測試--Down----->","ViewC:onTouchEvent");
} else if (event.getAction() == MotionEvent.ACTION_UP) {
Log.e("測試--UP------->","ViewC:onTouchEvent");
} else {
Log.e("測試--Other---->","ViewC:onTouchEvent");
}
return super.onTouchEvent(event);
}
}
將其寫入嵌套在一起,activity_test.xml。代碼如下
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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:background="@color/colorFE6016"
android:gravity="center"
android:orientation="vertical">
<com.hysf.ym.custorm.meView.fenfa.ViewGroupB
android:layout_width="300dp"
android:layout_height="300dp"
android:background="@color/color7F6F51"
android:gravity="center">
<com.hysf.ym.custorm.meView.fenfa.ViewC
android:layout_width="150dp"
android:layout_height="150dp"
android:background="@color/colorFD9C1A" />
</com.hysf.ym.custorm.meView.fenfa.ViewGroupB>
</RelativeLayout>
得到的效果如下:
最外面橘色的是ActivityA,大一點的灰色的是ViewGroupB,中間偏黃色的是ViewC,現在我們點擊ViewC ,得到如下流程:
從打印的結果,我們可以很清楚到看到事件的流程:
首先ActivityA得到了事件,由它的dispatchTouchEvent方法來分發事件,傳給了ViewGroupB,然後由於ViewGroupB的onInterceptTouchEvent方法沒有做出攔截,因此事件傳遞給了ViewC,而ViewC的onTouchEvent沒有處理,就又調用給ViewGroupB,繼ViewGroupB的onTouchEvent也沒有處理,然後傳給ActivityA的onTouchEvent方法,然後按下操作的流程就結束了,至於up事件又調用了ActivityA的dispatchTouchEvent和onTouchEvent,我們稍後再說。
實驗驗證:
ActivityA
測試1:單獨設置ActivityA的onTouchEvent方法返回值設置爲true,測試結果如下
測試2:單獨設置ActivityA的onTouchEvent方法返回值設置爲false,測試結果如下:
ViewGroudB:
測試1:單獨設置ViewGroupB的onInterceptTouchEvent方法返回值設置爲true,測試結果如下:
在測試1的基礎上,設置ViewGroupB的onTouchEvent方法返回值設置爲true,測試結果如下:
在測試1的基礎上,設置ViewGroupB的onTouchEvent方法返回值設置爲false,測試結果如下:
測試2:單獨設置ViewGroupB的onInterceptTouchEvent方法返回值設置爲false,測試結果如下:
在測試2的基礎上,設置ViewGroupB的onTouchEvent方法返回值設置爲true,測試結果如下:
在測試2的基礎上,設置ViewGroupB的onTouchEvent方法返回值設置爲false,測試結果如下:
ViewC
測試1:單獨設置ViewC的onTouchEvent方法返回值設置爲true,測試結果如下
測試2:單獨設置ViewC的onTouchEvent方法返回值設置爲false,測試結果如下:
總結:
- ACTION_DOWN可以理解成所有事件的一種,或者理解成探路的
- 當ACTION_DOWN判斷好具體是怎麼dispatch,怎麼消費的以後,對應的ACTION_MOVE和ACTION_UP就會按照消費的順序,進行執行
- 當然,如果中間有攔截,也是按消費的路線進行
- 對於事件的攔截,我們主要重寫就是OnInterceptTouchEvent和onTouchEvent方法。兩句就可以總結:
(1)對於事件的傳遞,返回結果爲true,表示攔截,不再往下傳遞,爲false,不攔截,繼續往下傳遞。主要針對的就是 OnInterceptTouchEvent方法。
(2)對於事件的處理,返回結果爲true,表示攔截,不再往上傳遞(即我處理的很完美,不需要你再來審覈我!),返回結果爲 false(沒有成功處理事件),繼續向上傳遞。(onTouchEvent方法。)