OverScrollLinearLayoutManager

这个是一个针对RecyclerView实现的滚动回弹效果,目前仅针对LinearLayoutManager做了拓展。核心代码是:

首先,通过修改LinearLayoutManager的滚动方法scrollVerticallyBy,实现over scroll。

@Override
    public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler, RecyclerView.State state) {
        /* scroll before content */
        int scrolled = scrollVerticallyBefore(dy, recycler, state);
        /* scroll content */
        scrolled += super.scrollVerticallyBy(dy - scrolled, recycler, state);
        /* scroll after content */
        scrolled += scrollVerticallyAfter(dy - scrolled, recycler, state);
        /* if still throw {java.lang.IllegalArgumentException: Pixel distance must be non-negative}
           just disable prefect 0.0 */
        //this.setItemPrefetchEnabled(mOverOffsetY == 0);
        return scrolled;
    }

然后修改onScrollStateChanged方法,在滚动结束的时候实现回弹效果:

@Override
    public void onScrollStateChanged(int state) {
        super.onScrollStateChanged(state);
        if (state == RecyclerView.SCROLL_STATE_IDLE) {
            if (mOverScrollY != 0) {
                this.mRecyclerView.smoothScrollBy(0, -mOverScrollY);
            }
        }
    }

完整代码如下:

/**
 * Description: extend LinearLayoutManager for over scroll or fling
 * Author: xuqingqi
 * E-mail: [email protected]
 * Date: 2017/6/15
 */

public class LinearLayoutManagerExtend extends LinearLayoutManager {

    private static final String TAG = LinearLayoutManagerExtend.class.getSimpleName();
    private static final int MAX_OVER_SCROLL_DOWN = -128;
    private static final int MAX_OVER_SCROLL_UP = 128;

    private RecyclerView mRecyclerView;
    private int mMaxOverScrollUp;
    private int mMaxOverScrollDown;

    private int mOverScrollY;
    private int mOverOffsetY;
    private int mOverFactorY = 2;

    private List mOverScrollListeners;

    private boolean mOnFling = false;

    public LinearLayoutManagerExtend(RecyclerView recyclerView, int orientation, boolean reverseLayout) {
        super(recyclerView.getContext(), orientation, reverseLayout);
        this.mRecyclerView = recyclerView;
        this.mRecyclerView.setOverScrollMode(RecyclerView.OVER_SCROLL_NEVER);
        float density = recyclerView.getContext().getResources().getDisplayMetrics().density;
        this.mMaxOverScrollDown = (int) (MAX_OVER_SCROLL_DOWN * density * mOverFactorY);
        this.mMaxOverScrollUp = (int) (MAX_OVER_SCROLL_UP * density * mOverFactorY);
    }

    @Override
    public void collectAdjacentPrefetchPositions(int dx, int dy, RecyclerView.State state, LayoutPrefetchRegistry layoutPrefetchRegistry) {
        if (mOverScrollY != 0) {
            // TODO ugly bug fix. should not happen
            return;
        }

        try {
            super.collectAdjacentPrefetchPositions(dx, dy, state, layoutPrefetchRegistry);
        } catch (IllegalArgumentException iae) {
            iae.printStackTrace();
            // TODO fix me, still throw #IllegalArgumentException: Pixel distance must be non-negative
        }
    }

    @Override
    public void collectInitialPrefetchPositions(int adapterItemCount, LayoutPrefetchRegistry layoutPrefetchRegistry) {
        if (mOverScrollY != 0) {
            // TODO ugly bug fix. should not happen
            return;
        }

        super.collectInitialPrefetchPositions(adapterItemCount, layoutPrefetchRegistry);
    }

