一、View的dispatchTouchEvent和onTouchEvent
找到这个判断:
onTouchEvent的源码比较多,贴最重要的:
可以看到有个performClick(),它的源码里有这么一句 li.mOnClickListener.onClick(this);
终于对上了,它执行了我们注册的onClick监听。当然执行前会经过一系列判断,是否注册了监听等。 总结:
在Activity里注册两个监听:
同时复写Activity的dispatch方法和onTouchEvent方法:
最终一次点击,打印信息如下:
事件流程为:
二、ViewGroup的dispatchTouchEvent、onInterceptTouchEvent、onTouchEvent
探讨Android事件传递机制前,明确android的两大基础控件类型:View和ViewGroup。View即普通的控件,没有子布局的,如Button、TextView. ViewGroup继承自View,表示可以有子控件,如Linearlayout、Listview这些。而事件即MotionEvent,最重要的有3个:
(1)MotionEvent.ACTION_DOWN 按下View,是所有事件的开始
(2)MotionEvent.ACTION_MOVE 滑动事件
(3)MotionEvent.ACTION_UP 与down对应,表示抬起
另外,明确事件传递机制的最终目的都是为了触发执行View的点击监听和触摸监听:
******.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // TODO Auto-generated method stub Log.i(tag, "testLinelayout---onClick..."); } });
*******.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { // TODO Auto-generated method stub return false; } });
我们简称为onClick监听和onTouch监听,一般程序会注册这两个监听。从上面可以看到,onTouch监听里默认return false。不要小看了这个return false,后面可以看到它有大用。
对于View来说,事件传递机制有两个函数:dispatchTouchEvent负责分发事件,在dispatch***里又会调用onTouchEvent表示执行事件,或者说消费事件,结合源码分析其流程。事件传递的入口是View的dispatchTouchEvent()函数:
- /**
- * Pass the touch screen motion event down to the target view, or this
- * view if it is the target.
- *
- * @param event The motion event to be dispatched.
- * @return True if the event was handled by the view, false otherwise.
- */
- public boolean dispatchTouchEvent(MotionEvent event) {
- if (mInputEventConsistencyVerifier != null) {
- mInputEventConsistencyVerifier.onTouchEvent(event, 0);
- }
- if (onFilterTouchEventForSecurity(event)) {
- //noinspection SimplifiableIfStatement
- ListenerInfo li = mListenerInfo;
- if (li != null && li.mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED
- && li.mOnTouchListener.onTouch(this, event)) {
- return true;
- }
- if (onTouchEvent(event)) {
- return true;
- }
- }
- if (mInputEventConsistencyVerifier != null) {
- mInputEventConsistencyVerifier.onUnhandledEvent(event, 0);
- }
- return false;
- }
if (li != null && li.mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED && li.mOnTouchListener.onTouch(this, event)) { return true; }
他会执行View的OnTouchListener.onTouch这个函数,也就是上面说的onTouch监听。里面有三个判断,如果三个都为1,就会执行return true,不往下走了。而默认的onTouch监听返回false,只要一个是false,就不会返回true。接着往下看,程序执行onTouchEvent:
- if (onTouchEvent(event)) {
- return true;
- }
- if (!mHasPerformedLongPress) {
- // This is a tap, so remove the longpress check
- removeLongPressCallback();
- // Only perform take click actions if we were in the pressed state
- if (!focusTaken) {
- // Use a Runnable and post this rather than calling
- // performClick directly. This lets other visual state
- // of the view update before click actions start.
- if (mPerformClick == null) {
- mPerformClick = new PerformClick();
- }
- if (!post(mPerformClick)) {
- performClick();
- }
- }
- }
- public boolean performClick() {
- sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
- ListenerInfo li = mListenerInfo;
- if (li != null && li.mOnClickListener != null) {
- playSoundEffect(SoundEffectConstants.CLICK);
- li.mOnClickListener.onClick(this);
- return true;
- }
- return false;
- }
1、事件入口是dispatchTouchEvent(),它会先执行注册的onTouch监听,如果一切顺利的话,接着执行onTouchEvent,在onTouchEvent里会执行onClick监听。
2、无论是dispatchTouchEvent还是onTouchEvent,如果返回true表示这个事件已经被消费、处理了,不再往下传了。在dispathTouchEvent的源码里可以看到,如果onTouchEvent返回了true,那么它也返回true。如果dispatch***在执行onTouch监听的时候,onTouch返回了true,那么它也返回true,这个事件提前被onTouch消费掉了。就不再执行onTouchEvent了,更别说onClick监听了。
3、我们通常在onTouch监听了设置图片一旦被触摸就改变它的背景、透明度之类的,这个onTouch表示事件的时机。而在onClick监听了去具体干某些事。
下面通过代码来说明,自定义一个TestButton继承自Button,重写它的dispath***和onTouchEvent方法,为了简单只关注down和up事件。
- package org.yanzi.ui;
- import android.content.Context;
- import android.util.AttributeSet;
- import android.util.Log;
- import android.view.MotionEvent;
- import android.widget.Button;
- public class TestButton extends Button {
- private final static String tag = "yan";
- public TestButton(Context context, AttributeSet attrs) {
- super(context, attrs);
- // TODO Auto-generated constructor stub
- }
- @Override
- public boolean onTouchEvent(MotionEvent event) {
- // TODO Auto-generated method stub
- switch(event.getAction()){
- case MotionEvent.ACTION_DOWN:
- Log.i(tag, "TestButton-onTouchEvent-ACTION_DOWN...");
- break;
- case MotionEvent.ACTION_UP:
- Log.i(tag, "TestButton-onTouchEvent-ACTION_UP...");
- break;
- default:break;
- }
- return super.onTouchEvent(event);
- }
- @Override
- public boolean dispatchTouchEvent(MotionEvent event) {
- // TODO Auto-generated method stub
- switch(event.getAction()){
- case MotionEvent.ACTION_DOWN:
- Log.i(tag, "TestButton-dispatchTouchEvent-ACTION_DOWN...");
- break;
- case MotionEvent.ACTION_UP:
- Log.i(tag, "TestButton-dispatchTouchEvent-ACTION_UP...");
- break;
- default:break;
- }
- return super.dispatchTouchEvent(event);
- }
- }
- testBtn.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- // TODO Auto-generated method stub
- Log.i(tag, "testBtn---onClick...");
- }
- });
- testBtn.setOnTouchListener(new View.OnTouchListener() {
- @Override
- public boolean onTouch(View v, MotionEvent event) {
- // TODO Auto-generated method stub
- switch(event.getAction()){
- case MotionEvent.ACTION_DOWN:
- Log.i(tag, "testBtn-onTouch-ACTION_DOWN...");
- break;
- case MotionEvent.ACTION_UP:
- Log.i(tag, "testBtn-onTouch-ACTION_UP...");
- break;
- default:break;
- }
- return false;
- }
- });
- @Override
- public boolean dispatchTouchEvent(MotionEvent ev) {
- // TODO Auto-generated method stub
- switch(ev.getAction()){
- case MotionEvent.ACTION_DOWN:
- Log.i(tag, "MainActivity-dispatchTouchEvent-ACTION_DOWN...");
- break;
- case MotionEvent.ACTION_UP:
- Log.i(tag, "MainActivity-dispatchTouchEvent-ACTION_UP...");
- break;
- default:break;
- }
- return super.dispatchTouchEvent(ev);
- }
- @Override
- public boolean onTouchEvent(MotionEvent event) {
- // TODO Auto-generated method stub
- switch(event.getAction()){
- case MotionEvent.ACTION_DOWN:
- Log.i(tag, "MainActivity-onTouchEvent-ACTION_DOWN...");
- break;
- case MotionEvent.ACTION_UP:
- Log.i(tag, "MainActivity-onTouchEvent-ACTION_UP...");
- break;
- default:break;
- }
- return super.onTouchEvent(event);
- }
Line 33: 01-08 14:59:45.847 I/yan ( 4613): MainActivity-dispatchTouchEvent-ACTION_DOWN... Line 35: 01-08 14:59:45.849 I/yan ( 4613): TestButton-dispatchTouchEvent-ACTION_DOWN... Line 37: 01-08 14:59:45.849 I/yan ( 4613): testBtn-onTouch-ACTION_DOWN... Line 39: 01-08 14:59:45.849 I/yan ( 4613): TestButton-onTouchEvent-ACTION_DOWN... Line 41: 01-08 14:59:45.939 I/yan ( 4613): MainActivity-dispatchTouchEvent-ACTION_UP... Line 43: 01-08 14:59:45.941 I/yan ( 4613): TestButton-dispatchTouchEvent-ACTION_UP... Line 45: 01-08 14:59:45.944 I/yan ( 4613): testBtn-onTouch-ACTION_UP... Line 47: 01-08 14:59:45.946 I/yan ( 4613): TestButton-onTouchEvent-ACTION_UP... Line 49: 01-08 14:59:45.974 I/yan ( 4613): testBtn---onClick...
事件先由Activity的dispatchTouchEvent进行分发,然后TestButton的dispatchTouchEvent进行分发,接着执行onTouch监听,然后执行onTouchEvent。第二次UP动作的时候,在onTouchEvent里又执行了onClick监听。
如果我们想这个TestButton只能执行onTouch监听不能执行onClick监听,方法有很多。在onTouch监听里默认返回false改为true,如下:
- testBtn.setOnTouchListener(new View.OnTouchListener() {
- @Override
- public boolean onTouch(View v, MotionEvent event) {
- // TODO Auto-generated method stub
- switch(event.getAction()){
- case MotionEvent.ACTION_DOWN:
- Log.i(tag, "testBtn-onTouch-ACTION_DOWN...");
- break;
- case MotionEvent.ACTION_UP:
- Log.i(tag, "testBtn-onTouch-ACTION_UP...");
- break;
- default:break;
- }
- return true;
- }
- });
Line 75: 01-08 15:05:51.627 I/yan ( 5262): MainActivity-dispatchTouchEvent-ACTION_DOWN... Line 77: 01-08 15:05:51.628 I/yan ( 5262): TestButton-dispatchTouchEvent-ACTION_DOWN... Line 79: 01-08 15:05:51.629 I/yan ( 5262): testBtn-onTouch-ACTION_DOWN... Line 81: 01-08 15:05:51.689 I/yan ( 5262): MainActivity-dispatchTouchEvent-ACTION_UP... Line 83: 01-08 15:05:51.691 I/yan ( 5262): TestButton-dispatchTouchEvent-ACTION_UP... Line 85: 01-08 15:05:51.695 I/yan ( 5262): testBtn-onTouch-ACTION_UP...
可以看到压根就没执行onTouchEvent。因为onTouch返回了true,已提前将这个事件消费了,就不往下传了,dispatch流程提前终止。
再来看ViewGroup,在复写ViewGroup时可以发现它的onTouchEvent在在View里的,表示这两个方法是一样的。但dispatchTouchEvent是在ViewGroup里的,表示和View的dispatchTouchEvent不一样,多了一个onInterceptTouchEvent函数,表示拦截的意思。链接 打个很形象的比喻,这玩意就像个秘书、谋士。为啥View没有呢,因为它级别不够,一个Button里面是不可能有子View的。但LinearLayout(继承ViewGroup)就有孩子(子布局),这个onInterceptTouchEvent就会判断事件要不要通知它的孩子呢。它的源码如下:
- public boolean dispatchTouchEvent(MotionEvent ev) {
- if (mInputEventConsistencyVerifier != null) {
- mInputEventConsistencyVerifier.onTouchEvent(ev, 1);
- }
- boolean handled = false;
- if (onFilterTouchEventForSecurity(ev)) {
- final int action = ev.getAction();
- final int actionMasked = action & MotionEvent.ACTION_MASK;
- // Handle an initial down.
- 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();
- }
- // Check for interception.
- final boolean intercepted;
- if (actionMasked == MotionEvent.ACTION_DOWN
- || mFirstTouchTarget != null) {
- final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
- if (!disallowIntercept) {
- intercepted = onInterceptTouchEvent(ev);
- ev.setAction(action); // restore action in case it was changed
- } else {
- intercepted = false;
- }
- } else {
- // There are no touch targets and this action is not an initial down
- // so this view group continues to intercept touches.
- intercepted = true;
- }
- // Check for cancelation.
- final boolean canceled = resetCancelNextUpFlag(this)
- || actionMasked == MotionEvent.ACTION_CANCEL;
- // Update list of touch targets for pointer down, if needed.
- final boolean split = (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) != 0;
- TouchTarget newTouchTarget = null;
- boolean alreadyDispatchedToNewTouchTarget = false;
- if (!canceled && !intercepted) {
- if (actionMasked == MotionEvent.ACTION_DOWN
- || (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)
- || actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
- final int actionIndex = ev.getActionIndex(); // always 0 for down
- final int idBitsToAssign = split ? 1
关注打赏
最近更新
- 深拷贝和浅拷贝的区别(重点)
- 【Vue】走进Vue框架世界
- 【云服务器】项目部署—搭建网站—vue电商后台管理系统
- 【React介绍】 一文带你深入React
- 【React】React组件实例的三大属性之state,props,refs(你学废了吗)
- 【脚手架VueCLI】从零开始,创建一个VUE项目
- 【React】深入理解React组件生命周期----图文详解(含代码)
- 【React】DOM的Diffing算法是什么?以及DOM中key的作用----经典面试题
- 【React】1_使用React脚手架创建项目步骤--------详解(含项目结构说明)
- 【React】2_如何使用react脚手架写一个简单的页面?
立即登录/注册


微信扫码登录