android触摸事件处理逻辑


要想做一个用户体验好的app,弄清楚系统对触摸事件的处理逻辑是最基本的。一个完整的触摸事件由一个ActionDownN个ActionMove和一个actionup组成,比如我们点击屏幕的时候,如果只是点击不滑动N的值就为1,如果滑动N的值就会不断的增加。要处理好触摸事件,只需要了解3个方法就可以了,onTouchEvent()onInterceptTouchEvent()dispatchTouchEvent(),其中的onInterceptTouchEvent只有ViewGroup才有。

1、onTouchEvent():用来消费触摸事件。

2、onInterceptTouchEvent():用来拦截事件。

3、dispatchTouchEvent():用来分发触摸事件。

一个触摸事件感知的顺序是从外到里的,也就是说最先感知到的是Activity,然后是最外层的viewGroup,一直到最后的View,消费的时候则相反,也就是从里到外。3个方法调用德尔顺序是:dispatch--->intercept--->touchevent,假如一个ViewGroup接收到一个触摸事件,那么最先调用的是这个ViewGroup的dispatchTouchEvent()方法,然后调用这个ViewGroup的onInterceptTouchEvent(),然后调用这个ViewGroup里面的View的dispatchTouchEvent(),因为View没有onInterceptTouchEvent方法,所以就会调用这个View的onTouchEvent方法,然后再调用ViewGroup的onTouchEvent方法,接着ViewGroup就会把这个事件传递给自己的父类的onTouchEvent方法(这样德尔逻辑是我们没有改变系统自己的传递逻辑的情况下,后面将详细的演示改变传递逻辑后的情况),如果传到Activity的onTouchEvent还没有被消费掉的话那么这个事件就会消失。

下面我们来具体的演示事件处理情况,首先要分两种情况,一种是只有一个View的情况,一种是有一个viewGroup然后ViewGroup里面还含有View的情况(ViewGroup含有多个ViewGroup的情况也是类似的,就不一样举例说明了)。这两种情况我都是用自定义View的形式来演示的。

情况一、只含有View的情况

这里就新建一个类继承Button,然后我们重写Button的onTouchEvent和dispatchTouchEvent方法,自定义view的代码如下:

public class OnlyButton extends Button {
	private String TAG = "OnlyButton";

	public OnlyButton(Context context, AttributeSet attrs) {
		super(context, attrs);
	}

	public OnlyButton(Context context) {
		super(context);
	}


	// 触摸消费
	@Override
	public boolean onTouchEvent(MotionEvent event) {
		switch (event.getAction()) {
		case MotionEvent.ACTION_DOWN:
			Log.e(TAG, "onTouchEvent---ACTION_DOWN");
			break;

		case MotionEvent.ACTION_MOVE:
			Log.e(TAG, "onTouchEvent---ACTION_MOVE");
			break;
		case MotionEvent.ACTION_CANCEL:
			Log.e(TAG, "onTouchEvent---ACTION_CANCEL");
			break;
		case MotionEvent.ACTION_UP:
			Log.e(TAG, "onTouchEvent---ACTION_UP");
			break;
		}
		return super.onTouchEvent(event);
	}

	// 触摸分发
	@Override
	public boolean dispatchTouchEvent(MotionEvent event) {
		switch (event.getAction()) {
		case MotionEvent.ACTION_DOWN:
			Log.e(TAG, "dispatchTouchEvent---ACTION_DOWN");
			break;

		case MotionEvent.ACTION_MOVE:
			Log.e(TAG, "dispatchTouchEvent---ACTION_MOVE");
			break;
		case MotionEvent.ACTION_CANCEL:
			Log.e(TAG, "dispatchTouchEvent---ACTION_CANCEL");
			break;
		case MotionEvent.ACTION_UP:
			Log.e(TAG, "dispatchTouchEvent---ACTION_UP");
			break;
		}

		return super.dispatchTouchEvent(event);
	}

}

然后看下Activity里面的代码:

