案例
Activity代码:
public class Main2Activity extends AppCompatActivity {
private static final String TAG = "-EVENT";
private RelativeLayout ll_parent;
private Button btn_test;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ll_parent = findViewById(R.id.ll_parent);
btn_test = (Button) findViewById(R.id.btn_test);
btn_test.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
return false;
}
});
btn_test.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.d(TAG, "onClick: View");
}
});
btn_test.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
Log.d(TAG, "onLongClick: View");
return false;
}
});
ll_parent.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
Log.d(TAG, "onTouch: viewGroup " + event.getAction());
return false;
}
});
ll_parent.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
Log.d(TAG, "onLongClick: viewGroup ");
return false;
}
});
ll_parent.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.d(TAG, "onClick: viewGroup ");
}
});
}
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.d(TAG, "onTouchEvent: Main2Activity" + event.getAction());
return super.onTouchEvent(event);
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
Log.d(TAG, "dispatchTouchEvent: Main2Activity " + ev.getAction());
return super.dispatchTouchEvent(ev);
}
}
布局文件:
<com.jiang.eventtest.TestRelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/ll_parent"
android:layout_width="150dp"
android:layout_height="150dp"
android:background="#7e8"
tools:context="com.jiang.eventtest.MainActivity">
<com.jiang.eventtest.TestButton
android:id="@+id/btn_test"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_centerInParent="true"
android:text="button" />
</com.jiang.eventtest.TestRelativeLayout>
然后页面是这样的
<meta charset="utf-8">
[图片上传失败...(image-ed5e48-1523543808448)]点击按键:
03-23 14:34:35.174 9163-9163/com.jiang.eventtest D/-EVENT: dispatchTouchEvent: Main2Activity 0
03-23 14:34:35.174 9163-9163/com.jiang.eventtest D/-EVENT: dispatchTouchEvent: viewGroup 0
03-23 14:34:35.174 9163-9163/com.jiang.eventtest D/-EVENT: onInterceptTouchEvent: viewGroup 0
03-23 14:34:35.174 9163-9163/com.jiang.eventtest D/-EVENT: dispatchTouchEvent: View 0
03-23 14:34:35.174 9163-9163/com.jiang.eventtest D/-EVENT: onTouchEvent: View 0
03-23 14:34:35.204 9163-9163/com.jiang.eventtest D/-EVENT: dispatchTouchEvent: Main2Activity 1
03-23 14:34:35.204 9163-9163/com.jiang.eventtest D/-EVENT: dispatchTouchEvent: viewGroup 1
03-23 14:34:35.204 9163-9163/com.jiang.eventtest D/-EVENT: onInterceptTouchEvent: viewGroup 1
03-23 14:34:35.204 9163-9163/com.jiang.eventtest D/-EVENT: dispatchTouchEvent: View 1
03-23 14:34:35.204 9163-9163/com.jiang.eventtest D/-EVENT: onTouchEvent: View 1
03-23 14:34:35.204 9163-9163/com.jiang.eventtest D/-EVENT: onClick: View
点击空白区域:
03-23 14:34:47.874 9163-9163/com.jiang.eventtest D/-EVENT: dispatchTouchEvent: Main2Activity 0
03-23 14:34:47.874 9163-9163/com.jiang.eventtest D/-EVENT: onTouchEvent: Main2Activity0
03-23 14:34:47.934 9163-9163/com.jiang.eventtest D/-EVENT: dispatchTouchEvent: Main2Activity 1
03-23 14:34:47.934 9163-9163/com.jiang.eventtest D/-EVENT: onTouchEvent: Main2Activity1
从上面可以看到,事件的传递顺序:
Activity:dipatchTouchEvent-->
---------------ViewGroup:onInterceptTouchEvent-->dipatchTouchEvent-->
---------------------------------------------------------------------View:dispatchTouchEvent-->onTouchEvent
源码
PART 1
public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
//空方法,当屏幕上按键(返回,home)按下,接触当前Activity时都会调用,可以根据需求重写
onUserInteraction();
}
if (getWindow().superDispatchTouchEvent(ev)) {
return true;
}
return onTouchEvent(ev);
}
然后这里有一个getWindow().superDispatchTouchEvent(ev)的调用。首先Window为抽象类,superDispatchTouchEvent为抽象方法,
这里的Window对象是根据Activity attch方法中
mWindow = new PhoneWindow(this, window, activityConfigCallback)
PhoneWindow继承了Window并重写了superDispatchTouchEvent方法
@Override
public boolean superDispatchTouchEvent(MotionEvent event) {
return mDecor.superDispatchTouchEvent(event);
}
而DecorView继承了FrameLayout也就是一个ViewGroup。所以getWindow().superDispatchTouchEvent(ev)就是把当前的事件分发出去,如果事件被消费则返回true
如果分发出去的事件未被消费则会调用onTouchEvent
public boolean onTouchEvent(MotionEvent event) {
if (mWindow.shouldCloseOnTouch(this, event)) {
finish();
return true;
}
return false;
}
Window的shouldCloseOnTouch:
public boolean shouldCloseOnTouch(Context context, MotionEvent event) {
if (mCloseOnTouchOutside && event.getAction() == MotionEvent.ACTION_DOWN
&& isOutOfBounds(context, event) && peekDecorView() != null) {
return true;
}
return false;
}
- mCloseOnTouchOutside是一个boolean变量,它是由Window的android:windowCloseOnTouchOutside属性值决定;
- isOutOfBounds(context, event)判断当前事件是否是在Activiy之外,是返回true;
- peekDecorView返回当前Acticvty对应的mDecor。