一、前言
Scroller 和 OverScroller,这两个是 Android UI 框架下实现滚动效果的最关键的类,ScrollView 内部的实现也是使用的 OverScroller,所以熟练的使用这两个类的相关API,可以让我们满足大部分的滚动开发需求。
二、VelocityTracker
VelocityTracker 主要用跟踪手指在滑动过程中的速度,包括水平和竖直方向的速度。我们来看个实例:
class VelocityTrackerTestView(context: Context?, attrs: AttributeSet?) : View(context, attrs) {
//1、创建实例
private var mVelocityTracker = VelocityTracker.obtain()
@SuppressLint("ClickableViewAccessibility")
override fun onTouchEvent(event: MotionEvent): Boolean {
//2、重置
if (event.actionMasked == MotionEvent.ACTION_DOWN) {
mVelocityTracker.clear()
}
//3、开始追踪
mVelocityTracker.addMovement(event)
when (event.actionMasked) {
MotionEvent.ACTION_DOWN -> {
//...
}
MotionEvent.ACTION_MOVE -> {
//o...
}
MotionEvent.ACTION_UP -> {
//速度 = ( 终点位置(px) - 起点位置(px) )/ 时间段(ms)
//4、设置时间段
mVelocityTracker.computeCurrentVelocity(1000)
//5、获取x方向、y方向的速度
//其中getXVelocity、getYVelocity方法的参数是pointerId,用于多指触控。不考虑多指时,可以不用传参数
var xVelocity = mVelocityTracker.getXVelocity(0)
var yVelocity = mVelocityTracker.getYVelocity(0)
//...
}
}
return true
}
override fun onDetachedFromWindow() {
//6、当不需要使用时,重置并回收内存
mVelocityTracker.clear()
mVelocityTracker.recycle()
super.onDetachedFromWindow()
}
}
上面注释得很明白,就不过多介绍了。VelocityTracker 一般用来判断当前是否达到一定的滑动速度来触发 Fling 的效果,这个滑动速度我们可以自己设置,也可以通过系统提供的来获取:
private var mViewConfiguration : ViewConfiguration = ViewConfiguration.get(context)
private var mMaxFlingVelocity = 0
//触发fling的速度
private var mMinFlingVelocity = 0
init {
mMaxFlingVelocity = mViewConfiguration.scaledMaximumFlingVelocity
mMinFlingVelocity = mViewConfiguration.scaledMinimumFlingVelocity
}
三、Scroller
在 View 类里面,有两个和滚动相关的类 scrollTo() 和 scrollBy。这两个方法可以实现 View 内容的移动,比如说一个 TextView,如果使用 scrollTo(),那么移动的是里面的文字而不是位置,scrollBy() 也是一样的。那么为什么是移动,不是滚动呢?这是因为这两个方法都是瞬间完成,而不是带有滚动过程的滚动,所以说如果要实现效果比较好的滚动,光靠 View 自带的方法是不行,还是需要 Scroller。但是 Scroller、OverScroller 并不是控制 View 进行滚动,实际上它们只是一个控件移动轨迹的辅助计算类,如果你想滚动的时候,它能帮你计算什么时间应该滚到什么位置,但是滚不滚需要靠我们来实现。所以说滚动位置由 Scrollers 计算出来了,我们在什么时候滚呢、滚多少呢?这时候就要 View 的一个回调函数 computeScroll() 出马了。
/**
* Called by a parent to request that a child update its values for mScrollX
* and mScrollY if necessary. This will typically be done if the child is
* animating a scroll using a {@link android.widget.Scroller Scroller}
* object.
*/
public void computeScroll() {
}
可以看到 View 里面的 computeScroll() 是空实现。如果我们用 Scroller 实现一个滚动动画的时候,这个方法就会被 parent 调用来改变 child。所以一般来说,这个方法可以用来更新 mScrollX 和 mScrollY,但是其实不光可以改变这些,我们还能做其他事情。如果我们调用 Scroller.startScroll(int startX, int startY, int dx, int dy),那么我们就可以在 computeScroll() 里面执行实际的操作了,就像下面这样:
@Override
public void computeScroll() {
// 先判断mScroller滚动是否完成
if (mScroller.computeScrollOffset()) {
// 这里调用View的scrollTo()完成实际的滚动
scrollTo( mScroller.getCurrX(), mScroller .getCurrY());
// 必须调用该方法,否则不一定能看到滚动效果
invalidate();
}
super.computeScroll();
}
Scroller.computeScrollOffset() 方法是来判断滚动过程是否完成的,如果没有完成就需要不停的 scrollTo 下去,所以需要在最后加一个invalidate() 来再次触发 computScroll(),直到滚动已经结束。
下面简单介绍一下常用的API。
常用API | 简介 |
computeScrollOffset() | 这个就是来判断当前的滑动动作是否完成的,用法很单一,就是在computeScroll()里面来做判断滚动是否完成 |
getCurrX()、getCurrY() | 这个就是获取当前滑动的坐标值,因为Scrollers只是一个辅助计算类,所以如果我们想获取滑动时的时时坐标,就可以通过这个方法获得,然后在computeScroll()里面调用 |
getFinalX()、getFinalY() | 这个是用来获取最终滑动停止时的坐标 |
isFinished() | 用来判断当前滚动是否结束 |
startScroll(int startX, int startY, int dx, int dy) | 用来开始滚动,这个是很重要的一个触发computeScroll()的方法,调用这个方法之后,我们就可以在computeScroll里面获取滚动的信息,然后完成我们的需要。这个还有一个带有滚动持续时间的重载函数,可以根据需求自由使用。特别要注意这四个参数,startX和startY是开始的坐标位置,正数左上,负数右下,dx、dy同理,当在computeScroll()获取getCurrX()的时候,变化范围就与这里地设置有关。 |
public void startScroll(int startX, int startY, int dx, int dy) {
startScroll(startX, startY, dx, dy, DEFAULT_DURATION);
}
四、OverScroller
OverScroller和Scroller有什么区别呢?事实上这两个类都属于Scrollers,Scroller出现的比较早在API1就有了,OverScroller是在API9才添加上的,所以功能比较完善。Over的意思就是超出,即OverScroller提供了对超出滑动边界的情况的处理,这两个类80%的API是一致的,OverScroller比Scroller添加了以下几个方法:
- isOverScrolled():返回当前Scroller当前是否返回有效位置
- springBack(int startX, int startY, int minX, int maxX, int minY, int maxY) :当你想'回弹'到有效的坐标范围内时调用它
- fling(int startX, int startY, int velocityX, int velocityY, int minX, int maxX, int minY, int maxY, int overX, int overY):基于一个扔手势开始滚动
- notifyHorizontalEdgeReached(int startX, int finalX, int overX)
- notifyVerticalEdgeReached(int startY, int finalY, int overY)
从名字也能看出来,都是对Over功能的支持。
fling 这个方法很重要,如果你想实现弹性滑动,即滑动之后布局能够根据移动速度慢慢减速的话,就需要用这个来实现。这里需要加速度的参数,我们可以通过 VelocityTracker 这个类来获取然后使用,也可以配合 GestureDetector 的 fling 手势使用。具体参数函数,其中 minXY、maxXY 代表滑动便捷,overX、overY 表示 x、y 轴超出回弹距离。fling 的使用同样需要在 computeScroll() 里进行相应处理
五、总结
本篇我们只是简单介绍一下 Scroller、OverScroller 的用法,下一篇我们将通过一个示例来加深对它的使用和理解。