public class SingleView extends Activity {
	private String TAG = "SingleView";

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_single_view);
	}

	@Override
	public boolean dispatchTouchEvent(MotionEvent ev) {
		switch (ev.getAction()) {
		case MotionEvent.ACTION_DOWN:
			Log.e(TAG, "dispatchTouchEvent---ACTION_DOWN");
			break;

		case MotionEvent.ACTION_MOVE:
			Log.e(TAG, "dispatchTouchEvent---ACTION_MOVE");
			break;
		case MotionEvent.ACTION_CANCEL:
			Log.e(TAG, "dispatchTouchEvent---ACTION_CANCEL");
			break;
		case MotionEvent.ACTION_UP:
			Log.e(TAG, "dispatchTouchEvent---ACTION_UP");
			break;
		}
		return super.dispatchTouchEvent(ev);
	}

	@Override
	public boolean onTouchEvent(MotionEvent event) {
		switch (event.getAction()) {
		case MotionEvent.ACTION_DOWN:
			Log.e(TAG, "onTouchEvent---ACTION_DOWN");
			break;

		case MotionEvent.ACTION_MOVE:
			Log.e(TAG, "onTouchEvent---ACTION_MOVE");
			break;
		case MotionEvent.ACTION_CANCEL:
			Log.e(TAG, "onTouchEvent---ACTION_CANCEL");
			break;
		case MotionEvent.ACTION_UP:
			Log.e(TAG, "onTouchEvent---ACTION_UP");
			break;
		}
		return super.onTouchEvent(event);
	}

}
Activity布局文件的代码:

<com.xinxue.androidevents.views.OnlyButton 
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="测试我" />


可以看到Activity界面只有一个自定义View,我们在自定义里面重写了两个事件处理的方法,同样的我们也重写了Activity的事件处理方法,下面是运行结果:

其中的singleview是我们的Activity,onlyButton是我们的自定义View,这些是系统默认的事件处理逻辑。上面提到的处理事件3个方法的返回值都是返回一个Boolean类型的值,dispatch的Boolean值代码是否给子View分发事件,intercept的Boolean代表是否拦截这次事件,touchevent的Boolean值代表是否消费这次事件。ok,知道了这些我们就来用自己的逻辑处理点击事件,我们发现上面的逻辑没有调用到Activity的onTouchevent方法,下面我们把onlyButton的onTouchEvent方法的返回值改为false,意思就是告诉Activity触摸事件没有被处理,那么Activity肯定会自己处理这次事件,我们看看是不是我们猜想的这样呢??看运行结果:

我们发现果真是这个逻辑,我们把代码改回去,onlyButton的dispatch方法返回值改为false,表示不给自己分发这次事件,按上面的逻辑,应该不会再调用onTouchEvent方法了,我们来看看是不是这样的呢??看运行结果:

不但结果是我们猜想的那样,而且我们发现在actionup事件触发的时候都没有再调用onlybutton的dispatch方法了,直接自己处理了,这样我们我们猜想:如果dispatch事件返回false,以后的事件处理都不会再调用这个View的dispatch方法,直接在父容器中就消费掉。后面我们将从含有ViewGroup的方法中来印证这个结论。

下面来一个疯狂的举动,都知道dispatch返回false是不再给子View分发事件,而Activity是整个UI的最上级,那么我们给它的dispatch返回false是不是就可以达到点击屏幕没有反应了呢??我们来看下运行效果:

哎呀,果真如我们想的那样,运行了这么多种情况,你是不是对触摸事件由所领悟了呢??不要急,下面我们来看另一种情况。

情况二、含有ViewGroup的情况

这里自定义一个View继承自Linearlayout,Activity里面的代码不变,布局文件如下:

<com.xinxue.androidevents.views.ViewGroupAndView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:background="#f00" >

    <com.xinxue.androidevents.views.OnlyButton
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="按钮" />

</com.xinxue.androidevents.views.ViewGroupAndView>

里面包含了情况一定义的View,下面来看看viewGroup里面的代码:

public class ViewGroupAndView extends LinearLayout {
	public ViewGroupAndView(Context context, AttributeSet attrs) {
		super(context, attrs);
	}

	private String TAG = "ViewGroupAndView";

	public ViewGroupAndView(Context context) {
		super(context);
	}

	// 触摸消费
	@Override
	public boolean onTouchEvent(MotionEvent event) {
		switch (event.getAction()) {
		case MotionEvent.ACTION_DOWN:
			Log.e(TAG, "onTouchEvent---ACTION_DOWN");
			break;

		case MotionEvent.ACTION_MOVE:
			Log.e(TAG, "onTouchEvent---ACTION_MOVE");
			break;
		case MotionEvent.ACTION_CANCEL:
			Log.e(TAG, "onTouchEvent---ACTION_CANCEL");
			break;
		case MotionEvent.ACTION_UP:
			Log.e(TAG, "onTouchEvent---ACTION_UP");
			break;
		}
		return super.onTouchEvent(event);
	}

