5.4View事件总结案例
目录介绍
- 01.Android事件分发机制
- 02.View和ViewGroup分发事件
- 03.onTouch()、onTouchEvent()和onClick()
- 04.事件的传递规则
- 05.View处理事件的优先级
- 06.点击事件传递过程
- 07.事件传递规则要点
01.Android事件分发机制
- 简述Android的事件分发机制?
- 事件分发顺序:Activity->ViewGroup->View
- 主要方法:dispatchTouchEvent-分发事件、onInterceptTouchEvent-当前View是否拦截该事件、onTouchEvent-处理事件
- 1.父View调用dispatchTouchEvent开启事件分发。
- 2.父View调用onInterceptTouchEvent判断是否拦截该事件,一旦拦截后该事件的后续事件(如DOWN之后的MOVE和UP)都直接拦截,不会再进行判断。
- 3.如果父View进行拦截,父View调用onTouchEvent进行处理。
- 4.如果父View不进行拦截,会调用子View的dispatchTouchEvent进行事件的层层分发。
02.View和ViewGroup分发事件
- ViewGroup事件分发伪代码
image
- View事件分发伪代码
image
- View和ViewGroup在dispatchTouchEvent上的区别
- ViewGroup在dispatchTouchEvent()中会进行事件的分发。
- View在dispatchTouchEvent()中会对该事件进行处理。技术博客大总结
03.onTouch()、onTouchEvent()和onClick()
- onTouch()、onTouchEvent()和onClick()关系是怎样的,哪一个先执行?
- onTouch->onTouchEvent->onClick
- 当一个View需要处理事件时,如果它设置了OnTouchListener,那么OnTouchListener的onTouch方法会被回调。
- 这时事件如何处理还得看onTouch的返回值,如果返回false,则当前View的onTouchEvent方法会被调用;如果返回true,那么onTouchEvent方法将不会被调用。由此可见,给View设置的onTouchListener,其优先级比onTouchEvent要高。
- 如果当前方法中设置了onClickListener,那么它的onClick方法会被调用。可以看出,常用的OnClickListener,其优先级别最低。
- onTouch->onTouchEvent->onClick
- 如果设置了onClickListener, 但是onClick()没有调用,可能产生的原因? 技术博客大总结
- 父View拦截了事件,没有传递到当前View
- View的Enabled = false(setEnabled(false)): view处于不可用状态,会直接返回。
- View的Clickable = false(setClickable\setLongClickable(false)):view不可以点击,不会执行onClick
- View设置了onTouchListener,且消耗了事件。会提前返回。
- View设置了TouchDelegate,且消耗了事件。会提前返回。
04.事件的传递规则
- 事件的传递规则是什么?
- 点击事件产生后,会先传递给根ViewGroup,并调用dispatchTouchEvent
- 之后会通过onInterceptTouchEvent判断是否拦截该事件,如果true,则表示拦截并交给该ViewGroup的onTouchEvent方法进行处理
- 如果不拦截,则当前事件会传递给子元素,调用子元素的dispatchTouchEvent,如此反复直到事件被处理
05.View处理事件的优先级
- View处理事件的优先级?
- 在View需要处理事件时,会先调用OnTouchListener的onTouch方法,并判断onTouch的返回值
- 返回true,表示处理完成,不会调用onTouchEvent方法
- 返回false,表示未完成,调用onTouchEvent方法进行处理
- 可见,onTouchEvent的优先级没有OnTouchListener高
- onTouchEvent没有消耗的话就会交给TouchDelegate的onTouchEvent去处理。
- 如果最后事件都没有消耗,会在onTouchEvent中执行performClick()方法,内部会执行OnClickListener的onClick方法,优先级最低,属于事件传递尾端
06.点击事件传递过程
- 点击事件传递过程遵循如下顺序?技术博客大总结
- Activity->Window->View->分发
- 如果View的onTouchEvent返回false,则父容器的onTouchEvent会被调用,最终可以传递到Activity的onTouchEvent
07.事件传递规则要点
- 事件传递规则要点?
- View一旦拦截事件,则整个事件序列都由它处理(ACTION_DOWN\UP等),onInterceptTouchEvent不会再调用(因为默认都拦截了)
- 但是一个事件序列也可以通过特殊方法交给其他View处理(onTouchEvent)
- 如果View开始处理事件(已经拦截),如果不消耗ACTIO_DOWN事件(onTouchEvent返回false),则同一事件序列的剩余内容都直接交给父onTouchEvent处理
- View消耗了ACTION_DOWN,但不处理其他的事件,整个事件序列会消失(父onTouchEvent)不会调用。这些消失的点击事件最终会传给Activity处理。
- ViewGroup默认不拦截任何事件(onInterceptTouchEvent默认返回false)
- View没有onInterceptTouchEvent方法,一旦有事件传递给View,onTouchEvent就会被调用
- View的onTouchEvent默认都会消耗事件return true, 除非该View不可点击(clickable和longClickable同时为false)
- View的enable属性不影响onTouchEvent的默认返回值。即使是disable状态。
- onClick的发生前提是当前View可点击,并且收到了down和up事件
- 事件传递过程是由父到子,层层分发,可以通过requestDisallowInterceptTouchEvent让子元素干预父元素的事件分发(ACTION_DOWN除外)
01.View滑动有哪些方法
- View滑动有哪些方法?
- layout:对View进行重新布局定位。在onTouchEvent()方法中获得控件滑动前后的偏移。然后通过layout方法重新设置。
- offsetLeftAndRight和offsetTopAndBottom:系统提供上下/左右同时偏移的API。onTouchEvent()中调用
- LayoutParams: 更改自身布局参数
- scrollTo/scrollBy: 本质是移动View的内容,需要通过父容器的该方法来滑动当前View
- Scroller: 平滑滑动,通过重载computeScroll(),使用scrollTo/scrollBy完成滑动效果。
- 属性动画: 动画对View进行滑动技术博客大总结
- ViewDragHelper: 谷歌提供的辅助类,用于完成各种拖拽效果。
- Layout实现滑动
image
- offsetLeftAndRight和offsetTopAndBottom实现滑动
image
- LayoutParams实现滑动
- 通过父控件设置View在父控件的位置,但需要指定父布局的类型,不好
- 用ViewGroup的MariginLayoutParams的方法去设置margin
//方法一:通过布局设置在父控件的位置。但是必须要有父控件, 而且要指定父布局的类型,不好的方法。 RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) getLayoutParams(); layoutParams.leftMargin = getLeft() + offsetX; layoutParams.topMargin = getTop() + offsetY; setLayoutParams(layoutParams); /**=============================================== * 方法二:用ViewGroup的MarginLayoutParams的方法去设置marign * 优点:相比于上面方法, 就不需要知道父布局的类型。 * 缺点:滑动到右侧控件会缩小 *===============================================*/ ViewGroup.MarginLayoutParams mlayoutParams = (ViewGroup.MarginLayoutParams) getLayoutParams(); mlayoutParams.leftMargin = getLeft() + offsetX; mlayoutParams.topMargin = getTop() + offsetY; setLayoutParams(mlayoutParams);
- scrollTo\scrollBy实现滑动
- 都是View提供的方法。
- scrollTo-直接到新的x,y坐标处。
- scrollBy-基于当前位置的相对滑动。
- scrollBy-内部是调用scrollTo.
- scrollTo\scrollBy, 效果是移动View的内容,因此需要在View的父控件中调用。
- scrollTo/By内部的mScrollX和mScrollY的意义
- mScrollX的值,相当于手机屏幕相对于View左边缘向右移动的距离,手机屏幕向右移动时,mScrollX的值为正;手机屏幕向左移动(等价于View向右移动),mScrollX的值为负。
- mScrollY和X的情况相似,手机屏幕向下移动,mScrollY为+正值;手机屏幕向上移动,mScrollY为-负值。
- mScrollX/Y是根据第一次滑动前的位置来获得的,例如:第一次向左滑动200(等于手机屏幕向右滑动200),mScrollX = 200;第二次向右滑动50, mScrollX = 200 + (-50)= 150,而不是(-50)。
- 动画实现滑动的方法
- 可以通过传统动画或者属性动画的方式实现
- 传统动画需要通过设置fillAfter为true来保留动画后的状态(但是无法在动画后的位置进行点击操作,这方面还是属性动画好)
- 属性动画会保留动画后的状态,能够点击。博客
- ViewDragHelper
- 通过ViewDragHelper去自定义ViewGroup让其子View具有滑动效果。不过用的很少
02.Activity事件分发过程
- Activity事件分发的过程?
- 事件分发过程:Activity->Window->Decor View(当前界面的底层容器,setContentView的View的父容器)->ViewGroup->View
- Activity的dispatchTouchEvent,会交给Window处理(getWindow().superDispatchTouchEvent()),
- 返回true:事件全部结束
- 返回false:所有View都没有处理(onTouchEvent返回false),则调用Activity的onTouchEvent
03.Window事件分发过程
- Window事件分发?
- Window和superDispatchTouchEvent分别是抽象类和抽象方法
- Window的实现类是PhoneWindow
- PhoneWindow的superDispatchTouchEvent()直接调用mDecor.superDispatchTouchEvent(),也就是直接传给了DecorView
04.DecorView的事件分发
- DecorView的事件分发?
- DecorView继承自FrameLayout
- DecorView的superDispatchTouchEvent()会调用super.dispatchTouchEvent()——也就是ViewGroup的dispatchTouchEvent方法,之后就会层层分发下去。
05.根View的事件分发
- 根View的事件分发?技术博客大总结
- 顶层View调用dispatchTouchEvent
- 调用onInterceptTouchEvent方法
- 返回true,事件由当前View处理。如果有onTouchiListener,会执行onTouch,并且屏蔽掉onTouchEvent。没有则执行onTouchEvent。如果设置了onClickListener,会在onTouchEvent后执行onClickListener
- 返回false,不拦截,交给子View重复如上步骤。
其他介绍
01.关于博客汇总链接
02.关于我的博客
- github:https://github.com/yangchong211
- 知乎:https://www.zhihu.com/people/yczbj/activities
- 简书:http://www.jianshu.com/u/b7b2c6ed9284
- csdn:http://my.csdn.net/m0_37700275
- 喜马拉雅听书:http://www.ximalaya.com/zhubo/71989305/
- 开源中国:https://my.oschina.net/zbj1618/blog
- 泡在网上的日子:http://www.jcodecraeer.com/member/content_list.php?channelid=1
- 邮箱:yangchong211@163.com
- segmentfault头条:https://segmentfault.com/u/xiangjianyu/articles
- 掘金:https://juejin.im/user/5939433efe88c2006afa0c6e