Android事件分发机制学习笔记
来源:互联网 发布:诺基亚java单机游戏 编辑:程序博客网 时间:2024/06/09 19:07
一、View的事件分发
1、整个View的事件转发流程是:
View.dispatchEvent->View.setOnTouchListener->View.onTouchEvent
在dispatchTouchEvent中会进行OnTouchListener的判断,如果OnTouchListener不为null且返回true,则表示事件被消费,onTouchEvent不会被执行;否则执行onTouchEvent。
2、onTouchEvent中的DOWN,MOVE,UP
(1)DOWN时:
a、首先设置标志为PREPRESSED,设置mHasPerformedLongPress=false ;然后发出一个115ms后的mPendingCheckForTap;
b、如果115ms内没有触发UP,则将标志置为PRESSED,清除PREPRESSED标志,同时发出一个延时为500-115ms的,检测长按任务消息;
c、如果500ms内(从DOWN触发开始算),则会触发LongClickListener:
此时如果LongClickListener不为null,则会执行回调,同时如果LongClickListener.onClick返回true,才把mHasPerformedLongPress设置为true;否则mHasPerformedLongPress依然为false;
(2)MOVE时:
主要就是检测用户是否划出控件,如果划出了:
115ms内,直接移除mPendingCheckForTap;
115ms后,则将标志中的PRESSED去除,同时移除长按的检查:removeLongPressCallback();
(3)UP时:
a、如果115ms内,触发UP,此时标志为PREPRESSED,则执行UnsetPressedState,setPressed(false);会把setPress转发下去,可以在View中复写dispatchSetPressed方法接收;
b、如果是115ms-500ms间,即长按还未发生,则首先移除长按检测,执行onClick回调;
c、如果是500ms以后,那么有两种情况:
- i.设置了onLongClickListener,且onLongClickListener.onClick返回true,则点击事件OnClick事件无法触发;
- ii.没有设置onLongClickListener或者onLongClickListener.onClick返回false,则点击事件OnClick事件依然可以触发;
- d、最后执行mUnsetPressedState.run(),将setPressed传递下去,然后将PRESSED标识去除;
**注意:**setOnLongClickListener和setOnClickListener是否只能执行一个?
不是的,只要setOnLongClickListener中的onClick返回false,则两个都会执行;返回true则会屏蔽setOnClickListener
二、ViewGroup事件分发
这里关于ViewGroup事件分发的讲解非常详细,写的非常好:http://blog.csdn.net/lmj623565791/article/details/39102591
正常情况下ViewGroup之中的事件触发顺序:MyLinearLayout的dispatchTouchEvent -> MyLinearLayout的onInterceptTouchEvent -> MyButton的dispatchTouchEvent ->Mybutton的onTouchEvent
1.ViewGroup 中的事件监听(dispatchTouchEvent ):
ACTION_DOWN:
ViewGroup捕获到事件,然后判断是否拦截,如果没有拦截,则找到包含当前x,y坐标的子View,赋值给mMotionTarget,然后调用 mMotionTarget.dispatchTouchEventACTION_MOVE:
ViewGroup捕获到事件,然后判断是否拦截,如果没有拦截,则直接调用mMotionTarget.dispatchTouchEvent(ev)ACTION_UP:
ViewGroup捕获到事件,然后判断是否拦截,如果没有拦截,则直接调用mMotionTarget.dispatchTouchEvent(ev)
当然了在分发之前都会修改下坐标系统,把当前的x,y分别减去child.left 和 child.top ,然后传给child;
2.ViewGroup如何拦截事件
默认是不拦截的,要拦截的话重写ViewGroup的onInterceptTouchEvent方法:
@Overridepublic boolean onInterceptTouchEvent(MotionEvent ev){ int action = ev.getAction(); switch (action) { case MotionEvent.ACTION_DOWN: //如果你觉得需要拦截 return true ; case MotionEvent.ACTION_MOVE: //如果你觉得需要拦截 return true ; case MotionEvent.ACTION_UP: //如果你觉得需要拦截 return true ; } return false;}
如果你需要拦截,只要return true就行了,这要该事件就不会往子View传递了,并且如果你在ACTION_DOWN retrun true ,则DOWN,MOVE,UP子View都不会捕获事件;如果你在MOVE return true , 则子View在MOVE和UP都不会捕获事件(因为mMotionTarget被赋为null)。
3.如何让ViewGroup不拦截事件
Android给我们提供了一个方法:requestDisallowInterceptTouchEvent(boolean) 用于设置是否允许拦截,我们在子View的dispatchTouchEvent中直接这么写:
@Overridepublic boolean dispatchTouchEvent(MotionEvent event){ getParent().requestDisallowInterceptTouchEvent(true); int action = event.getAction(); switch (action) { case MotionEvent.ACTION_DOWN: Log.e(TAG, "dispatchTouchEvent ACTION_DOWN"); break; case MotionEvent.ACTION_MOVE: Log.e(TAG, "dispatchTouchEvent ACTION_MOVE"); break; case MotionEvent.ACTION_UP: Log.e(TAG, "dispatchTouchEvent ACTION_UP"); break; default: break; } return super.dispatchTouchEvent(event);}
getParent().requestDisallowInterceptTouchEvent(true); 这样即使ViewGroup在MOVE的时候return true,子View依然可以捕获到MOVE以及UP事件。
4.总结:
如果ViewGroup找到了能够处理该事件的View,则直接交给子View处理,自己的onTouchEvent不会被触发;
可以通过复写onInterceptTouchEvent(ev)方法,拦截子View的事件(即return true),把事件交给自己处理,则会执行自己对应的onTouchEvent方法
子View可以通过调用getParent().requestDisallowInterceptTouchEvent(true); 阻止ViewGroup对其MOVE或者UP事件进行拦截;
5.应用举例
案例一:比如你需要写一个类似slidingmenu的左侧隐藏menu,主Activity上有个Button、ListView或者任何可以响应点击的View,你在当前View上死命的滑动,菜单栏也出不来;因为MOVE事件被子View处理了~ 你需要这么做:在ViewGroup的dispatchTouchEvent中判断用户是不是想显示菜单,如果是,则在onInterceptTouchEvent(ev)拦截子View的事件;自己进行处理,这样自己的onTouchEvent就可以顺利展现出菜单栏了~~
案例二:如果在ViewPager中嵌套另外一个ViewPager的话,那么滑动屏幕事件会被父ViewPager拦截掉,根据以上叙述,可以在子ViewPager中这么写:
public boolean onTouchEvent(MotionEvent evt) { switch (evt.getAction()) { case MotionEvent.ACTION_DOWN: // 记录按下时候的坐标 downPoint.x = evt.getX(); downPoint.y = evt.getY(); if (this.getChildCount() > 1) { //有内容,多于1个时 // 通知其父控件,现在进行的是本控件的操作,不允许拦截 getParent().requestDisallowInterceptTouchEvent(true); } break; case MotionEvent.ACTION_MOVE: if (this.getChildCount() > 1) { //有内容,多于1个时 // 通知其父控件,现在进行的是本控件的操作,不允许拦截 getParent().requestDisallowInterceptTouchEvent(true); } break; case MotionEvent.ACTION_UP: // 在up时判断是否按下和松手的坐标为一个点 if (PointF.length(evt.getX() - downPoint.x, evt.getY() - downPoint.y) < (float) 5.0) { onSingleTouch(this); return true; } break; } return super.onTouchEvent(evt);}
- android学习笔记---事件分发机制(上)
- Android事件分发机制学习笔记
- Android事件分发机制学习笔记
- Android事件分发机制学习笔记
- Android事件分发机制---学习笔记
- Android学习笔记--事件分发机制
- Android View事件分发机制学习笔记
- Android ViewGroup事件分发机制学习笔记
- Android事件分发机制学习
- 【Android学习笔记】Android中View的事件分发机制
- 再次深入研究Android事件分发机制,学习笔记。
- Android学习笔记之事件分发机制(一)
- Android学习笔记之事件分发机制(二)
- Android 学习笔记之四 View的事件分发机制
- Android学习笔记 3.4View的事件分发机制
- Cocos2d-x事件分发机制学习笔记
- View事件分发机制学习笔记
- 安卓事件分发机制学习笔记
- Java程序员面试题集(116-135)
- 通过金矿模型介绍动态规划(经典入门)
- R常用技巧
- getOutputStream() has already been called for this response的解决
- 第五周 阅读程序 5
- Android事件分发机制学习笔记
- Java中的private、protected、public和default的区别
- 如何在一个cpp中调用另一个cpp中的类?
- iOS视图响应者链以及响应处理和传递过程
- Gson在Android开发中可能存在的陷阱
- 写一个算法来将一个乱序的序列保输出
- 基于Ubuntu-14.04系统-连接真机调试Android程序
- 如何随时随地退出Activity,在退出Activity的时候关闭之前的Activity,如何退出程序所有Activity
- input type="file" 选取文件路径时出现fakepath问题IE浏览器解决办法