android事件分发机制详解
2016-05-22 23:01
495 查看
先讲View:
只要你触摸到了任何一个控件,就一定会调用该控件的dispatchTouchEvent方法,看下该函数的实现:
总结一下,总的调用顺序:
dispatchTouchEvent-->onTouch()[需要返回false才会继续往下]-->onTouchEvent()[需要控件是clickable才会继续往下走]-->onClick()。
再看看ViewGroup的事件分发,ViewGroup是什么呢?
ViewGroup就是一组View的集合,它包含很多的子View和子VewGroup,是Android中所有布局的父类或间接父类,像LinearLayout、RelativeLayout等都是继承自ViewGroup的。但ViewGroup实际上也是一个View,只不过比起View,它多了可以包含子View和定义布局参数的功能。
面前说点击某一个控件,会调用到该控件的dispatchTouchEvent方法,实际是这样的:当你点击了某个控件,首先会去调用该控件所在布局的dispatchTouchEvent方法,然后在布局的dispatchTouchEvent方法中找到被点击的相应控件,再去调用该控件的dispatchTouchEvent方法。
下面来看下ViewGroup的dispatchTouchEvent()方法的实现吧:
看一个例子:
该例子中,MyLayout布局里面包含了一个button1,并且为myLayout和button1都添加了OnTouchListener,如果我们重写的onInterceptTouchEvent返回true,那么touch事件就被myLayout拦截了,不会传到button1上面,反之会则会传到button1上面,当然点击空白区域还是会被myLayout捕获的。
下面用一张图来总结下这个流程:
在解决事件冲突中的运用:
比如scrollview嵌套viewpager导致的滑动冲突
有两种解决方法:
1、重写scrollview类的onInterceptTouchEvent方法,如果发现是水平滑动的话,那就在该方法内返回false,这样就不拦截viewpager了,会执行viewpager的onTouchEvent方法;
2、重写viewpager中的onTouchEvent方法,如果发现水平滑动就设置getParent().requestDisallowInterceptTouchEvent(true)来通知父控件不要拦截事件,如果是竖直滑动就设置为false。
只要你触摸到了任何一个控件,就一定会调用该控件的dispatchTouchEvent方法,看下该函数的实现:
public boolean dispatchTouchEvent(MotionEvent event) { if (mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED && mOnTouchListener.onTouch(this, event)) { return true; } return onTouchEvent(event); }可以看出一个Enable的控件设置了OnTouchListener,如果在onTouch()里面返回了true,那么触碰事件就被onTouch()“消费”掉了,不会走到onTouchEvent();如果返回false,那么就会往下走,这样到了onTouchEvent()方法里面,其实现如下:
public boolean onTouchEvent(MotionEvent event) { ... if (((viewFlags & CLICKABLE) == CLICKABLE || (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)) { switch (event.getAction()) { case MotionEvent.ACTION_UP: ... performClick(); break; case MotionEvent.ACTION_DOWN: break; case MotionEvent.ACTION_CANCEL: break; case MotionEvent.ACTION_MOVE: break; } return true; } return false; }可以看到,如果一个控件是clickable的,那么就会走到ACTION_UP,ACTION_DOWN等case里面,并且最终返回true。需要说明的是:如果在上一个case(比如:ACTION_UP)返回了false,那么其下面所有的case(比如:ACTION_CANCEL、ACTION_MOVE等)都不会得到执行。在ACTION_UP下面调用了performClick()方法,其实现如下:
public boolean performClick() { sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED); if (mOnClickListener != null) { playSoundEffect(SoundEffectConstants.CLICK); mOnClickListener.onClick(this); return true; } return false; }在这里我们就知道了为什么点击一个按钮会执行onClick()方法。
总结一下,总的调用顺序:
dispatchTouchEvent-->onTouch()[需要返回false才会继续往下]-->onTouchEvent()[需要控件是clickable才会继续往下走]-->onClick()。
再看看ViewGroup的事件分发,ViewGroup是什么呢?
ViewGroup就是一组View的集合,它包含很多的子View和子VewGroup,是Android中所有布局的父类或间接父类,像LinearLayout、RelativeLayout等都是继承自ViewGroup的。但ViewGroup实际上也是一个View,只不过比起View,它多了可以包含子View和定义布局参数的功能。
面前说点击某一个控件,会调用到该控件的dispatchTouchEvent方法,实际是这样的:当你点击了某个控件,首先会去调用该控件所在布局的dispatchTouchEvent方法,然后在布局的dispatchTouchEvent方法中找到被点击的相应控件,再去调用该控件的dispatchTouchEvent方法。
下面来看下ViewGroup的dispatchTouchEvent()方法的实现吧:
public boolean dispatchTouchEvent(MotionEvent ev) { ... if (disallowIntercept || !onInterceptTouchEvent(ev)) { final int count = mChildrenCount; for (int i = count - 1; i >= 0; i--) { final View child = children[i]; ... if (child.dispatchTouchEvent(ev)) { mMotionTarget = child; return true; } } } ... if (target == null) { ... return super.dispatchTouchEvent(ev); } ... return target.dispatchTouchEvent(ev); }可以看到里面调用了一个函数onInterceptTouchEvent(),这个是什么东东呢?看下实现:
public boolean onInterceptTouchEvent(MotionEvent ev) { return false; }该函数就是说,父视图是否拦截子视图的touch事件,返回false,说明默认是不拦截的。由于此处返回是的false,所以会走到子view的dispatchTouchEvent()方法,这个方法上面我们详细分析过了。如果点击的是父视图的空白区域,或是onInterceptTouchEvent返回true,那么就会走到第17行,这里的super当然就View啦,就又回到了上面我们讲过的View的分发流程。
看一个例子:
public class MyLayout extends LinearLayout { public MyLayout(Context context, AttributeSet attrs) { super(context, attrs); } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { return true; } } myLayout.setOnTouchListener(new OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { Log.d("TAG", "myLayout on touch"); return false; } }); button1.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { Log.d("TAG", "You clicked button1"); } });
该例子中,MyLayout布局里面包含了一个button1,并且为myLayout和button1都添加了OnTouchListener,如果我们重写的onInterceptTouchEvent返回true,那么touch事件就被myLayout拦截了,不会传到button1上面,反之会则会传到button1上面,当然点击空白区域还是会被myLayout捕获的。
下面用一张图来总结下这个流程:
在解决事件冲突中的运用:
比如scrollview嵌套viewpager导致的滑动冲突
有两种解决方法:
1、重写scrollview类的onInterceptTouchEvent方法,如果发现是水平滑动的话,那就在该方法内返回false,这样就不拦截viewpager了,会执行viewpager的onTouchEvent方法;
2、重写viewpager中的onTouchEvent方法,如果发现水平滑动就设置getParent().requestDisallowInterceptTouchEvent(true)来通知父控件不要拦截事件,如果是竖直滑动就设置为false。
相关文章推荐
- 自学Android到什么程度才能找到工作?
- MD5加密
- 《深入理解Android内核设计思想》学习笔记
- Android基础之打造自己的魔法万能适配器
- 实现基于Android的英文电子词典
- Android键盘处理-监听键盘状态并实现QQ拉出式弹出回复框。
- windows电脑使用Android studio添加依赖
- md5密码加盐
- Android HelloWorld
- android 使用Intent传递对象 Serializable 或者 Parcelabel
- 5分钟实现Android中更换头像功能
- Android 如何全局获取Context
- Android 收发短信 (8.2)
- android 获取屏幕高度和宽度 的方法
- Android亮屏和熄屏控制
- android中的动画
- Android开发项目之一:打招呼
- android studio 提示 “Gradle project sync failed”
- 命令模式在Android实际场景中运用
- 国内使用东软镜像更新Android SDK很方便