android中View的事件体系总结

本章代码请见:
https://github.com/OYYMING/ArtOfAndroidDev/tree/master/app/src/main/java/com/example/artofandroiddev/chapter3

1 基础知识

1.1 TouchSlop

TouchSlop是能被系统识别为滑动事件的最小距离。这是一个与设备有关的常量。可以通过ViewConfiguration.get(getContext()).getScaledTouchSlop()来获取。在重写onTouchEvent时可以用这个值来判断用户是否是在滑动,感觉算是一个防抖动的处理吧。当然这个值其实很小了,8dp的大小其实也就1.25毫米左右。

1.2 VelocityTracker,GestureDetector和Scroller

1.关于VelocityTracker有一个问题,见问题1。
2.什么时候用GestureDetector,作者给出了一个小建议,在监听滑动事件时自己在onTouchEvent中实现,在监听双击这种行为时用GestureDetector。为什么这么建议呢?个人认为是这样的:使用GestureDetector的时候肯定需要重写了onTouchEvent或者dispatchTouchEvent,重写了onTouchEvent后再将滑动事件的处理交由GestureDetector来处理并实现它的六个方法未免麻烦,所以只是监听滑动事件自己重写onTouchEvent就可以了。但是在监听双击,onSingleTapConfirmedonLongPressonFlingonShowPress(当确定是按下,而非滑动时触发)这类需要额外的参数来记录一定时间段内点击次数或者滑动速度之类的虽然也可以自己去实现,不过还是GestureDetector更简单吧。
3.使用Scroller的代码比较固定,记住就可以了

2 View的滑动

2.1 View滑动的三种方法

1.scrollTo和scrollBy——每个View都有,只能改变View中内容的位置,不能改变View的位置。
2.View动画和属性动画——View动画不能真正改变View的位置属性,只相当于改变了View显示的位置而已,所以只适用于没有交互的View。而属性动画可以真正的改变View的位置和宽高等属性。
3.改变View的布局参数——可以改变View的属性,在对LayoutParams做出修改之后requestLayout即可。使用稍微麻烦,适用于交互性的View。

3 弹性滑动

View自带的scrollTo和scrollBy两个方法都是瞬间移动,带来的用户体验并不好。想要做出渐进式的移动效果,其根本思想在于每隔一小段时间(为了确保流畅,最好<16ms)就重新绘制一次,将一次滑动分成多次绘制。实现的方法也分三种:
1.Scroller
Scroller本身可以实现这种效果,其原理在于每隔一段时间重新利用computeScrollOffset方法内部的interpolator来计算当前滑动的位置,并重新绘制View,不过这个方法只限于View的内容的移动。
2.利用动画
利用属性动画很容易就可以实现渐进式移动的效果。除此之外,还可以利用ValueAnimator的onAnimationUpdate方法实现其他效果。比如说获取当前动画经过的Fraction,计算新的位置并刷新View。类似于Interpolator的用法,不过却又可以协调某一时刻多个View的动作。可以参考本章代码里的AnimatorActivity。
3.利用Handler
在Handler的handlerMessage中刷新Layout,然后再发送新的Message,直到到达目的地之后停止刷新Layout。

4 View的事件分发机制

4.1 事件分发过程:

