当前位置: 代码迷 >> Android >> Android onTouchEvent跟onInterceptTouchEvent事件分发详解(三)
  详细解决方案

Android onTouchEvent跟onInterceptTouchEvent事件分发详解(三)

热度:289   发布时间:2016-04-24 11:05:28.0
Android onTouchEvent和onInterceptTouchEvent事件分发详解(三)

尊重原创,转载请标明出处    http://blog.csdn.net/abcdef314159

紧接前一篇几个问题的验证,在看之前最好把上一篇的 Android onTouchEvent和onInterceptTouchEvent事件分发详解(二)先看一下。

在上一篇我们根据源码分析了Android事件的分发机制,在最后总结了几个问题,在这一篇我们将为大家逐一验证。

总共有3个类,一个Activity,一个ViewGroup,一个View

(1)在Activity中的代码为

	@Override	public boolean dispatchTouchEvent(MotionEvent ev) {		Log.d("wld",				"DispatchActivity_dispatchTouchEvent" + "_"						+ MotionEvent.actionToString(ev.getAction()));		return super.dispatchTouchEvent(ev);	}	@Override	public boolean onTouchEvent(MotionEvent event) {		Log.d("wld",				"DispatchActivity_onTouchEvent" + "_"						+ MotionEvent.actionToString(event.getAction()));		return super.onTouchEvent(event);	}
ViewGroup中
	@Override	public boolean dispatchTouchEvent(MotionEvent ev) {		Log.d("wld", "LinearLayoutParent_dispatchTouchEvent" + "_"				+ MotionEvent.actionToString(ev.getAction()));		return super.dispatchTouchEvent(ev);	}	@Override	public boolean onTouchEvent(MotionEvent event) {		Log.d("wld",				"LinearLayoutParent_onTouchEvent" + "_"						+ MotionEvent.actionToString(event.getAction()));		return super.onTouchEvent(event);	}	@Override	public boolean onInterceptTouchEvent(MotionEvent ev) {		Log.d("wld", "LinearLayoutParent_onInterceptTouchEvent" + "_"				+ MotionEvent.actionToString(ev.getAction()));		return super.onInterceptTouchEvent(ev);	}
View中的代码(View为TextView)
	@Override	public boolean dispatchTouchEvent(MotionEvent ev) {		Log.d("wld",				"TextViewChild_dispatchTouchEvent" + "_"						+ MotionEvent.actionToString(ev.getAction()));		return super.dispatchTouchEvent(ev);	}	@Override	public boolean onTouchEvent(MotionEvent event) {		Log.d("wld",				"TextViewChild_onTouchEvent" + "_"						+ MotionEvent.actionToString(event.getAction()));		return super.onTouchEvent(event);	}
我们运行一下,看一下结果


我们知道TextView默认是不消耗事件的,我们看一下打印的log


DOWN和UP只会触发一次,MOVE可能会触发很多次,我们知道就行了,从上面可以看到,默认情况下TextView是不消耗事件的,只是触发了DOWN事件,后面的MOVE,UP都没有触发,且传递顺序是从Activity的dispatchTouchEvent→ViewGroup的dispatchTouchEvent→ViewGroup的onInterceptTouchEvent→View的dispatchTouchEvent→View的onTouchEvent(返回false往上抛)→ViewGroup的onTouchEvent(返回false表示事件没消耗,往上抛)→Activity的onTouchEvent(处理了事件,执行了MOVE,UP事件),此后会一直交给Activity处理,不在往下分发。

(2)我们让TextView中的onTouchEvent方法返回为true,在看一下执行结果

	@Override	public boolean onTouchEvent(MotionEvent event) {		Log.d("wld",				"TextViewChild_onTouchEvent" + "_"						+ MotionEvent.actionToString(event.getAction()));		super.onTouchEvent(event);		return true;	}

打印log如下

我们看到TextView的onTouchEvent消耗了事件(执行了DOWM,MOVE,UP事件),就不会再往上抛,所以ViewGroup的onTouchEvent和Activity的onTouchEvent就不会再触发,此后所有的事件都会交由它来处理。

(3)我们在看另外一种情况,把上面TextView的onTouchEvent返回默认值(false)让ViewGroup的onTouchEvent放回true,

	@Override	public boolean onTouchEvent(MotionEvent event) {		Log.d("wld",				"LinearLayoutParent_onTouchEvent" + "_"						+ MotionEvent.actionToString(event.getAction()));		super.onTouchEvent(event);		return true;	}

我们看一下打印的log


