滑动是Android开发中非常重要的UI效果,几乎所有应用都包含了滑动效果,而本文将对滑动的使用以及原理进行介绍。
一、scrollTo与ScrollBy
View提供了专门的方法用于实现滑动效果,分别为scrollTo与scrollBy。先来看看它们的源码:
[代码]java代码:
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
|
从源码中可以看出scrollBy实际上是调用了scrollTo函数来实现它的功能。scrollBy实现的是输入参数的相对滑动,而scrollTo是绝对滑动。需要说明的是mScrollX、mScrollY这两个View的属性,这两个属性可以通过getScrollX、getScrollY获得。
· mScrollX : View的左边缘在View内容的左边缘的右边时,为正值,反之为负值。
· mScrollY : View的上边缘在View内容的上边缘的下边时,为正值,反之为负值。
下面我们来实现一个滑动的效果:
[代码]java代码:
001 002 003 004 005 006 007 008 009 010 011 012 013 014 015 016 017 018 019 020 021 022 023 024 025 026 027 028 029 030 031 032 033 034 035 036 037 038 039 040 041 042 043 044 045 046 047 048 049 050 051 052 053 054 055 056 057 058 059 060 061 062 063 064 065 066 067 068 069 070 071 072 073 074 075 076 077 078 079 080 081 082 083 084 085 086 087 088 089 090 091 092 093 094 095 096 097 098 099 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 |
|
现在我们来分析下这段代码:
· 首先在构造函数中获取了最小滑动距离TouchSlop。
· 重写onInterceptTouchEvent拦截事件,记录当前坐标。点下时,默认不拦截,只有当滑动还未完成的情况下,才继续拦截。在移动时,对滑动冲突进行了处理,当水平方向的移动距离大于竖直方向的移动距离,并且移动距离大于最小滑动距离时,我们判断此时为水平滑动,拦截事件自己处理;否则不拦截,交由子View处理。提起手指时,同样不拦截事件。
· 重写onTouchEvent处理事件,记录当前坐标。在手指按下时,与拦截事件时做相似处理。在ACTION_MOVE时,向左滑动,如果滑动距离超过左边界,则对滑动距离进行处理,相对的滑动距离超出又边界,也是一样处理,之后把滑动的距离交给scrollBy进行处理。当手指抬起时,根据当前的滚动值来判定应该滚动到哪个子控件的界面,然后使用scrollTo滑动到那个子控件。
· 重写了onMeasure和onLayout方法,在onMeasure中测量每一个子控件的大小值,在onLayout中对每一个子view在水平方向上进行布局。子view的layout的right增加父类的paddingLeft参数,来处理设置padding的情况。这两个函数的流程分析将会放在之后的文章中详细说明。
这个类的使用方法如下 :
[代码]xml代码:
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
|
HorizontalScroller设置全屏,padding为10dp。使用4个ImageView作为子View,并且都设置为可点击状态。示例效果图如下:
二、Scroller
可以看到上面使用scrollTo与ScrollBy方法的滑动都是瞬时完成的,这有些无法满足我们在切换子view时的需求。我们希望切换子View时,可以拥有滑动过程的效果,而Scroller正好可以完成这一点。
Scroller的使用方法:
* 1、创建Scroller实例
* 2、使用startScroll方法,对其进行初始化
* 3、重写computeScroll()方法,在其内部调用scrollTo或ScrollBy方法,完成滑动过程。
[代码]java代码:
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 |
|
上面的代码是Scroller的典型用法,也就是传说中的套路。当时Scroller使用startScroll方法时,只是对一系列参数进行了初始化。我们从下面的源码中可以看出。
[代码]java代码:
01 02 03 04 05 06 07 08 09 10 11 12 13 |
|
参数中,startX、startY是滑动的起点,dx、dy是滑动的距离,duration是滑动的时间系统设置为250ms。我们可以看到startScroll只是进行了滑动时间、是否滑动完成、起点、终点、滑动距离等的参数的设置,那么是如何调用computeScroll()函数的呢?其实computeScroll()的调用是由之后的invalidate()函数来完成的,invalidate可以请求View重绘,在View重绘时会调用draw方法,draw方法又会去调用computeScroll函数。但computeScroll()函数在view中是一个空的函数,需要我们去实现它。
computeScroll()函数的实现已经在上面给出了,有了computeScroll方法之后,就可以实现View的弹性滑动了。来看下computeScroll()的实现过程,首先要进行computeScrollOffset()的判断,来看下它的源码 :
[代码]java代码:
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
|
computeScrollOffset()首先检测scroller是否完成滑动,完成则返回false,未完成则继续AnimationUtils.currentAnimationTimeMillis获取当前的毫秒值,减去之前startScroll方法时获得毫秒值,就是当前滑动的执行时间。之后判断执行时间是否小于设置的总时间,如果小于,根据startScroll时设置的模式SCROLL_MODE,然后根据Interpolator计算出当前滑动的mcurrX、mcurrY(顺便提一下在实例化scroller的时候,是可以设置动画插值器。);如果执行时间大于或者等于设置的总时间,则直接设置mcurrX、mcurrY为终点值,并且设置mFinished,表示动画已经完成。
Scroller弹性滑动的流程如下
现在使用Scroller方法来更改一下上面的代码,当ACTION_UP时,子View的滑动可以有一个过程,而不是瞬时完成。
[代码]java代码:
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
|
上面就是代码中需要增加和修改的部分,我们来简单分析下。
· 在构造函数中增加对Scroller进行了实例化。
· 替换onTouchEvent中手指抬起后的方法,改为使用startScroll方法,对mScroller进行初始化,之后invalidate请求重绘。
· 增加重写的computeScroll()方法,在其内部调用scrollTo或ScrollBy方法,完成滑动过程,之后使用postInvalidate()请求view重绘。
示例效果图如下 :
三、回弹效果
从上面的效果图可以看出,我们已经实现了view的平滑滚动,滑动位置超过当前view的1/2时,松手之后变会自动滑出此item的View。可是如果想要在首位两端实现回弹效果,该如何做呢?其实只要修改onTouchEvent方法即可。
[代码]java代码:
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
|
来简单分析下修改的onTouchEvent方法:
在滑动的过程中,如果滑动的位置超过了试图的左、右边界,则缩小View的滑动距离,使之为手指滑动距离的1/3。当手指离开时,如果通过view宽度获得的当前inder小与0,则index为第一个View;如果获得的当前index超过了子View的数量-1,则index为最后一个View。View的回弹效果如下:
四、小结
本文介绍弹性滑动的实现方法,并对弹性滑动的过程进行了详细分析。在之后通过例子实现了view的弹性滑动以及回弹效果,但最后还留有两个问题,即invalidate与postInvalidate的区别又在哪里呢?invalidate是如何调用computeScroll()函数的呢?,这些问题我将在下一篇文章中进行详细的分析。