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

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

尝试使用Markdown语法编写

View的滑动三种方法

  • 使用View自身提供的 scrollTo/scrollBy方法
  • 通过动画给View平移效果
  • 通过改变View的LayoutParams使得View重新布局

scrollTo/scrollBy方法

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

!注意

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

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

使用动画

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

改变布局参数

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

三个方法的对比?

  1. scrollTo/scrollBy实现 比较方便实现滑动效果不影响内部元素单击事件。 只能滑动View的内容,而不能滑动View的本身。
  2. 动画实现 3.0版本以上没有明显缺点,而3.0以下版本,则在View不需要交互的情况下使用比较合适。还有个优点就是能够实现较为复杂的动画,其他方式很难或者不能做到。
  3. 改变布局参数实现只是使用起来比较麻烦,适用需要交互的View。

弹性滑动

  • 使用Scroller,内部保存了几个参数(滑动起点:startXstartY;滑动距离:dxdy;滑动时间:duartion),最后通过invalidate 方法导致View重绘,重绘后,在View的draw方法中会调用computeScroll,而computeScroll 会去Scroller获取当前的scrollXscrollY,最后通过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的滑动冲突

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

**三种滑动冲突场景**
  • 外部滑动方向与内部滑动方向不一致
  • 外部滑动方向与内部滑动方向一致
  • 以上两种情况的嵌套

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

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

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

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

解决方法

  • 外部拦截法

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

  • 内部拦截法

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

注意:为什么父容器不能拦截ACTION_DOWN 事件,因为拦截之后,所有事件都无法传递给子控件了。

你可能感兴趣的:(个人记录,读书笔记)