	// 触摸分发
	@Override
	public boolean dispatchTouchEvent(MotionEvent event) {
		switch (event.getAction()) {
		case MotionEvent.ACTION_DOWN:
			Log.e(TAG, "dispatchTouchEvent---ACTION_DOWN");
			break;

		case MotionEvent.ACTION_MOVE:
			Log.e(TAG, "dispatchTouchEvent---ACTION_MOVE");
			break;
		case MotionEvent.ACTION_CANCEL:
			Log.e(TAG, "dispatchTouchEvent---ACTION_CANCEL");
			break;
		case MotionEvent.ACTION_UP:
			Log.e(TAG, "dispatchTouchEvent---ACTION_UP");
			break;
		}

		return super.dispatchTouchEvent(event);
	}

	// 拦截方法
	@Override
	public boolean onInterceptTouchEvent(MotionEvent ev) {
		switch (ev.getAction()) {
		case MotionEvent.ACTION_DOWN:
			Log.e(TAG, "onInterceptTouchEvent---ACTION_DOWN");
			break;

		case MotionEvent.ACTION_MOVE:
			Log.e(TAG, "onInterceptTouchEvent---ACTION_MOVE");
			break;
		case MotionEvent.ACTION_CANCEL:
			Log.e(TAG, "onInterceptTouchEvent---ACTION_CANCEL");
			break;
		case MotionEvent.ACTION_UP:
			Log.e(TAG, "onInterceptTouchEvent---ACTION_UP");
			break;
		}
		return super.onInterceptTouchEvent(ev);
	}
}
我们重写了一个intercept方法,下面来看看默认的运行逻辑:

其中multiview为Activity,viewGroupandView为定义的ViewGroup,我们看到先调用的是Activity的dispatch方法,然后是viewgroup的dispatch方法,而ViewGroup有一个是否拦截事件的方法,用来处理是否给自己的孩子分发事件,ViewGroup首先会调用自己的这个方法用来判断是否给自己的孩子分发,默认是给的(这算不算一种伟大的父爱呢?),调用孩子的dispatch方法之后,孩子自己就会判断我是否有能力处理这个事件,我们发现孩子虽然没有处理这个事件(没有返回true),但是这个事件却不再往上发了,直接就消失了!!这样我们就可以得出事件传到最底层的View中,默认就不会往上传递了,如果我们在这个View返回false,告诉他自己不能处理,情况又是怎么样的呢?会不会给Activity去处理呢??看运行结果:

我们发现也没有往上传,这次事件是真的消失了!!!那么如果view处理了,也就是返回了true,会不会告诉自己的父亲呢?我们来试验一下:


好吧,我们该下结论了:不管最底层的view处不处理本次事件,这次事件都会消失掉!!

照着我们现在的逻辑,是不是ViewGroup的intercept返回true,表示拦截这次事件,自己的孩子就收不到本次事件了呢??来看结果:

啊哈,果真如我们想的那样~~~~~~~~·利用这个我们就可以屏蔽子类的触摸事件啦!等等~仔细看上面的结果有没有发现一个问题,对!如果你不给自己的孩子分发,那么下次我你也收不到触摸事件了(父亲必须照顾自己的孩子!)!!!这样的设计是不是很有意思呢~~~~

 

ok,两种情况就介绍完了,还有一个就是点击事件和触摸事件一块的时候是怎么样的。其实吧,点击事件是在onTouchEvent里面调用的,也就是说点击事件是在触摸事件的后面才被触发的,点击事件能否触摸还要看触摸事件是否返回true,如果为true就不再触发了!


 扫描关注我的微信公众号:


总结:

对于触摸事件分两种情况,一种是只有View的一种是含有ViewGroup的,如果含有ViewGroup的话,事件传递到最里面的view就不会再往上传了,也就是说不管最底层的view是否有能力处理这个事件,本次事件都会消失掉,我们要拦截子类的触摸事件只需要给父类的intercept返回true就可以了,但是如果返回了true下次就不会再收到触摸事件。其实对于触摸事件,我们只需要知道那3个方法是干嘛的,返回值代表什么意思,就非常容易弄清楚这块知识点,ok,今天的内容就说到这里吧,如果文中你们发现有什么问题,欢迎给我留言指正!!!



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值