您的位置:首页 > 移动开发 > Android开发

View的事件体系 - Android开发艺术探索读书笔记(第三章)

2016-01-29 18:52 309 查看

Android开发艺术探索读书笔记(第三章)

尝试使用Markdown语法编写

View的滑动三种方法

使用View自身提供的 scrollTo/scrollBy方法

通过动画给View平移效果

通过改变View的LayoutParams使得View重新布局

scrollTo/scrollBy方法

只能改变内容位置,而不能改变View的布局位置 。

!注意

原始状态mScrollY 与mScrollX 都是0,然后View左滑动(View的左边缘在父控件的左边缘的右边),则mScrollX 为正值,同理右滑动mScrollX 为负值。View上滑动(View的上边缘在父控件的上边缘上边)mScrollY 则为正,同理,View下滑,mScrollY 为负。

总结:左正右负、上正下负

使用动画

使用传统属性动画,Android3.0以上版本能够真正将控件移动(而3.0以下版本,则只能将View的影像移动,需要通过0一些小手段:在新的位置预先设置一个相同的控件,然后当木目标控件动画结束时候,将目标控件隐藏,同时把预设的控件显示出来 )

改变布局参数

例子:将如想把某个控件往右移动100px,则在修改该控件的marginLeft参数值即可,另一种方法则是,在该控件左边放置一个空View,然后增加空View的宽度。

三个方法的对比?

scrollTo/scrollBy实现 比较方便实现滑动效果不影响内部元素单击事件。 只能滑动View的内容,而不能滑动View的本身。

动画实现 3.0版本以上没有明显缺点,而3.0以下版本,则在View不需要交互的情况下使用比较合适。还有个优点就是能够实现较为复杂的动画,其他方式很难或者不能做到。

改变布局参数实现只是使用起来比较麻烦,适用需要交互的View。

弹性滑动

使用Scroller,内部保存了几个参数(滑动起点:startX,startY;滑动距离:dx,dy;滑动时间:duartion),最后通过invalidate 方法导致View重绘,重绘后,在View的draw方法中会调用computeScroll,而computeScroll 会去Scroller获取当前的scrollX 和scrollY,最后通过scrollTo 方法实现滑动,紧接着又调用postInvalidate方法进行第二次重绘,如此反复。

总结:Scroller本身并不能实现View的滑动,它配合了View的computeScroll方法,不断让View重绘,而每次重绘距离滑动起始时间有一段时间间隔,间隔里,Scroller得出了View当前的滑动位置,再通过scrollTo方法进行小幅度滑动,反复进行。

View的事件分发机制

点击事件传递规则

**ViewGroup中的三个重要方法**

dispatchTouchEvent 进行事件分发

onInterceptTouchEvent 内部调用,判断是否拦截某个事件(View中没有这个方法)

onTouchEvent dispatchTouchEvent方法中的调用,处理点击事件,返回结果是否消耗当前事件

dispatchTouchEvent 逻辑概述:先调用onInterceptTouchEvent 方法判断是否拦截当前事件,若拦截,则调用onTouchEvent 判断是否消耗当前时间;若不拦截,则调用子控件的dispatchTouchEvent 方法

补充:如果View需要处理事件,又设置了onClickListener,那么其中的onTouch 方法会被回调,返回false,则当前View的onTouchEvent 方法仍会被调用,返回true,那么onTouchEvent方法则不会被调用。由此可见,onTouchListener 优先级比onTouchEvent 高,另外,如果onTouchListener 中设置有onClickListener ,则onClick 方法会被调用。

优先级: onTouchListener > onTouchEvent > onClickListener

**重要结论**
1. 一个事件序列只能被一个View拦截并且消耗,但可以通过特殊手段做到让两个View同时处理,在一个View中通过*onTouchEvent* 强行传递给其他View处理
2. 某个View拦截之后,它的*onInterceptTouchEvent* 就不会再被调用,所以一旦拦截,该事件序列都只能由拦截它的控件调用了
3. 某个View如果不消耗*ACTION_DOWN* 事件,则同一事件序列的其他事件都不会再交给它处理,将重新将事件交给它的父元素处理
4. 某个View如果不消耗除了*ACTION_DOWN* 之外的事件,那么,该点击事件将会无效,后续的事件还是会接收,并且父元素的*onTouch* 方法也不会被调用,最后,事件会传递给Activity处理
5. ViewGroup默认不会拦截任何事件
6. View没有*onInterceptTouchEvent* 方法
7. View的*onTouchEvent* 默认会消耗事件(返回true),除非该View不可点击(clickable与longClickable同时为false)
8. View的enable属性不影响*onTouchEvent* 默认返回值,两者无关
9. *onClick* 被调用的前提是,View可点击,并且收到down、up的事件
10. 事件传递过程是由外向内的,先传给父元素、再由父元素分发给子View,通过*requestDisallowInterceptTouchEvent* 可以在子元素中敢于父元素的事件分发过程(*ACTION_DOWN除外*)

View的滑动冲突

只要界面中内外两层同时可以滑动,则会产生滑动冲突

**三种滑动冲突场景**

外部滑动方向与内部滑动方向不一致

外部滑动方向与内部滑动方向一致

以上两种情况的嵌套

外内不一致的场景

例子:ViewPager 与 Fragment 配合使用组成页面滑动,Fragment 里面有ListView ,然而这种情况是没有滑动冲突的,因为ViewPager 内部已经解决了滑动冲突。但如果是ScrollView 就要手动解决冲突了。

处理规则:判断滑动是水平还是垂直的,滑动是水平时候,让外部View拦截滑动事件,而滑动是垂直的时候则是让内部View来拦截滑动事件。注意,斜向滑动时候,则根据水平滑动距离与垂直滑动距离来判断,算距离大的

内外一致的场景

处理规则:该场景较为特殊,一般需要从业务上找到突破点,明确什么时候需要外部滑动,什么时候需要内部滑动,制定相应的处理规则。

以上两种情况的嵌套

处理规则:该场景更为特殊,处理方式与内外一致的场景 相同,根据业务需求制定相应的处理规则。

解决方法

外部拦截法

点击事件都经由父控件的拦截处理,若父控件需要则拦截,不需要则不拦截,比较符合点击事件的分发机制,需要重写父容器的onInterceptTouchEvent 方法,并在内部做相应的拦截。

内部拦截法

父控件不拦截任何事件,所有的事件都传递给子元素,若子元素需要则消耗掉该事件,否则交给父控件处理,该方法与Android事件分发机制不一致,需要配合requestDisallowInterceptTouchEvent 方法才能正常工作,需要重写子元素的dispatchTouchEvent 方法

注意:为什么父容器不能拦截ACTION_DOWN 事件,因为拦截之后,所有事件都无法传递给子控件了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: