其实,本来ScrollView就拥有回弹效果,但不知道为什么没有开放出来,导致网上好多方案都是自己去实现一个自定义view。这篇文章就是要用一种简单的方式来实现回弹效果,我们只需要稍微修改下就可以了。
github地址:https://github.com/hunter0147/SpringScroll
首先看下最终成品的效果。
aafmm-46rgs.gif
因为我们是在对ScrollView进行扩展,所以第一步,新建一个类用来继承ScrollView。
class SpringScrollView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : ScrollView(context, attrs, defStyleAttr) {
接着重写overScrollBy方法,把maxOverScrollY参数改成你想要的最大回弹距离。ScrollView没有回弹效果就是这边原来传的maxOverScrollY是0,那肯定就没有回弹效果了。
override fun overScrollBy(
deltaX: Int,
deltaY: Int,
scrollX: Int,
scrollY: Int,
scrollRangeX: Int,
scrollRangeY: Int,
maxOverScrollX: Int,
maxOverScrollY: Int,
isTouchEvent: Boolean
): Boolean {
return super.overScrollBy(
deltaX,
deltaY,
scrollX,
scrollY,
scrollRangeX,
scrollRangeY,
maxOverScrollX,
MAX_OVER_SCROLL_Y,
isTouchEvent
)
}
这时候,已经支持了回弹。但是,这边还是有一个问题,如果我们来测试的话会发现慢划的时候x可以回弹,但是快划ScrollView却会停在滑动的位置,不会回弹了。
查看源码发现,在快划的时候走的是flingWithNestedDispatch逻辑,慢划的时候才走的mScroller.springBack,所以慢划才支持回弹。
case MotionEvent.ACTION_UP:
if (mIsBeingDragged) {
final VelocityTracker velocityTracker = mVelocityTracker;
velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
int initialVelocity = (int) velocityTracker.getYVelocity(mActivePointerId);
if ((Math.abs(initialVelocity) > mMinimumVelocity)) {
flingWithNestedDispatch(-initialVelocity);
} else if (mScroller.springBack(mScrollX, mScrollY, 0, 0, 0,
getScrollRange())) {
postInvalidateOnAnimation();
}
mActivePointerId = INVALID_POINTER;
endDrag();
}
break;
为了让快划也支持回弹,我们还需要再重写一个方法dispatchNestedFling。在这个方法里面我们先用反射取到mScroller,再用mScroller去调用回弹方法springBack。
private val scrollRange: Int
get() = if (childCount > 0) {
Math.max(0, getChildAt(0).height - (height - paddingBottom - paddingTop))
} else 0
override fun dispatchNestedFling(
velocityX: Float,
velocityY: Float,
consumed: Boolean
): Boolean {
if (!consumed) {
try {
val scrollview = ScrollView::class.java
val scrollField = scrollview.getDeclaredField("mScroller")
scrollField.isAccessible = true
val scroller = scrollField.get(this) as OverScroller
if (scroller.springBack(
scrollX, scrollY, 0, 0, 0,
scrollRange
)
) {
postInvalidateOnAnimation()
}
} catch (e: NoSuchFieldException) {
e.printStackTrace()
} catch (e: IllegalAccessException) {
e.printStackTrace()
}
}
return super.dispatchNestedFling(velocityX, velocityY, consumed)
}
现在再试下,就是我们上面gif上的效果了。