最近在整理 关于Android的Touch事件的分发机制,首先Touch事件的方法包括三种:dispatchTouchEvent(MotionEvent event) , onInterceptTouchEvent(MotionEvent ev) , onTouchEvent(MotionEvent event) ,下面是这三个方法在界面中的调用映射表:
Touch事件的方法 | 方法介绍 | View | ViewGroup | Activity |
---|---|---|---|---|
dispatchTouchEvent | 事件的分发 | yes | yes | yes |
onInterceptTouchEvent | 事件的拦截 | yes | yes | no |
onTouchEvent | 事件的响应 | yes | yes | yes |
Ps:这里的View 若只是Button或者其他不包含子控件的View是不包含OnInterceptTouchEvent方法的。
下面进行事件分发的详细解析:
Touch事件的方法\返回值 | 解析 | true | false | super() |
---|---|---|---|---|
dispatchTouchEvent | 每次的Touch都会由Activity的dispatchTouchEvent向下层的View或ViewGroup进行事件分发 | ☞当前的控件自己处理事件 | 1、若该View是Activity或者是button等不含子控件的View,则将事件分发给自己处理 ☞ ; 2、否则传递给上层的Activity或者父控件的OnTouchEvent方法 ☝ | ☟ 下发给Activity的子控件或View自身的OnIntercepterTouchEvent |
onInterceptTouchEvent | 是否拦截事件 | ☞ 当前View的OnTouchEvent | ☟ 子控件的dispatch | ☞ |
onTouchEvent | 事件的响应 | ☞ 当前View | ☝ 父控件的TouchEvent | ☝ |
解释:
☝ : 表示向上父类传递事件
☟ : 表示向下子类分发事件
☞ : 表示由自己来处理事件
下面 我们通过代码来验证一下我们上面的理论:
首先定义两个子布局:
tauchButton:
public class tauchButton extends LinearLayout {
public tauchButton(Context context) {
super(context);
}
public tauchButton(Context context, AttributeSet attrs) {
super(context, attrs);
}
public tauchButton(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.i("zgy", "TouchEventChilds | onTouchEvent --> " + TouchEventUtil.getTouchAction(event.getAction()));
return super.onTouchEvent(event);
}
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
Log.i("zgy", "TouchEventChilds | dispatchTouchEvent --> " + TouchEventUtil.getTouchAction(event.getAction()));
return super.dispatchTouchEvent(event);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
Log.i("zgy", "TouchEventChilds | onInterceptTouchEvent --> " + TouchEventUtil.getTouchAction(ev.getAction()));
return super.onInterceptTouchEvent(ev);
}
}
父控件touchLinearlayout:
public class touchLinearlayout extends LinearLayout {
public touchLinearlayout(Context context) {
super(context);
}
public touchLinearlayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public touchLinearlayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.e("zgy", "TouchEventFather | onTouchEvent --> " + TouchEventUtil.getTouchAction(event.getAction()));
return super.onTouchEvent(event);
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
Log.e("zgy", "TouchEventFather | dispatchTouchEvent --> " + TouchEventUtil.getTouchAction(ev.getAction()));
return super.dispatchTouchEvent(ev);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
Log.e("zgy", "TouchEventFather | onInterceptTouchEvent --> " + TouchEventUtil.getTouchAction(ev.getAction()));
return super.onInterceptTouchEvent(ev);
}
}
activity的layout布局:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<com.example.lk.helloworld.view.touchLinearlayout
android:layout_width="match_parent"
android:layout_height="400dp"
android:background="@color/bg_discount"
android:layout_gravity="center">
<com.example.lk.helloworld.view.tauchButton
android:id="@+id/buttonChild"
android:layout_width="200dp"
android:layout_height="200dp"
android:text="button"
android:background="@color/bg_blue_light"
android:layout_gravity="center"
android:layout_marginLeft="20dp"
/>
</com.example.lk.helloworld.view.touchLinearlayout>
</LinearLayout>
Activity1:
package com.example.lk.helloworld;
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.MotionEvent;
import com.example.lk.helloworld.utils.TouchEventUtil;
/**
*
*/
public class Activity1 extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity1_layout);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.w("zgy", "TouchEventActivity | onTouchEvent --> " + TouchEventUtil.getTouchAction(event.getAction()));
return super.onTouchEvent(event);
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
Log.w("zgy", "TouchEventActivity | dispatchTouchEvent --> " + TouchEventUtil.getTouchAction(ev.getAction()));
return super.dispatchTouchEvent(ev);
}
}
首先我们先看看都使用super的方法时是神马结果;
case1 : 所有的返回值都调用super方法
这里我们可以看出,首先由Activity的dispatchTouchEvent方法,想下分发事件,由于是Activity,没有OnIterceptTouchEvent因此是分发给子控件的dispatchTouchEvent方法,当到TouchEventFather分发事件的时候,就是分发给了自己的OnIntercepterTouchEvent ; 这时候调用的依然是super的方法,根据上面的表格显示,默认不拦截,就把事件分发给了下一个控件的dispatchTouchEvent , 最终由于都没有消耗该事件,于是事件会由子控件返回给当前的Acticity,然后释放掉。
下面我们随机测试一下其他的情况:
case2 : 父控件的onTouchEvent的返回值设置为true
截图可以看出,当运行到TouchEventFather的OnTouchEvent的时候,就把事件给消耗掉了,就不会走Activity的OntouchEvent的方法了;
case3:tauchButton的dispatchTouchEvent的返回值是false
截图可以发现,当运行到tauchButton的dispatchEvent方法,返回false时,事件不再向下分发,而是将事件传递给上层的View的OntouchEvent方法;
剩下的 都经过实践的出,这里不再一一列举。
当然到这里,我们基本搞懂了View的事件分发,不过我们似乎还忘了一点事情,那就是View的监听事件:setOnTouchListener 和 setOnClickListener.到这里的时候 我突然想到如果给button加一个监听事件会怎么样。
下面将Activity该成如下形式:
package com.example.lk.helloworld;
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import com.example.lk.helloworld.utils.TouchEventUtil;
import com.example.lk.helloworld.view.tauchButton;
/**
*
*/
public class Activity1 extends Activity {
private tauchButton buttonChild;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity1_layout);
buttonChild = (tauchButton)findViewById(R.id.buttonChild);
buttonChild.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
Log.e("Activity1.this","touchListener 事件触发");
return true;
}
});
buttonChild.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.e("Activity1.this", "clickListener 事件触发");
}
});
}
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.w("zgy", "TouchEventActivity | onTouchEvent --> " + TouchEventUtil.getTouchAction(event.getAction()));
return super.onTouchEvent(event);
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
Log.w("zgy", "TouchEventActivity | dispatchTouchEvent --> " + TouchEventUtil.getTouchAction(ev.getAction()));
return super.dispatchTouchEvent(ev);
}
}
我们再次运行会发现如图所示。当事件分发到TouchEventChilds的OntouchEvent的时候,被touchListener事件拦截,并且消耗掉事件。
如果我们把setOnTouchListener的返回值设置为false;
这时我们可以发现,事件会被一直传递下去,直到传递给setOnClickListener事件。如果没有监听Onclick事件的时候:
事件会按原路返回到Activity然后释放掉;
那么我们现在可以发现:
触摸事件传递顺序:dispatchTouchEvent–>>onTouch–>>onTouchEvent–>>onClick。而且是当OnTouchEvent的手势ACTION_UP结束的时候才会调用Onclick方法。到这里我们基本对Touch的事件有了一定的了解了。