我们看到TextView只执行了DOWM事件,因为返回的是false,表示事件没有被消耗,就会往上抛,交给ViewGroup,但ViewGroup的onTouchEvent返回的是true,表示事件被他消耗了,就不会再往上抛,所以就执行了后面的MOVE,UP事件,此后的一系列事件也不会再往下传递了,每次传递到ViewGroup的时候就直接调用它的onTouchEvent方法。

(4)我们在(3)的基础上,让onInterceptTouchEvent返回true,在看一下

	@Override	public boolean onInterceptTouchEvent(MotionEvent ev) {		Log.d("wld", "LinearLayoutParent_onInterceptTouchEvent" + "_"				+ MotionEvent.actionToString(ev.getAction()));		super.onInterceptTouchEvent(ev);		return true;	}

我们看一下打印的log


我们看到TextView的方法根本就没有执行,这时TextView的方法无论放回true还是false都没有任何影响,因为ViewGroup的onInterceptTouchEvent方法返回true,表示事件被他拦截,就不会再往下传递,我们还看到ViewGroup的onInterceptTouchEvent方法只执行了一次,因为已经被拦截了,后续的一系列事件就都会交给他,就不需要在拦截了,因为ViewGroup的onTouchEvent返回true,事件被他消耗,就不在往上抛。

(5)我们在(4)的基础上,让onTouchEvent返回false,在看一下打印的log

因为ViewGroup拦截,所以没有传递到TextView,因为ViewGroup没有消耗事件,所以往上抛到Activity,所以事件最终被Activity给处理了(执行了MOVE,UP事件)

(6)关于requestDisallowInterceptTouchEvent事件,这里我们再来研究一下,我们让所有的都返回默认,然后让ViewGroup的onInterceptTouchEvent方法返回true(表示拦截,默认情况下就不会往下传递),我们让TextView的onTouchEvent方法返回true(表示事件被他消耗),然后改写TextView的dispatchTouchEvent方法

	@Override	public boolean dispatchTouchEvent(MotionEvent ev) {		getParent().requestDisallowInterceptTouchEvent(true);		Log.d("wld",				"TextViewChild_dispatchTouchEvent" + "_"						+ MotionEvent.actionToString(ev.getAction()));		return super.dispatchTouchEvent(ev);	}

我们看一下log


发现很奇葩的事,TextView的方法并没有被调用,我们看一下源码就会发现,在ViewGroup的dispatchTouchEvent方法中有这样一段代码

            if (actionMasked == MotionEvent.ACTION_DOWN) {                // Throw away all previous state when starting a new touch gesture.                // The framework may have dropped the up or cancel event for the previous gesture                // due to an app switch, ANR, or some other state change.                cancelAndClearTouchTargets(ev);                resetTouchState();            }

表示在按下的时候调用了resetTouchState方法,我们再看一下这个方法

    private void resetTouchState() {        clearTouchTargets();        resetCancelNextUpFlag(this);        mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;    }

看到没,在最后一行把FLAG_DISALLOW_INTERCEPT标记清除了,就是说每次点击的时候,执行DOWN事件的时候就会把它清除,由于后面的onInterceptTouchEvent返回true,所以TextView的方法根本就执行不了,我们把ViewGroup的onInterceptTouchEvent方法改一下

	@Override	public boolean onInterceptTouchEvent(MotionEvent ev) {		Log.d("wld", "LinearLayoutParent_onInterceptTouchEvent" + "_"				+ MotionEvent.actionToString(ev.getAction()));		// super.onInterceptTouchEvent(ev);		if (ev.getAction() == MotionEvent.ACTION_DOWN)			return false;		return true;	}

然后再看一下log


我们看到TextView的所有方法都被执行了,虽然在ViewGroup的onInterceptTouchEvent方法中除了DOWN以外都被拦截了,但我们还是能看到TextView消耗了事件,因为在我们在TextView的dispatchTouchEvent方法中调用了requestDisallowInterceptTouchEvent方法(不让父类拦截事件),我们把TextView中的requestDisallowInterceptTouchEvent方法去掉再看一下


我们看到TextView只执行了DOWN和CANCEL事件,MOVE和UP事件都被ViewGroup拦截了,由于没有消耗,直接往上抛,交给Activity处理。且ViewGroup的onInterceptTouchEvent方法在UP事件的时候也没有执行,因为在执行MOVE的时候就已经返回了True,所以后面的UP事件就不会再触发。

OK,到目前为止,Activity的事件拦截机制就分析完了。




  相关解决方案