我们先从Activity的dispatchTouchEvent方法进入Activity源码查看。
public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
// 第一次按下,调该方法
onUserInteraction();
}
/**
* 事件传递给window,window是抽象类,具体实现是PhoneWindow
*/
if (getWindow().superDispatchTouchEvent(ev)) {
return true;
}
// 在当前视图没有view消费该事件则调用Activity的onTouchEvent方法
return onTouchEvent(ev);
}
我们再看看PhoneWindow实现的源码。
@Override
public boolean superDispatchTouchEvent(MotionEvent event) {
/**
* mDecor是DecorView,它是Activity顶层的View,DecorView继承FrameLayout
*/
return mDecor.superDispatchTouchEvent(event);
}
我们继续看看 DecorView的superDispatchTouchEvent方法源码。
public boolean superDispatchTouchEvent(MotionEvent event) {
/**
* 继续调用父类的方法,FrameLayout本身没有dispatchTouchEvent这个方法。
* 在FrameLayout父类,ViewGroup中调用。
*/
return super.dispatchTouchEvent(event);
}
截止到这里,从Activity的事件,被传递到了ViewGroup。
而Activity的 getWindow().superDispatchTouchEvent的返回值就是ViewGroup.dispatchTouchEvent的方法返回值。如果getWindow().superDispatchTouchEvent,返回为true,则该事件被消费了,否者调用Activity 的onTouchEvent的方法。
我们看看 Activity的onTouchEvent的方法。
public boolean onTouchEvent(MotionEvent event) {
//判断事件是否在可响应范围之类
if (mWindow.shouldCloseOnTouch(this, event)) {
finish();
return true;
}
return false;
}
Activity的dispatchTouchEvent方法的返回值就等于 Activity的onTouchEvent方法的返回值。无论返回值是什么到这里都结束了。
public class MainActivity extends AppCompatActivity {
public static final String TAG = "事件分发流程";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.d(MainActivity.TAG, "MainActivity:onTouchEvent: ");
return super.onTouchEvent(event);
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
Log.d(MainActivity.TAG, "MainActivity:dispatchTouchEvent: ");
return super.dispatchTouchEvent(ev);
}
}
创建MyViewGroup
public class MyViewGroup extends FrameLayout {
public MyViewGroup(@NonNull Context context) {
super(context);
}
public MyViewGroup(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.d(MainActivity.TAG, "MyViewGroup:onTouchEvent: ");
return super.onTouchEvent(event);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
Log.d(MainActivity.TAG, "MyViewGroup:onInterceptTouchEvent: ");
return super.onInterceptTouchEvent(ev);
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
Log.d(MainActivity.TAG, "MyViewGroup:dispatchTouchEvent: ");
return super.dispatchTouchEvent(ev);
}
}
创建MyView
public class MyView extends View {
public MyView(Context context) {
super(context);
}
public MyView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.d(MainActivity.TAG, "MyView:onTouchEvent: ");
return super.onTouchEvent(event);
}
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
Log.d(MainActivity.TAG, "MyView:dispatchTouchEvent: ");
return super.dispatchTouchEvent(event);
}
}
MainActivity 的activity_main布局
<com.yc.reviewandroid.MyViewGroup xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity"
android:background="@color/colorPrimaryDark">
<com.yc.reviewandroid.MyView
android:layout_width="200dp"
android:layout_height="200dp"
android:background="@color/colorAccent"/>
com.yc.reviewandroid.MyViewGroup>
运行代码
点击红色view查看日志
总结:
事件默认传递流程
MainActivity:dispatchTouchEvent -> MyViewGroup:dispatchTouchEvent
-> MyViewGroup:onInterceptTouchEvent -> MyView:dispatchTouchEvent
-> MyView:onTouchEvent -> MyGroupView:onTouchEvent
-> MainActivity:onTouchEvent
总结:同一个事件,如果子View(ViewGroup)没有消费该事件,
那么后续事件 就不会再传递到子View中。
MainActivity:dispatchTouchEvent -> MainActivity:onTouchEvent
public class MyScrollView extends ScrollView {
public MyScrollView(Context context) {
super(context);
}
public MyScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
boolean result=super.onInterceptTouchEvent(ev);
Log.d(MainActivity.TAG, "MyScrollView:onInterceptTouchEvent: "+result);
return result;
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
return super.dispatchTouchEvent(ev);
}
}
创建MyButton
public class MyButton extends AppCompatButton {
public MyButton(Context context) {
super(context);
}
public MyButton(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if(event.getAction()==MotionEvent.ACTION_CANCEL){
Log.d(MainActivity.TAG, "MyButton:onTouchEvent: 取消");
}else{
Log.d(MainActivity.TAG, "MyButton:onTouchEvent: "+event.getAction());
}
return super.onTouchEvent(event);
}
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
if(event.getAction()==MotionEvent.ACTION_CANCEL){
Log.d(MainActivity.TAG, "MyButton:dispatchTouchEvent: 取消");
}else{
Log.d(MainActivity.TAG, "MyButton:dispatchTouchEvent: "+event.getAction());
}
return super.dispatchTouchEvent(event);
}
}
修改MainActivity 的activity_main布局
<com.yc.reviewandroid.MyScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/colorPrimaryDark"
tools:context=".MainActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<com.yc.reviewandroid.MyButton
android:layout_width="200dp"
android:layout_height="200dp"
android:background="@color/colorAccent"
android:text="button" />
<View
android:layout_width="match_parent"
android:layout_height="2000dp" />
LinearLayout>
com.yc.reviewandroid.MyScrollView>
运行代码
点击button,不抬起继续向上滚动列表,然后查看打印日志
MyScrollView的onInterceptTouchEvent返回true,事件被拦截,MyButton的dispatchTouchEvent就产生了
取消事件
<com.yc.reviewandroid.MyViewGroup xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity"
android:background="@color/colorPrimaryDark">
<com.yc.reviewandroid.MyView
android:layout_width="200dp"
android:layout_height="200dp"
android:background="@color/colorAccent"/>
com.yc.reviewandroid.MyViewGroup>
修改MyViewGroup里面的代码
public class MyViewGroup extends FrameLayout {
public MyViewGroup(@NonNull Context context) {
super(context);
}
public MyViewGroup(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
Log.d(MainActivity.TAG, "MyViewGroup:onTouchEvent: 手指按下");
break;
case MotionEvent.ACTION_MOVE:
Log.d(MainActivity.TAG, "MyViewGroup:onTouchEvent: 手指移动");
break;
case MotionEvent.ACTION_UP:
Log.d(MainActivity.TAG, "MyViewGroup:onTouchEvent: 手指抬起");
break;
}
super.onTouchEvent(event);
return true;
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
Log.d(MainActivity.TAG, "MyViewGroup:onInterceptTouchEvent: ");
super.onInterceptTouchEvent(ev);
return true;
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
Log.d(MainActivity.TAG, "MyViewGroup:dispatchTouchEvent:");
return super.dispatchTouchEvent(ev);
}
}
运行点击MyView,查看日志
日志显示到MyViewGroup 手指抬起就终止了,MyView没被打印,事件被MyViewGroup的onTouchEvent消费了。
我也是学习别人的自己总结记录下来,方便以后查看,俗话说,好记性不如烂笔头,文章写得不是很好,不喜勿喷,谢谢,有错误的地方,欢迎留言。