原文链接 Android View滑动解决大法
对于触控式操作来说,滑动是一个特地重要的手势操作,如何做到让应用程序的页面滑动起来如丝般顺滑,让用户感觉到手起刀落的晦涩感,是开发人猿须要重点解决的问题,这对晋升用户体验是最为重要的事件。本文就将探讨一下,Android中View的滑动相干常识,以及如何做到丝般顺滑。
如何让View滑动起来
View的滑动是GUI反对的一项根本个性,就像触摸事件一件,这是废话,平台如果不反对,你还搞个毛线。
View滑动的基本原理
咱们先来看一下Android中实现View的滑动的基本原理。其实屏幕并没有动啊,一个View的可绘制区域,对于屏幕来说,对于view tree来说都是没有变动 的。父布局给某一个View的绘制区域是在layout之后就确定好了的,当View的实在高度或者宽度超过了这块可绘制区域,那么就须要滑动才能够把整个View做到用户可见。View外部通过两个要害成员变量mScrollX和mScrollY来记录滑动之后的坐标,View自身有mLeft和mTop来标识本人绝对于父布局的坐标地位,那么当有滑动的时候,在此View当中具体要绘制的区域就变成了以mLeft+mScrollX和mTop+mScrollY为终点的区域了。由此View便滚动起来了。
如何实现View的滑动
对于开发人猿来说,实现View的滑动,须要关注三个重要的办法,也即是View#scrollBy),View#scrollTo)以及View#onScrollChanged),这是实现滑动的三个最为外围的办法。
scrollBy提供的参数是须要滑动的间隔,而scrollTo则是须要传入要滑动到的指标坐标值,这两个办法都是要批改mScrollX和mScrollY的值,实质上是一样的。而onScrollChanged则是一个回调,用以告诉更新了的滑动地位。
Scroll手势
要想让View滑动起来,离不开事件手势的反对。最简略也是最间接的手势就是onScroll手势,这个在GestureDetecor中能够辨认出此手势,或者本人去间接解决touch event也能够得出此手势。这个并不简单,就是间接通过touch 事件来计算滑动多少间隔就好了,依照View预设计的能够滑动的方向,比方横向就计算不同工夫点MotionEvent的坐标值,失去一个程度间隔deltaX,而后调用scrollBy即可。垂直方向依此类推。
Scroll手势简略是因为它是间接来源于事件,且速度较慢,并不需要额定解决,所以整体逻辑解决流程并不简单。
在GestureDetector中的辨认就是在ACTION_MOVE时,查看滑动过的间隔,这个间隔(由sqrt(dx x dx, dy x dy)如果大于touch slop,就会触发onScroll手势回调。
Fling手势
Fling也即是疾速滑动,就是手指在屏幕上使劲的『挠』一下,手势的要点是手指在屏幕疾速滑过一小段短距离,就像把一个小球弹出去的感觉一样。对于Fling手势来说,最重要的是速度,程度方向的速度和垂直方向的速度,能够了解为高中物理常讲到的平抛静止一样。
GestureDetector辨认Fling的逻辑是,在ACTION_UP时,查看此次事件的速度,如果程度方向速度或者垂直方向速度超过了阈值,便会触发Fling手势回调。
留神:注意Scroll与Fling的区别,Scroll是慢的,不关怀工夫与速度,只关怀滑动的间隔,是在ACTION_MOVE时,手指并未有来到屏幕时就触发了,只有是ACTION_MOVE还在持续,就会持续触发onScroll,并且ACTION_UP时终止整个Scroll,而Fling只关怀速度,不关怀间隔,是在ACTION_UP时,手指来到了屏幕了(此次事件流解决结了)才会触发。
VelocityTracker
Fling事件速度是决定性的,认真看GestureDetector的处理过程会发现它应用了一个叫做VelocityTracker的对象,来帮忙解决一些对于速度的具体逻辑,那么有必要深刻理解一下这个对象。
VelocityTracker应用起来并不简单,获取它的一个对象后,只须要一直的把MotionEvent塞给它就能够了,而后在须要的时候让其计算两个方向上的速度,而后就没有而后了:
velocityTracker = VelocityTracker.obtain(); onTouchEvent(MotionEvent ev) { velocityTracker.addMovement(ev); if (want to know velocities) { velocityTracker.computeCurrentVelocity(100); vx = velocityTracker.getXVelocity(); vy = veolocityTracker.getYVelocity(); be happy with vx and vy. } }
这个类的实现,值得认真看一下,它次要的实现都是用JNI去实现,可能是因为计算形式较简单,所以computeCurrentVelocity)办法也阐明了,让你真用的时候再调,这个不必去管细节实现。重点看一下这个类,外面有一个对象池,用以缓存对象,并且创建对象的形式并不是间接new,而是用其obtain)办法。这里用的是叫享元(Flyweight Pattern)的设计模式,也就是说VelocityTracker对象其实是共享的。
顺滑如丝
后面提到了,让View滑动,只须要调用scrollBy或者scrollTo即可,但这个吧,是间接批改了mScrollX,mScrollY,而后invalidate,View下次draw时就间接在把指标区域内容绘制进去了,换句话说这两个办法滑动是霎时跳格局的。
一般来说,这也没有问题,就像onScroll手势,ACTION_MOVE时,一直的scrollBy刚刚滑过的间隔,都还okay,没有什么问题。
然而对于Fling事件就不行了,Fling事件,也即疾速滑动,要求短时间内进行大间隔滑动,或者像有跳转的需要时,也是短时间内要滑动大间隔。如果间接scrollBy或者scrollTo一步到位了,会显得 相当的突兀,体验相当不好,卡顿感特地强。如果能像做动画那样,在肯定工夫内,让其平滑的滑动,就会如丝般顺滑,体验好很多。Scroller就是专门用来解决此问题的。
Scroller
[Scroller]()是对滑动的封装,并不是View的子类,其实它跟View一点关系也没有,也不能操作View,实际上它与属性动画相似,它仅是一个滚动地位的计算器,通知它起始地位和要滚动的间隔,而后它就会通知你地位随工夫变动的值。其实这是一个中学物理题,也即给定初始地位,给定要滚动的间隔,以肯定的形式来计算每个工夫点的地位。具体的计算形式由mInterpolater成员来管制,默认是ViscousFluid,是按天然指数为减速度来计算的,具体的能够查看Scroller的源码。如果不喜爱默认的计算形式,能够本人实现个Interpolator,而后在结构时传进去。
Scroller的作用在于实现安稳滑动,不让View的滚动呈现跳跃,比方滑动一下ListView,开始滑动时的地位是x0,y0(ActionDown的地位),要向下滑动比方500个像素,不安稳的意思是,从x0,一下跳到x0+500的地位。要安稳,就要一直的一点点的扭转x的值而后invalidate,这也就是Scroller的典型应用场景:
Scroller scroller = new Scroller(getContext());scroller.startScroll(x0, y0, 500, 0);
而后在computeScroll时:
if (scroller.computeScrollOffset()) { int currX = scroller.getCurrX(); int currY = scroller.getCurrY(); invalidate(); // with currX and currY}
computeScrollOffset在滚动没完结时返回true,也就是说你须要持续刷新view。返回false时表明滚动完结了,当然也就没有必要再刷新view(当然如果你乐意也能够持续刷,然而地位啥的都不变了,所以刷了也白刷)。
滑动抵触解决
对于View的滑动,最难搞的问题便是手势抵触解决,特地是当页面的构造变得复杂了当前。一般来讲,滑动手势,是让某一个View沿着某一个方向『平移』一段距离,如果某一个页面中只有一个View是能够滑动的,或者页面中不同的View的可滑动方向是垂直正交的,那么就不会有抵触的问题。
所谓滑动抵触,是指父View和子View都承受滑动手势,并且方向又是一样的,这时就产生了滑动抵触,常见就是ScrollView中套着ListView(这个通常是垂直Y方向下面有滑动抵触),或者ViewPager中套着ScrollView(这个是程度X方向上有滑动抵触)。
要想解决好滑动抵触问题,须要先的确好整体的设计方案,有了大的准则后,就容易用技术计划找到解法。最现实的计划,也是目前用的最多的计划就是在子View的边界设定一个margin区域,当ACTION_DOWN在margin区域以外,认定滑动手势归父View解决,否则交由子View解决。像一些全局手势也是要用如此的计划,当点击间隔屏幕肯定范畴内(margin区域)认定此事件归以后页面解决,否则就认定为全局手势,就好比从屏幕右边向右滑动,很多应该将此辨认为BACK到上一页,但如果离右边较远时滑动,就会是页面外部的滑动事件(如果它有可滑动的组件的话,事件手势会被其滑消耗掉)。
参考资料
- Detect common gestures
- Flyweight pattern
- Design Patterns - Flyweight Pattern
- Animate a scroll gesture
- Android Scroller simple example