Android 酷炫拖拽反弹ReboundRecyclerView

开篇

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

  • RecyclerView列表无缝下拉上滑
  • RecyclerView列表支持平滑fling
  • 释放反弹效果

效果截屏

《Android 酷炫拖拽反弹ReboundRecyclerView》

立即体验

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

《Android 酷炫拖拽反弹ReboundRecyclerView》

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

篇尾!

  QQ:1006368252WeChat:eoy9527

学到很多东西的诀窍,就是一下子不要学很多。——洛克

    原文作者:SwitchLife
    原文地址: https://www.jianshu.com/p/c3f2c9f852ef
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