继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

Android 酷炫拖拽反弹ReboundRecyclerView

幕布斯6054654
关注TA
已关注
手记 1282
粉丝 219
获赞 1011

开篇

  有了我之前的一篇文章做铺垫:仿IOS拖拽回弹之进阶ReboundFrameLayout,本篇特地针对RecyclerView做了一个酷炫的拖拽反弹效果。当然,如果童鞋们想自己实现一个下拉刷新、上拉加载更多的RecyclerView,可以参考本篇的ReboundRecyclerView,在其基础上封装实现。

  • RecyclerView列表无缝下拉上滑

  • RecyclerView列表支持平滑fling

  • 释放反弹效果

效果截屏

https://img.mukewang.com/5d2c75ba0001b65c03600640.jpg

立即体验

扫描以下二维码下载体验App(从0.2.3版本开始,体验App内嵌版本更新检测功能):

https://img3.mukewang.com/5d2c75c800015f1804160419.jpg


JSCKit库传送门:https://github.com/JustinRoom/JSCKit


简析源码

ReboundRecyclerView.java

  • 初始化视图:

    public void init(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        overScroller = new OverScroller(context);        final ViewConfiguration viewConfiguration = ViewConfiguration.get(context);
        mMinimumVelocity = viewConfiguration.getScaledMinimumFlingVelocity();
        mMaximumVelocity = viewConfiguration.getScaledMaximumFlingVelocity();
        scaledTouchSlop = viewConfiguration.getScaledTouchSlop();

        recyclerView = new RecyclerView(context);
        recyclerView.setVerticalScrollBarEnabled(false);
        recyclerView.setNestedScrollingEnabled(false);
        addView(recyclerView, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
    }

mMinimumVelocity:最小滑动速度
mMaximumVelocity:最大滑动速度

  • 是否拦截touch事件:

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {        switch (ev.getAction()) {            case MotionEvent.ACTION_DOWN:                //记录是否按下
                if (!pressed) {
                    pressed = true;
                }                //停止自动滑动
                if (!overScroller.isFinished()) {
                    overScroller.forceFinished(true);
                }                //停止RecyclerView列表的滑动
                recyclerView.stopScroll();
                mLastTouchY = ev.getY();                break;            case MotionEvent.ACTION_MOVE:                float curTouchY = ev.getY();                float deltaY = mLastTouchY - curTouchY;
                mLastTouchY = curTouchY;                //如果滑动距离小于scaledTouchSlop,则把事件交给子View消耗;否则此事件交由自己的onTouchEvent(MotionEvent event)方法消耗。
                if (Math.abs(deltaY) >= scaledTouchSlop)                    return true;                break;            case MotionEvent.ACTION_UP:
                pressed = false;                break;
        }        return super.onInterceptTouchEvent(ev);
    }
  • 处理touch事件:

    @Override
    public boolean onTouchEvent(MotionEvent event) {        //跟踪滑动事件,在MotionEvent.ACTION_UP计算滑动速度
        trackerEvent(event);        switch (event.getAction()) {            case MotionEvent.ACTION_DOWN:
                mLastTouchY = event.getY();                break;            case MotionEvent.ACTION_MOVE:                float curTouchY = event.getY();                float dy = mLastTouchY - curTouchY;                int deltaY = (int) (dy);
                mLastTouchY = curTouchY;                //执行滑动处理
                move(deltaY);                break;            case MotionEvent.ACTION_UP:                //计算滑动速度
                int initialVelocity = getYVelocity();                if (Math.abs(initialVelocity) > mMinimumVelocity) {                    int verticalOffset = recyclerView.computeVerticalScrollOffset();                    int distanceFromBottom = recyclerView.computeVerticalScrollRange() - verticalOffset - recyclerView.getMeasuredHeight();                    if ((initialVelocity > 0 && verticalOffset > 0)
                            || (initialVelocity < 0 && distanceFromBottom > 0)) {                        //执行RecyclerView平滑
                        recyclerView.fling(0, -initialVelocity);
                    }
                }                //执行反弹
                rebound();                //释放滑动事件跟踪器
                recycleVelocityTracker();                break;
        }        return true;
    }
  • 处理MotionEvent.ACTION_MOVE事件:

    /**
     *
     * @param deltaY negative value represents moving down, positive value represents moving up.
     */
    private void move(int deltaY) {        int verticalOffset = recyclerView.computeVerticalScrollOffset();        int distanceFromBottom = recyclerView.computeVerticalScrollRange() - verticalOffset - recyclerView.getMeasuredHeight();//        Log.i(TAG, "move: scrollY = " + getScrollY());
        //getScrollY()<0 子view的顶部向下离开ReboundRecyclerView的顶部;getScrollY()>0 子view的底部向上离开ReboundRecyclerView的底部
        int scrollDistance = 0;        int scrollY = -getScrollY();        if (deltaY < 0) {//向下滑动
            //recyclerView底部已经向上离开ReboundRecyclerView的底部
            if (scrollY < 0) {
                scrollDistance = Math.max(deltaY, scrollY);
                scrollBy(0, (int) (scrollDistance * scrollRatio));
                deltaY = deltaY - scrollDistance;                if (deltaY >= 0)                    return;
            }

            scrollDistance = Math.max(deltaY, -verticalOffset);
            recyclerView.scrollBy(0, scrollDistance);
            deltaY = deltaY - scrollDistance;            if (deltaY >= 0)                return;

            scrollBy(0, (int) (deltaY * scrollRatio));
        } else if (deltaY > 0) {//向上滑动
            //recyclerView顶部已经向下离开ReboundRecyclerView的顶部
            if (scrollY > 0) {
                scrollDistance = Math.min(deltaY, scrollY);
                scrollBy(0, (int) (scrollDistance * scrollRatio));
                deltaY = deltaY - scrollDistance;                if (deltaY <= 0)                    return;
            }

            scrollDistance = Math.min(deltaY, distanceFromBottom);
            recyclerView.scrollBy(0, scrollDistance);
            deltaY = deltaY - scrollDistance;            if (deltaY <= 0)                return;

            scrollBy(0, (int) (deltaY * scrollRatio));
        }
    }
}
  • 执行回弹:

    private void rebound() {        if (getScrollY() == 0)            return;
        overScroller.startScroll(0, getScrollY(), 0, -getScrollY(), calculateDurationByScrollY());
        invalidate();
    }

完事!!!
如果想在这个ReboundRecyclerView基础上实现下拉刷新、上拉加载更多功能,请处理move(int deltaY)方法中的deltaY



作者:JustinRoom
链接:https://www.jianshu.com/p/c3f2c9f852ef


打开App,阅读手记
0人推荐
发表评论
随时随地看视频慕课网APP