Android事件分发机制分析
来源:互联网 发布:0基础软件开发 编辑:程序博客网 时间:2024/06/05 06:56
本文基于Android事件分发机制完全解析,带你从源码的角度彻底理解和从Android源码的角度理解应用开发(1)-Touch机制进行编写的,加入自己的理解。方便自己理清思路和便于以后的查看。
①首先我们先写一个小Demo,Demo源码下载, 如下图所示
布局代码如下:
<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:lingchen="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.yitong.mytouchevent.MainActivity"> <com.yitong.mytouchevent.view.TouchView android:layout_width="match_parent" android:layout_height="match_parent" android:background="@android:color/holo_blue_dark" android:gravity="center" lingchen:viewName="Out" > <com.yitong.mytouchevent.view.TouchView android:layout_width="200dp" android:layout_height="200dp" android:background="@android:color/holo_green_dark" android:gravity="center" lingchen:viewName="Center"> <com.yitong.mytouchevent.view.TouchView android:id="@+id/main_in" android:layout_width="100dp" android:layout_height="100dp" android:background="@android:color/holo_red_dark" lingchen:viewName="In"/> </com.yitong.mytouchevent.view.TouchView> </com.yitong.mytouchevent.view.TouchView></RelativeLayout>
当In_View设置了android:clickable=”true”时,当点击了In_View时。事件处理经过如下:
=============================================ACTION_DOWN: Out dispatchTouchEventACTION_DOWN: Center dispatchTouchEventACTION_DOWN: In dispatchTouchEventACTION_DOWN: In onTouchEvent=============================================ACTION_MOVE: Out dispatchTouchEventACTION_MOVE: Center dispatchTouchEventACTION_MOVE: In dispatchTouchEventACTION_MOVE: In onTouchEvent=============================================ACTION_UP: Out dispatchTouchEventACTION_UP: Center dispatchTouchEventACTION_UP: In dispatchTouchEventACTION_UP: In onTouchEvent=============================================
当In_View没有设置android:clickable=”true”时,当点击了In_View时。事件处理经过如下:
=============================================ACTION_DOWN: Out dispatchTouchEventACTION_DOWN: Center dispatchTouchEventACTION_DOWN: In dispatchTouchEvent=======================ACTION_DOWN: In onTouchEventACTION_DOWN: Center onTouchEventACTION_DOWN: Out onTouchEvent=============================================
②接着我们测试下当view的touch和onClick事件的关系,比较触发点击事件时,那个事件先执行
mainIn.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { switch(event.getAction()) { case MotionEvent.ACTION_DOWN: Log.d(Constants.name + TAG, "onTouch_ACTION_DOWN"); break; case MotionEvent.ACTION_MOVE: Log.d(Constants.name + TAG, "onTouch_ACTION_MOVE"); break; case MotionEvent.ACTION_UP: Log.d(Constants.name + TAG, "onTouch_ACTION_UP"); break; } return false; } }); mainIn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Log.d(Constants.name + TAG, "onClick"); } });
当In_View设置了android:clickable=”true”时,当点击了In_View时。会先执行onTouch方法,再执行onClick
=============================================MainActivity: onTouch_ACTION_DOWNMainActivity: onTouch_ACTION_MOVEMainActivity: onTouch_ACTION_UP=======================MainActivity: onClick=============================================
当In_View的onTouch的返回值返回为true,则不会执行onClick事件了
mainIn.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { ... return false; } }); mainIn.setOnClickListener(new View.OnClickListener() { ... });=============================================MainActivity: onTouch_ACTION_DOWNMainActivity: onTouch_ACTION_MOVEMainActivity: onTouch_ACTION_UP=============================================
几点重要总结:
只要你触摸到了任何一个控件,就一定会调用该控件的dispatchTouchEvent方法。首先在dispatchTouchEvent中最先执行的就是onTouch方法,因此onTouch肯定是要优先于onClick执行的。如果在onTouch方法里返回了true,就会让dispatchTouchEvent方法直接返回true,不会再继续往下执行。onClick的调用是在onTouchEvent(event)方法中。
如果你在执行ACTION_DOWN的时候返回了false,后面一系列其它的action就不会再得到执行了。简单的说,就是当dispatchTouchEvent在进行事件分发的时候,只有前一个action返回true,才会触发后一个action。
当In_View的onTouch的返回值返回为false,并且In_View没有设置了android:clickable=”true”时,只会去执行onTouch中的ACTION_DOWN方法
MainActivity: onTouch_ACTION_DOWNonTouch和onTouchEvent区别:这两个方法都是在View的dispatchTouchEvent中调用的,onTouch优先于onTouchEvent执行。如果在onTouch方法中通过返回true将事件消费掉,onTouchEvent将不会再执行。
==================至此关于事件分发机制介绍完毕=====================
接下几种情况带你回顾事件分发机制:
①为什么图片轮播器里的图片使用Button而不用ImageView?
- 就是因为Button是可点击的,而ImageView是不可点击的。如果想要使用ImageView,可以有两种改法。第一,在ImageView的onTouch方法里返回true,这样可以保证ACTION_DOWN之后的其它action都能得到执行,才能实现图片滚动的效果。第二,在布局文件里面给ImageView增加一个android:clickable=”true”的属性,这样ImageView变成可点击的之后,即使在onTouch里返回了false,ACTION_DOWN之后的其它action也是可以得到执行的。
②为什么给ListView引入了一个滑动菜单的功能,ListView就不能滚动了?
- 滑动菜单的功能是通过给ListView注册了一个touch事件来实现的。如果你在onTouch方法里处理完了滑动逻辑后返回true,那么ListView本身的滚动事件就被屏蔽了,自然也就无法滑动,因此解决办法就是在onTouch方法里返回false。
③touch监听器没被调用到?
- 如果是事件被这个View的viewparent拦截了。可以修改这个view的parent的onInterceptTouchTouchEvent(),或者在这个View中调用getParent().requestDisallowInterceptTouchEvent()。
④设置了onClickListener后,点击View没有反应?
- 可能覆盖了onTouchEvent(),需要在覆盖的方法调用super.onTouchEvent()或者手动调用performClick()
⑤点击两下View才调用onClickListener的bug?
- 这个其实是安卓的设计,当某个View调用了setFocusableInTouchMode(true)后,第一次点击会引起这个View的focus,第二次点击才会调用onClickListener,只需要设置setFocusableInTouchMode(false)即可。
=========让我们梳理一下============
1. Android事件分发是先传递到ViewGroup,再由ViewGroup传递到View的。
2. 在ViewGroup中可以通过onInterceptTouchEvent方法对事件传递进行拦截,onInterceptTouchEvent方法返回true代表不允许事件继续向子View传递,返回false代表不对事件进行拦截,默认返回false。
3. 子View中如果将传递的事件消费掉,ViewGroup中将无法接收到任何事件。
===============又是一个分水岭=======================
接下来我们就实战一下:
①写一个不能左右滑动的ViewPager,我们知道ViewPager是可以左右滑动的,当我们的ViewPager中嵌套一个轮播图(又一个ViewPager)。这时如果我们滑动轮播图,但是ViewPager把事件给拦截了。
public class NoScrollViewPager extends ViewPager { public NoScrollViewPager(Context context, AttributeSet attrs) { super(context, attrs); } public NoScrollViewPager(Context context) { super(context); } /** * 表示事件是否拦截, 返回false表示不拦截, 可以让嵌套在内部的viewpager相应左右划的事件 */ @Override public boolean onInterceptTouchEvent(MotionEvent arg0) { return false; } /** * 重写onTouchEvent事件,什么都不用做 */ @Override public boolean onTouchEvent(MotionEvent arg0) { return false; }}
②我们使用手机的新闻客户端,在主页面总体用的是一个ViewPager,可以查看娱乐、社会、实时…,当滑动到第一个主题(最左边)时,我们想再次滑动就可以打开菜单栏。这时单纯的ViewPager就不能满足。
public class HorizontalViewPager extends ViewPager { public HorizontalViewPager(Context context, AttributeSet attrs) { super(context, attrs); } public HorizontalViewPager(Context context) { super(context); } /** * 事件分发, 请求父控件及祖宗控件是否拦截事件 */ @Override public boolean dispatchTouchEvent(MotionEvent ev) { if (getCurrentItem() == 0) {// 第一个主题,再次滑动打开菜单栏 getParent().requestDisallowInterceptTouchEvent(false);// 请求父控件拦截我的事件,让父控件去处理该事件 } else {// 如果不是第一个主题,ViewPager自身处理 getParent().requestDisallowInterceptTouchEvent(true);// 请求父控件不要拦截我的事件 } return super.dispatchTouchEvent(ev); }}
③上面第一个不能左右滑动的NoScrollViewPager显然功能太弱,我们想要当里面的ViewPager滑动到第一个主题和最后一个主题的时候能够触发外面的ViewPager。
public class TopNewsViewPager extends ViewPager { int startX; int startY; public TopNewsViewPager(Context context, AttributeSet attrs) { super(context, attrs); } public TopNewsViewPager(Context context) { super(context); } /** * 事件分发, 请求父控件及祖宗控件是否拦截事件 1. 右划, 而且是第一个页面, 需要父控件拦截 2. 左划, 而且是最后一个页面, 需要父控件拦截 * 3. 上下滑动, 需要父控件拦截 */ @Override public boolean dispatchTouchEvent(MotionEvent ev) { switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: getParent().requestDisallowInterceptTouchEvent(true);// 不要拦截, // 这样是为了保证ACTION_MOVE调用 startX = (int) ev.getRawX(); startY = (int) ev.getRawY(); break; case MotionEvent.ACTION_MOVE: int endX = (int) ev.getRawX(); int endY = (int) ev.getRawY(); if (Math.abs(endX - startX) > Math.abs(endY - startY)) {// 左右滑动 if (endX > startX) {// 右划 if (getCurrentItem() == 0) {// 第一个页面, 需要父控件拦截 getParent().requestDisallowInterceptTouchEvent(false); } } else {// 左划 if (getCurrentItem() == getAdapter().getCount() - 1) {// 最后一个页面, // 需要拦截 getParent().requestDisallowInterceptTouchEvent(false); } } } else {// 上下滑动 getParent().requestDisallowInterceptTouchEvent(false); } break; default: break; } return super.dispatchTouchEvent(ev); }}
- Android事件分发机制分析
- android事件分发机制分析
- Android 事件分发机制分析
- 分析Android的Touch事件分发机制
- Android事件分发机制源码分析
- Android事件分发机制源码分析
- Android事件分发机制源码分析
- Android 事件拦截和分发机制分析
- Android事件分发机制案例分析(一)
- Android事件分发机制源码分析
- 源码分析android的事件分发机制
- Android事件分发机制代码片段分析
- Android 事件分发机制-源码分析
- Android 事件拦截机制、事件分发机制简单分析
- android事件分发机制
- Android事件分发机制
- Android 事件分发机制
- Android事件分发机制
- getgroups
- ubuntu 15.04 安装无线网卡驱动
- [php]属性、方法和常量
- mybatis简单运用(基于Annotation)
- Java解析XML getNodeValue()返回空
- Android事件分发机制分析
- 《文件传输基础----Java IO流---《一》》
- 程序媛进步指南,带着思考去做这些事情
- 四种方案解决ScrollView嵌套ListView问题
- 提高项目34-矩阵相乘
- 使用JDOM方式解析XML
- commons-pool实战之 GenericObjectPool和GenericKeyedObjectPool
- jdk1.7和jdk1.8区别
- C语言getgroups()函数:获取组代码函数