关系分布
Android 中与 Touch 事件相关的方法包括:dispatchTouchEvent(MotionEvent ev)、onInterceptTouchEvent(MotionEvent ev)、onTouchEvent(MotionEvent ev);能够响应这些方法的控件包括:ViewGroup 及其子类、Activity,其中Activity和最小单位的view(例如TextView,不能向其中添加子view )是不存在onInterceptTouchEvent方法的。以下所说的上层下层是按照由外到内来说。
Touch事件的三个方法
1.事件分发:public boolean dispatchTouchEvent(MotionEvent ev)
从开始触摸的时候,事件的执行顺序一定是要从最外层的Activity开始,然后按照Activity->window->viewgroup->view依次往下传递,最终将事件传递到具体的view。这种方式也叫隧道方式。我们可以根据它的返回值来控制其走向和终止。
- return super.dispatchTouchEvent(ev):判断本层是否可执行onInterceptTouchEvent方法,能就派发给onInterceptTouchEvent方法执行,不能就接着向下层的dispatchTouchEvent方法去派发。
- return false:判断时候有上层view,有就接着让上层的onTouchEvent方法消费,没有(说明已经是最外层)本方法消费后消失。
- return true:仅本view消费后消失,并不往下分发。
2.事件拦截:public boolean onInterceptTouchEvent(MotionEvent ev)
每当viewgroup中的dispatchTouchEvent正常分发紧接着要执行viewgroup中的onInterceptTpuchEvent方法对此事件进行拦截,和上边一样我们也可以控制其返回值进而控制其是进行拦截事件然后响应还是接着向下一层分发。
- return super.onInterceptTouchEvent(ev):判断是否有下层,有就接着向下层的dispatchTouchEvent派发,没有就执行本层view的onTouchEvent.
- return false:和super.onInterceptTouchEvent返回值功能一样。
- return true:进行拦截,不在向下层进行派发,直接交由本层的onTouchEvent方法进行消费。
3.事件响应:public boolean onTouchEvent(MotionEvent ev)
一旦事件拦截成功或者执行到最后,事件要进行消费(响应),这里也是有三个返回值,可以控制消费完毕后是就此消失还是接着让其他view也跟着响应一下。
- return onTouchEvent(ev):接着向上层传递,通知上层的onTouchEvent方法消费。若上层的返回值也是super.onTouchEvent或者false,接着向上传递,直至事件终止。
- return false:和super.onTouchEvent返回值的功能一样。
- return true:消费后终止(消失)。
代码分析
代码布局
这里创建两个类FatherView、ChildView分别代表父View和子View,两者都继承LineraLayout,由父View嵌套子View。
父View:
public class FatherView extends LinearLayout {
public FatherView(Context context) {
super(context);
}
public FatherView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public FatherView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
Log.e("FatherView","++++++++++++++dispathTouchEvent---->>"+ TouchEventUtil.getTouchAction(ev.getAction()));
return super.dispatchTouchEvent(ev);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
Log.e("FatherView","++++++++++++++onInterceptTouchEvent---->>"+TouchEventUtil.getTouchAction(ev.getAction()));
return super.onInterceptTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.e("FatherView","++++++++++++++onTouchEvent---->>"+TouchEventUtil.getTouchAction(event.getAction()));
return super.onTouchEvent(event);
}
}
子View:
和父View一样,只是换个名字。
主Activity:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
Log.e("MainActivity","++++++++++++++dispatchTouchEvent---->>"+TouchEventUtil.getTouchAction(ev.getAction()));
return super.dispatchTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.e("MainActivity","++++++++++++++onTouchEvent---->>"+TouchEventUtil.getTouchAction(event.getAction()));
return super.onTouchEvent(event);
}
}
为了便于事件说明和减少代码量我们将事件触发封装成一个方法类:
public class TouchEventUtil {
public static String getTouchAction(int actionId){
String touchAction;
switch (actionId){
case MotionEvent.ACTION_DOWN:
touchAction = "ACTION_DOWN";
break;
case MotionEvent.ACTION_MOVE:
touchAction = "ACTION_MOVE";
break;
case MotionEvent.ACTION_UP:
touchAction = "ACTION_UP";
break;
case MotionEvent.ACTION_CANCEL:
touchAction = "ACTION_CANCEL";
break;
case MotionEvent.ACTION_OUTSIDE:
touchAction = "ACTION_OUTSIDE";
break;
default:
touchAction = "unknow";
break;
}
touchAction = "TouchAction is " + touchAction;
return touchAction;
}
}
五种情形
情形1:
将所有的层级的三个事件方法返回值都按默认值设置,即分别返回super.dispatchTouchEvent(ev)、super.onIntercepterTouchEvent(ev)、super.onTouchEvent(ev).
通过日志可以看出没进一层都要经过事件分发、拦截、响应消费三个依次传递,如果没有拦截则会接着向下一层的事件分发传递,如果没有下一层则直接进入本层的onTouchEvent中进行消费。
情形2:
仅将父View中dispatchTouchEvent返回值设为true。
通过日志可以看出如果dispatchTouchEvent的返回值设为true,则事件不在往下分发而是直接由本方法进行消费消失。
情形3:
仅将父View中dispatchTouchEvent返回值设为false。
当dispatchTouchEvent返回值设为false时,事件也不在往下分发,而是通知上层的onTouchEvent进行消费,如果没有上层,则事件就此消费后终止。
情景4:
仅将父View中的onInterceptTouchEvent返回值设为true和onTouchEvent返回值设为true。
通过日志可以看出如果将onInterceptTouchEvent返回值设为true,则代表事件将被本层拦截进而传递到本层的onTouchEvent,如果本层的onTouchEvent返回值也设为true,则表示事件在此完全消费而后消失。
情形5:
仅将父View中的onInterceptTouchEvent返回值设为false和子View中onTouchEvent返回值设为false。
通过日志有没有发现和情形1一样,没错,onInterceptTouchEvent返回值设成false和设成默认都一样,都是将事件进一步向下层传递。onTouchEvent返回值设成false和设成默认也是一样,作用都是本方法消费后向上层的onTouchEvent传递消费。
其实还有其他情形,这里就不在一一说明,这五种情形基本上已经将触摸事件的分发、拦截、响应功能已涵盖完,特殊情况有待过往同学再次深入探究,错误之处望明确指出。