深入浅出 Android Touch 事件分发拦截相应机制
2015-08-20 11:56
651 查看
主要讲ViewGroup的 Touch机制
原创地址:/article/1973052.html欢迎大家访问我的Github开源库,这里有好玩的App源码,想和大家分享。https://github.com/ChoicesWang
Android中的Touch分为三个层面。Activity层,ViewGroup层,View层。今天主要讲ViewGroup。
先看下面的:
[code]public class CusFrameLayout extends FrameLayout { public CusFrameLayout(Context context) { super(context); } public CusFrameLayout(Context context, AttributeSet attrs) { super(context, attrs); } public CusFrameLayout(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } /** * 获取当前Layout的名字 * * @return name */ private String getName() { switch (getId()) { case R.id.red: return "Red_Layout"; case R.id.blue: return "Blue_Layout"; case R.id.yellow: return "Yellow_Layout"; default: return "00000"; } } private void showLog(String method, String action) { Log.d("WoW", getName() + " ---- " + method + " ---- " + action); } @Override public boolean dispatchTouchEvent(MotionEvent ev) { final int action = MotionEventCompat.getActionMasked(ev); switch (action) { case MotionEvent.ACTION_DOWN: showLog("dispatchTouchEvent()", "ACTION_DOWN"); break; case MotionEvent.ACTION_MOVE: showLog("dispatchTouchEvent()", "ACTION_MOVE"); break; case MotionEvent.ACTION_CANCEL: case MotionEvent.ACTION_UP: showLog("dispatchTouchEvent()", "ACTION_CANCEL & ACTION_UP"); break; } return super.dispatchTouchEvent(ev); } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { final int action = MotionEventCompat.getActionMasked(ev); switch (action) { case MotionEvent.ACTION_DOWN: showLog("onInterceptTouchEvent()", "ACTION_DOWN"); break; case MotionEvent.ACTION_MOVE: showLog("onInterceptTouchEvent()", "ACTION_MOVE"); break; case MotionEvent.ACTION_CANCEL: case MotionEvent.ACTION_UP: showLog("onInterceptTouchEvent()", "ACTION_CANCEL & ACTION_UP"); break; } return super.onInterceptTouchEvent(ev); } @Override public boolean onTouchEvent(MotionEvent ev) { final int action = MotionEventCompat.getActionMasked(ev); switch (action) { case MotionEvent.ACTION_DOWN: showLog("onTouchEvent()", "ACTION_DOWN"); break; case MotionEvent.ACTION_MOVE: showLog("onTouchEvent()", "ACTION_MOVE"); break; case MotionEvent.ACTION_CANCEL: case MotionEvent.ACTION_UP: showLog("onTouchEvent()", "ACTION_CANCEL & ACTION_UP"); break; } return super.onTouchEvent(ev); } }
这是一个自定义的Layout。其中我们Override了父类的三个有关Touch的方法:
方法 | 解释 |
---|---|
dispatchTouchEvent() | 分发 |
onInterceptTouchEvent() | 拦截 |
onTouchEvent() | 响应 |
事件Action | 解释 |
---|---|
ACTION_DOWN | 手指头按下屏幕 |
ACTION_MOVE | 手指头开始移动 |
ACTION_CANCEL & ACTION_UP | 手指头离开屏幕 (抬起) |
然后我们看一下XML吧
[code] <com.choices.touchevent.CusFrameLayout android:id="@+id/red" android:layout_width="300dp" android:layout_height="360dp" android:background="#FFFF1744"> <com.choices.touchevent.CusFrameLayout android:id="@+id/blue" android:layout_width="200dp" android:layout_height="200dp" android:layout_gravity="center" android:background="#FF81D4FA"> <com.choices.touchevent.CusFrameLayout android:id="@+id/yellow" android:layout_width="120dp" android:layout_height="120dp" android:layout_gravity="center" android:background="#FFFFB300" /> </com.choices.touchevent.CusFrameLayout> </com.choices.touchevent.CusFrameLayout>
简单的说一下结构吧:
最下层是 红色 的 FrameLayout . 然后他里面包括了两个子Layout。蓝色 和 红色.
中间层是 蓝色.
最上层是 ***.
这个时候,我们要看点击屏幕的时候,事件到底是怎么传递的
细心的同学已经发现,我加了Log。那怎么就看看log是怎么样的吧。当我们点击上图中箭头的地方:
在log中我们看到。Action_Down 完整的一个过程如下:
红色 —— 蓝色 —— *** —— 蓝色 —— 红色
这是因为,Down事件一直没有被相应,处理的方法是:
红色(分发 拦截)——蓝色(分发 拦截)——***(分发 拦截 响应)—— 蓝色(响应)—— 红色 ( 响应 )
妈蛋,为什么只有Action_Down? Move 和 Up去哪了?
这位同学问的好,你平时一定是一位聪明善良,善解人意的人。我正要讲这个呢。我们都知道,手指头触摸屏幕一般是这个过程:
按下 ——> 移动 ——> 抬起
只有你在 Action_Down 的时候 return true 。他才能知道你接下来还要干什么。
让我们试试吧。
我们在dispatchTouchEvent() 方法中,在Action_Down,并且是红色Layout下,return true;
我们看看Log:
因为我们在 红色 Layout的 分发Down时,return true。这时候,只有红色能够接受到这些事件,其他View的事件都没有被分发。并且,不会执行红色Layout的onInterceptTouchEvent()方法。除了Down之外的其他Action都执行了 分发 和 响应 方法。
大家可以下载我的源码,然后去试试
https://github.com/ChoicesWang/TouchEvent 源码地址总结
Target View 指的是,注册了onClickListener,或者 onTouchEvent 和 返回 true的View。就是你想让事件所响应的View。Android的TouchEvent通常包含三个动作,ACTION_DOWN,ACTION_MOVE与ACTION_UP。发出的顺序是DOWN->MOVE->MOVE->…->UP(注意MOVE事件是否能够被触发取决于操作手势里面是否包含了移动的动作)。
消息分发流程,从上到下,从父到子:Activity->ViewGroup1->ViewGroup1的子ViewGroup2->…->Target View
消息响应流程,从下到上,从子到父:Target View->…->ViewGroup1的子ViewGroup2->ViewGroup1->Activity
public boolean dispatchTouchEvent(MotionEvent ev);
事件分发处理函数,通常会在Activity层根据UI的显示情况,把事件传递给相应的ViewGroup。上面的演示代码中,为了方便模拟,会直接return true,只有这样,才会接受到 Move 事件。
public boolean onInterceptTouchEvent(MotionEvent ev);
对分发的事件进行拦截,注意拦截ACION_DOWN与其他ACTION的差异。
第1种情况:如果ACTION_DOWN的事件没有被拦截,顺利找到了TargetView,那么后续的MOVE与UP都能够下发。如果后续的MOVE与UP下发时还有继续拦截的话,事件只能传递到拦截层,并且发出ACTION_CANCEL。
第2种情况:如果ACITON_DOWN的事件下发时被拦截,导致没有找到TargetView,那么后续的MOVE与UP都无法向下派发。
public boolean onTouchEvent(MotionEvent ev);
响应处理函数,如果有设置对应listener的话,这里还会与onTouch,onClick,onLongClick有关联。具体执行顺序是onTouch()->onTouchEvent()->onClick()->onLongClick()。是否能够顺序执行,取决于每个方法的返回值是true还是false
强关注点:dispatch与intercept的差异,ACTION_DOWN与其他ACITON会对寻找target组件带来差异,而是否寻找到Target组件对整个流程有着重大的的影响。
原创地址:/article/1973052.html
欢迎大家访问我的Github开源库,这里有好玩的App源码,想和大家分享。https://github.com/ChoicesWang
相关文章推荐
- Android listview的某条item数据一直排在首位的问题
- Android 自定义ContentProvider
- Android中ListView异步加载图片错位、重复、闪烁问题分析及解决方案
- Android突击:常用的五种布局
- Android Notification使用之activity使用back键返回到上一级activity
- Leo仿【DOTA视频站】项目实践【二】---- 优库SDK学习
- Android android:gravity属性介绍及效果图
- 彻底解决Android 拍照 内存溢出 Out of Memory的问题
- Android定时器的使用 4000
- android判断用户是否已登陆详细代码
- Android基础总结——Service生命周期
- android 导入联系人基本信息到电话簿
- Android在设置里面添加新功能的方法
- Android studio相关设置及实现存在于工程目录中的视频播放
- android 构建GPS Provide步骤及信息
- Android Studio配置SVN 以及使用代码管理
- Android init进程分析——学习笔记
- Android adt 添加J2ee 开发插件
- Android 开发工具类 36_ getSimSerial
- Android Fragment 你应该知道的一切