了解Android View事件的傳遞過程枚荣,是編寫復雜控件、解決ScrollView嵌套等問題的基礎澄成。
以touch事件為例暖哨,和其相關的回調函數(shù)有:
- dispatchTouchEvent肌厨,進行事件的分發(fā)
- onInterceptTouchEvent培慌,ViewGroup對事件進行攔截
- onTouchEvent,響應具體的touch事件
下面通過一個簡單的demo來觀察這三個函數(shù)是如何調用的柑爸。
demo包括兩部分吵护,CustomViewGroup和CustomView,通過重寫上面的三個方法,在其中輸出日志來觀察調用過程馅而。
CustomViewGroup代碼如下:
public class CustomViewGroup extends FrameLayout {
private static final String TAG = "CustomViewGroup";
public CustomViewGroup(Context context) {
super(context);
}
public CustomViewGroup(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CustomViewGroup(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.d(TAG, "onTouchEvent");
return super.onTouchEvent(event);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
Log.d(TAG, "onInterceptTouchEvent");
return super.onInterceptTouchEvent(ev);
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
Log.d(TAG, "dispatchTouchEvent");
return super.dispatchTouchEvent(ev);
}
}
CustomView代碼如下:
public class CustomView extends View {
private static final String TAG = "CustomView";
public CustomView(Context context) {
super(context);
}
public CustomView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public CustomView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.d(TAG, "onTouchEvent");
return super.onTouchEvent(event);
}
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
Log.d(TAG, "dispatchTouchEvent");
return super.dispatchTouchEvent(event);
}
}
布局文件
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout 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"
tools:context="com.xiaomi.shop.testdemo.MainActivity">
<com.xiaomi.shop.testdemo.view.event.CustomViewGroup
android:background="#ff0"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.xiaomi.shop.testdemo.view.event.CustomView
android:background="#f00"
android:layout_width="200dp"
android:layout_height="200dp" />
</com.xiaomi.shop.testdemo.view.event.CustomViewGroup>
</FrameLayout>
運行demo祥诽,界面如下:
紅色區(qū)域為CustomView,黃色區(qū)域為CustomViewGroup
點擊紅色區(qū)域瓮恭,看到日志輸出如下:
05-08 11:44:55.304 12481-12481/com.xiaomi.shop.testdemo D/CustomViewGroup: dispatchTouchEvent
05-08 11:44:55.304 12481-12481/com.xiaomi.shop.testdemo D/CustomViewGroup: onInterceptTouchEvent
05-08 11:44:55.304 12481-12481/com.xiaomi.shop.testdemo D/CustomView: dispatchTouchEvent
05-08 11:44:55.304 12481-12481/com.xiaomi.shop.testdemo D/CustomView: onTouchEvent
05-08 11:44:55.305 12481-12481/com.xiaomi.shop.testdemo D/CustomViewGroup: onTouchEvent
1. 默認情況
dispatchTouchEvent雄坪,進行事件的分發(fā)
onInterceptTouchEvent,ViewGroup對事件進行攔截屯蹦,默認返回false维哈,即不攔截
onTouchEvent,響應具體的touch事件登澜,默認返回false阔挠,即無響應
由上面的日志輸出可以看出,默認情況下脑蠕,函數(shù)的調用順序為:
ViewGroup.dispatchTouchEvent
ViewGroup.onInterceptTouchEvent
View.dispatchTouchEvent
View.onTouchEvent
ViewGroup.onTouchEvent
需要注意的是购撼,上面的日志只在手指點到紅色區(qū)域后輸出一次,手指滑動時谴仙,并沒有新的日志輸出迂求。因為CustomView的onTouchEvent返回的false,所以CustomViewGroup認為CustomView不處理touch事件狞甚,所以后續(xù)的點擊事件便不再向其分發(fā)锁摔。如何沒有子View處理touch事件的話,那么就會自己處理哼审,所以最后會調用到CustomViewGroup的onTouchEvent谐腰。
2. CustomView的onTouchEvent返回true
下面把CustomView的onTouchEvent的返回值改為true,表示處理touch事件涩盾,此時的日志輸出為:
05-08 15:27:05.354 24788-24788/com.xiaomi.shop.testdemo D/CustomViewGroup: dispatchTouchEvent
05-08 15:27:05.354 24788-24788/com.xiaomi.shop.testdemo D/CustomViewGroup: onInterceptTouchEvent
05-08 15:27:05.354 24788-24788/com.xiaomi.shop.testdemo D/CustomView: dispatchTouchEvent
05-08 15:27:05.355 24788-24788/com.xiaomi.shop.testdemo D/CustomView: onTouchEvent
05-08 15:27:05.380 24788-24788/com.xiaomi.shop.testdemo D/CustomViewGroup: dispatchTouchEvent
05-08 15:27:05.380 24788-24788/com.xiaomi.shop.testdemo D/CustomViewGroup: onInterceptTouchEvent
05-08 15:27:05.380 24788-24788/com.xiaomi.shop.testdemo D/CustomView: dispatchTouchEvent
05-08 15:27:05.380 24788-24788/com.xiaomi.shop.testdemo D/CustomView: onTouchEvent
...
這次跟前面有兩個不同十气,首先是點擊和滑動都會輸出日志,因為CustomView對touch事件感興趣春霍,所以后續(xù)的事件都會傳遞給它砸西。另外這次沒有調用CustomViewGroup的onTouchEvent函數(shù),這是因為子View處理了touch事件址儒,所以自己便不再處理芹枷。
3. 打開事件攔截
將CustomViewGroup的onInterceptTouchEvent函數(shù)的返回值改為true,啟動事件攔截莲趣。在紅色區(qū)域點擊滑動鸳慈,日志輸出如下:
05-08 15:35:26.393 8556-8556/com.xiaomi.shop.testdemo D/CustomViewGroup: dispatchTouchEvent
05-08 15:35:26.394 8556-8556/com.xiaomi.shop.testdemo D/CustomViewGroup: onInterceptTouchEvent
05-08 15:35:26.394 8556-8556/com.xiaomi.shop.testdemo D/CustomViewGroup: onTouchEvent
CustomView的任何函數(shù)都沒有調用。