美文网首页
让ScrollView拥有回弹效果

让ScrollView拥有回弹效果

作者: 会飞的狒狒 | 来源:发表于2020-02-04 20:30 被阅读0次

    其实,本来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上的效果了。

    相关文章

      网友评论

          本文标题:让ScrollView拥有回弹效果

          本文链接:https://www.haomeiwen.com/subject/fojlzctx.html