    @Override
    public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler, RecyclerView.State state) {
        /* scroll before content */
        int scrolled = scrollVerticallyBefore(dy, recycler, state);
        /* scroll content */
        scrolled += super.scrollVerticallyBy(dy - scrolled, recycler, state);
        /* scroll after content */
        scrolled += scrollVerticallyAfter(dy - scrolled, recycler, state);
        /* if still throw {java.lang.IllegalArgumentException: Pixel distance must be non-negative}
           just disable prefect 0.0 */
        //this.setItemPrefetchEnabled(mOverOffsetY == 0);
        return scrolled;
    }

    @Override
    public int scrollHorizontallyBy(int dx, RecyclerView.Recycler recycler, RecyclerView.State state) {
        return super.scrollHorizontallyBy(dx, recycler, state);
    }

    @SuppressWarnings("unused")
    private int scrollVerticallyBefore(int dy, RecyclerView.Recycler recycler, RecyclerView.State state) {
        if (getOrientation() == HORIZONTAL || dy == 0) {
            return 0;
        }

        int consumed = 0;
        if (dy > 0 && mOverScrollY < 0) { /* scroll up */
            consumed = dy + mOverScrollY > 0?
                    -mOverScrollY : dy;
        } else if (dy < 0 && mOverScrollY > 0) { /* scroll down */
            consumed = dy + mOverScrollY < 0?
                    -mOverScrollY : dy;
        }

        if (consumed != 0) {
            mOverScrollY += consumed;
            int offset = mOverScrollY / mOverFactorY - mOverOffsetY;
            offsetChildrenVertical(-offset);
            mOverOffsetY += offset; /* mOverOffsetY equals (int) (mOverScrollY * mOverFactorY) */
            dispatchOverScrolled(consumed, offset);
            Looger.I(this, "over scrolled before by " + consumed);
        }

        return consumed;
    }

    @SuppressWarnings("unused")
    private int scrollVerticallyAfter(int dy, RecyclerView.Recycler recycler, RecyclerView.State state) {
        if (getOrientation() == HORIZONTAL || dy == 0) {
            return 0;
        }

        int consumed = 0;
        if (dy > 0) { /* scroll up */
            consumed = dy + mOverScrollY > mMaxOverScrollUp?
                    mMaxOverScrollUp - mOverScrollY : dy;
        } else if (dy < 0) { /* scroll down */
            consumed = dy + mOverScrollY < mMaxOverScrollDown?
                    mMaxOverScrollDown - mOverScrollY : dy;
        }

        if (consumed != 0) {
            mOverScrollY += consumed;
            int offset = mOverScrollY / mOverFactorY - mOverOffsetY;
            offsetChildrenVertical(-offset);
            mOverOffsetY += offset; /* mOverOffsetY equals {mOverScrollY / mOverFactorY} */
            dispatchOverScrolled(consumed, offset);
            Looger.I(this, "over scrolled after by " + consumed);
        }

        return consumed;
    }

    @Override
    public void onScrollStateChanged(int state) {
        super.onScrollStateChanged(state);
        if (state == RecyclerView.SCROLL_STATE_IDLE) {
            if (mOverScrollY != 0) {
                this.mRecyclerView.smoothScrollBy(0, -mOverScrollY);
            }
        }
    }

    @SuppressWarnings("unused")
    private Context getContext() {
        return this.mRecyclerView.getContext();
    }

    @Override
    public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state){
        super.onLayoutChildren(recycler, state);
    }

    @Override
    public void onLayoutCompleted(RecyclerView.State state) {
        super.onLayoutCompleted(state);
        updateLayoutState();
    }

    private void updateLayoutState() {
        mOverScrollY = 0;
        mOverOffsetY = 0;
        dispatchStateUpdated();
    }

    @Override
    public void onAttachedToWindow(RecyclerView view) {
        updateLayoutState();
        super.onAttachedToWindow(view);
    }

    @Override
    public void onDetachedFromWindow(RecyclerView view, RecyclerView.Recycler recycler) {
        super.onDetachedFromWindow(view, recycler);
        updateLayoutState();
    }

    public void addOverScroll(OnOverScrollListener listener) {
        if (listener == null) {
            return;
        }
        if (mOverScrollListeners == null) {
            mOverScrollListeners = new ArrayList<>();
        }
        if (mOverScrollListeners.contains(listener)) {
            return;
        }
        mOverScrollListeners.add(listener);
    }

    public void removeOverScroll(OnOverScrollListener listener) {
        if (listener == null) {
            return;
        }
        if (mOverScrollListeners == null) {
            return;
        }
        mOverScrollListeners.remove(listener);
    }

    private void dispatchOverScrolled(int dScroll, int dOffset) {
        if (mOverScrollListeners == null) {
            return;
        }
        for (int i = 0; i < mOverScrollListeners.size(); i++) {
            OnOverScrollListener listener = mOverScrollListeners.get(i);
            listener.onOverScrolled(mRecyclerView, mOverScrollY, mOverOffsetY, dScroll, dOffset);
        }
    }

    private void dispatchStateUpdated() {
        if (mOverScrollListeners == null) {
            return;
        }
        for (int i = 0; i < mOverScrollListeners.size(); i++) {
            OnOverScrollListener listener = mOverScrollListeners.get(i);
            listener.onStateUpdated(mRecyclerView, mOverScrollY, mOverOffsetY);
        }
    }

    public void onFling(int velocityX, int velocityY) {
        mOnFling = true;
    }

}

你可能感兴趣的:(Android,OverScroll,recyclerview使用)