当手点击屏幕上的某一点时,事件分发开始。分发顺序为Activity->Window->DecorView->ContentView->ViewGroup->...ViewGroup->View,顺利的话,最后一般都会传递到一个View上。但是从DecorView开始的每一个ViewGroup都有一个方法onInterceptTouchEvent可以决定是否拦截这个点击事件由自己的onTouchEvent来处理。一般一个TouchEvent只能由一个View(ViewGroup)来处理(但是也可以在子View消耗过事件之后在父View的dispatchTouchEvent中也响应这个TouchEvent,不过这对父子最好不要有冲突,比如说父View和子View都响应事件进行滑动之类的体验就太差了),当TouchEvent被消耗掉之后(返回true),一个事件的传递链条就形成了,接下来的一系列事件都会沿着这个链条进行传递。但是一旦有某个父ViewGroup决定intercept这个TouchEvent并交由自己的onTouchEvent来处理,那本该处理这个事件的View将会收到ACTION_CANCEL,且传递链将会缩短到这个ViewGroup的onTouchEvent(并且该ViewGroup的onInterceptTouchEvent也不再调用)。当然如果子View不想让父View拦截TouchEvent,想要由自己来处理TouchEvent,它可以通过调用getParent().requestDisallowInterceptTouchEvent()方法来请求父ViewGroup(以及父亲的父亲等等等)不拦截事件。如果子View不消耗事件,则会冒泡传递到父View的onTouchEvent,如果父View也不消耗,还会再向上传递,最终传递到Activity的onTouchEvent。

4.2 关于enable和clickable

从源码来看,enable为false的话,只会影响onTouchListener中onTouch方法的调用,并不会影响View自身onTouchEvent的调用。只要clickable和longClickable有一个为true,就会消耗事件(onTouchEvent返回true),只是不会做出响应而已。

4.3 关于onTouch和onTouchEvent

onTouch是onTouchListener中的回调方法,通过设置onTouchListener可以在触发TouchEvent事件时调用onTouch,且这个方法的调用优先于View自身的onTouchEvent,如果你在onTouch中返回true,也就不会再调用onTouchEvent了。

5 滑动冲突

滑动冲突通常是由于嵌套的两个View可以同时滑动造成的。解决思想就是界定清楚这两个View滑动的时间,什么时候由外层滑动,什么时候由内层滑动。体现在代码中有两种做法:
(1)在某些条件下外层View拦截事件交由自己处理,其他情况不拦截事件交由内层View去处理
(2)外层View默认拦截DOWN以外的事件,但是内层View默认请求外层View不拦截事件。这样一来事件默认由内层View来处理,但是在达到一定条件下内层View同意外层View可以拦截事件,这样接下来的一系列事件又变成外层来处理。
另外在试验作者所说的同一方向上的滑动冲突的解决方法时发现一个问题,见问题2。


问题

1.p126作者提到getXVelocity取到的速度是一段时间内手指所划过的像素数,还给了公式说是时间段内的平均速度。但是根据查阅的资料并写了测试例子之后发现这个速度应该是当前点的瞬时速度,而非平均速度。我分别统计了10ms,100ms和1000ms的速度,如果确实如作者所说是平均速度,那么当我在一秒内手指做加速或者明显的非匀速运动时,这三者的值应该差异很大,但是最终发现,把他们换算成同一时间单位的话速度几乎完全一样。


2.在试验解决同一方向上的滑动冲突的外部拦截法时,我用自定义的ScrollView作为外部ViewGroup,用默认的ListView作为内部View来模拟这种冲突。但是在ListView滑动到最顶部应该由外部的ScrollView来接管滑动事件时发现ScrollView的onInterceptTouchEvent一直没有调用,后来意识到应该是ListView里不知道什么时候调用了requestDisallowInterceptTouchEvent(true)方法了,自己重写了一个ListView并在dispatchTouchEvent里调用了requestDisallowInterceptTouchEvent(false)之后就没有问题了。换言之,使用外部拦截法时可能存在被内层View偷偷请求不拦截事件的问题。要解决这个问题,我认为有两种做法,
一是自己重写一个内层的View,然后在dispatchTouchEvent里调用requestDisallowInterceptTouchEvent(false);
二是直接给这个View加一个OnTouchListener,在onTouch方法里调用requestDisallowInterceptTouchEvent(false)。
总之目的都在于完全由外部View来控制滑动事件的分发。


3.可以在父ViewGroup和子View之间反复传递TouchEvent吗?
换句话说,一系列MOVE事件,可以一会儿导致父View滑动,一会儿导致子View滑动吗?答案是可以,虽然不一定有什么实际用途,不过我还是研究了一下,详见代码:RepeatedInterceptActivity

嗯,本章到此结束。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值