OverScrollLinearLayoutManager

来源:互联网 发布:南京软件大道租房 编辑:程序博客网 时间:2024/06/06 17:31

这个是一个针对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: xuqingqi01@gmail.com * 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<OnOverScrollListener> 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;    }}
原创粉丝点击