touch翻译为接触,触摸。我们今天要聊的就是摸的事件。
在Android中了解了Touch事件可以帮助我们解决,ScrollView嵌套ListView,GridView,viewPager滑动冲突,还可以实现覆盖多层布局中里层某个控件的触摸事件处理(简单一点我理解的意思就是隔山打牛)等等,可能和你理解的有些偏差都是我在项目开发过程中总结而来的。针对以上问题,文章后面会给出解决方案。
什么是摸的事件,它们之间又是怎么传递的呢,请听我一一给你们道来。
就是触摸屏幕的一个过程,在Android中用户的Touch事件被包装成MotionEvent。
touch事件主要类型有:
touch事件的元数据包括:
当然一个touch手势被定义为以ACTION_DOWN开始和以 ACTION_UP结束。
Android 中与 Touch 事件相关的三个方法
1、public boolean dispatchTouchEvent(MotionEvent ev),这个方法是用来分发事件处理。
2、public boolean onInterceptTouchEvent(MotionEvent ev) ,这个方法是用来拦截,截断事件处理。
3、public boolean onTouchEvent(MotionEvent ev),这个方法是用来处理事件。
能够响应、调用以上方法的有ViewGroup、View、Activity,继承ViewGroup的控件大多是容器控件,如LinearLayout,RelativeLayout等。继承View的控件大多是显示控件,如TextView,Button等。
需要注意的是显示控件是没有onInterceptTouchEvent方法。
上面我们具体来看一个案例,来了解事件是怎么传递的:
上图的.xml文件
"1.0" encoding="utf-8"?>
"http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.github.ws.touchdemo.MyRelativeLayout1
android:layout_width="280dp"
android:layout_height="280dp"
android:layout_centerInParent="true"
android:background="#f00000">
<com.github.ws.touchdemo.MyRelativeLayout2
android:layout_width="180dp"
android:layout_height="180dp"
android:layout_centerInParent="true"
android:background="#00f000">
<com.github.ws.touchdemo.MyTextView
android:layout_width="80dp"
android:layout_height="80dp"
android:layout_centerInParent="true"
android:background="#0000f0" />
com.github.ws.touchdemo.MyRelativeLayout2>
com.github.ws.touchdemo.MyRelativeLayout1>
MyRelativeLayout1
package com.github.ws.touchdemo;
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.widget.RelativeLayout;
/**
* Created by Administrator on 3/9 0009.
*/
public class MyRelativeLayout1 extends RelativeLayout {
public MyRelativeLayout1(Context context) {
super(context);
}
public MyRelativeLayout1(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
Log.e("------MyRelativeLayout1----", "--------dispatchTouchEvent------");
return super.dispatchTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.e("------MyRelativeLayout1----", "--------onTouchEvent------");
return super.onTouchEvent(event);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
Log.e("------MyRelativeLayout1----", "--------onInterceptTouchEvent------");
return super.onInterceptTouchEvent(ev);
}
}
MyRelativeLayout2和MyRelativeLayout1一样。
MyTextView
package com.github.ws.touchdemo;
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.widget.TextView;
/**
* Created by Administrator on 3/9 0009.
*/
public class MyTextView extends TextView {
public MyTextView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MyTextView(Context context) {
super(context);
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
Log.e("------MyTextView----", "--------dispatchTouchEvent------");
return super.dispatchTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.e("------MyTextView----", "--------onTouchEvent------");
return super.onTouchEvent(event);
}
}
当我们触摸蓝色区域Log打印如下:
通过log图,我们很容易的得出结论:dispatchTouchEvent(),onInterceptTouchEvent()隧道式向下分发(先里后外)。而onTouchEvent()执行顺序恰好相反即冒泡式向上处理(先外后里)。
我们来改变touch方法的返回值,看看它们又是怎么执行的。
我直接贴log:
很明显事件并不会向外分发,后面的事件不会触发,默认返回false。
onInterceptTouchEvent()用于处理事件并改变事件的传递方向。处理事件就是在函数内部编写代码处理就可以了。而决定传递方向的是返回值,返回为false时事件会传递给子控件的dispatchTouchEvent();返回值为true时事件会传递给当前控件的onTouchEvent(),不会传递给子控件,这就是所谓的Intercept(拦截,截断)。默认返回false。
我们在onTouchEvent中添加如下代码:
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
Log.e("-------MyRelativeLayout1---------", "--------ACTION_DOWN------");
break;
case MotionEvent.ACTION_MOVE:
Log.e("--------MyRelativeLayout1--------", "--------ACTION_MOVE------");
break;
}
贴出log:
onTouchEvent() 用于处理事件,返回值决定当前控件是否消费了这个事件。如果返回值为true表示消费了这个事件,那么系统就会认为ACTION_DOWN已经发生,ACTION_MOVE或者ACTION_UP就被捕获。反之,ACTION_MOVE或者ACTION_UP就不会被捕获。默认是返回false。(ACTION_MOVE或者ACTION_UP发生的前提是一定曾经发生了ACTION_DOWN)。
看到这里,我相信大家对事件传递有了自己的认识,我给出开头滑动冲突的解决方案:
mScrollView.requestDisallowInterceptTouchEvent(true);
一张图来诠释 Android Touch
事件传递:
如果你有兴趣查看源码,请点击github地址