【Android】RecyclerView 辅助类 SnapHelper总结

1、SnapHelper说明

    SnapHelper是个辅助类,用于辅助RecyclerView在滚动结束时将Item对齐到某个位置。SnapHelper是个抽象类,官方提供了LinearSnapHelper、PagerSnapHelper子类。例如LinearSnapHelper可以让RecyclerView滚动停止时相应的Item停留中间位置。PagerSnapHelper可以使RecyclerView像ViewPager一样的效果,一次只能滑一页,而且居中显示。

2、SnapHelper使用

new LinearSnapHelper().attachToRecyclerView(mRecyclerView);
//或者
new PagerSnapHelper().attachToRecyclerView(mRecyclerView);

创建SnapHelper对象之后调用attachToRecyclerView()附着到对应的RecyclerView对象上就可以了。

3、Fling操作

Fling操作从手指离开屏幕瞬间被触发,在滚动停止时结束。手指在屏幕上滑动RecyclerView然后松手,RecyclerView中的内容会顺着惯性继续往手指滑动的方向继续滚动直到停止,这个过程叫做Fling。

4、SnapHelper三个抽象方法说明

(1)

public abstract int findTargetSnapPosition(LayoutManager layoutManager, int velocityX, int velocityY);

说明:该方法会根据触发Fling操作的速率(参数velocityX和参数velocityY)来找到RecyclerView需要滚动到哪个位置,该位置对应的ItemView就是那个需要进行对齐的列表项。我们把这个位置称为targetSnapPosition,对应的View称为targetSnapView。如果找不到targetSnapPosition,就返回RecyclerView.NO_POSITION。

(2)

public abstract View findSnapView(LayoutManager layoutManager);

说明:该方法会找到当前layoutManager上最接近对齐位置的那个view,该view称为SanpView,对应的position称为SnapPosition。如果返回null,就表示没有需要对齐的View,也就不会做滚动对齐调整。

(3)

public abstract int[] calculateDistanceToFinalSnap(LayoutManager layoutManager, View targetView);

说明:这个方法会计算第二个参数对应的View当前的坐标与需要对齐的坐标之间的距离。该方法返回一个大小为2的int数组,分别对应x轴和y轴方向上的距离。

5、SnapHelper源码分析

(1)attachToRecyclerView

    public void attachToRecyclerView(@Nullable RecyclerView recyclerView)
            throws IllegalStateException {
        if (mRecyclerView == recyclerView) {
            return; // nothing to do
        }
        if (mRecyclerView != null) {
            destroyCallbacks();
        }
        mRecyclerView = recyclerView;
        if (mRecyclerView != null) {
            setupCallbacks();
            mGravityScroller = new Scroller(mRecyclerView.getContext(),
                    new DecelerateInterpolator());
            snapToTargetExistingView();
        }
    }

说明:(1)如果snaphelper之前已经添加到该recyclerView上,则什么都不做。(2)如果snaphelper之前依附的recyclerView和现在的不一致,则移除之前recyclerView的所有回调。(3)将snaphelper依附到新的recyclerView上,并设置新的回调。(4)创建一个Scroller对象mGravityScroller,用于辅助计算fling操作。(5)调用snapToTargetExistingView方法实现对SnapView进行对齐滚动处理。

(2)destroyCallbacks

    /**
     * Called when the instance of a {@link RecyclerView} is detached.
     */
    private void destroyCallbacks() {
        mRecyclerView.removeOnScrollListener(mScrollListener);
        mRecyclerView.setOnFlingListener(null);
    }

说明:snaphelper依附的recyclerView移除ScrollListener和FlingListener。

(3)setupCallbacks

    /**
     * Called when an instance of a {@link RecyclerView} is attached.
     */
    private void setupCallbacks() throws IllegalStateException {
        if (mRecyclerView.getOnFlingListener() != null) {
            throw new IllegalStateException("An instance of OnFlingListener already set.");
        }
        mRecyclerView.addOnScrollListener(mScrollListener);
        mRecyclerView.setOnFlingListener(this);
    }

说明:给recyclerView添加ScrollListener和FlingListener。

(4)snapToTargetExistingView

    /**
     * Snaps to a target view which currently exists in the attached {@link RecyclerView}. This
     * method is used to snap the view when the {@link RecyclerView} is first attached; when
     * snapping was triggered by a scroll and when the fling is at its final stages.
     */
    void snapToTargetExistingView() {
        if (mRecyclerView == null) {
            return;
        }
        LayoutManager layoutManager = mRecyclerView.getLayoutManager();
        if (layoutManager == null) {
            return;
        }
        View snapView = findSnapView(layoutManager);
        if (snapView == null) {
            return;
        }
        int[] snapDistance = calculateDistanceToFinalSnap(layoutManager, snapView);
        if (snapDistance[0] != 0 || snapDistance[1] != 0) {
            mRecyclerView.smoothScrollBy(snapDistance[0], snapDistance[1]);
        }
    }

说明:(1)使用findSnapView(layoutManager)找到离目标位置最近的snapview。(2)然后使用calculateDistanceToFinalSnap(layoutManager, snapView)计算snapview和目标位置之间的距离。(3)最后调用mRecyclerView.smoothScrollBy滚动到目标位置。

(5)mScrollListener

// Handles the snap on scroll case.
private final RecyclerView.OnScrollListener mScrollListener =
    new RecyclerView.OnScrollListener() {
        boolean mScrolled = false;

        @Override
        public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
            super.onScrollStateChanged(recyclerView, newState);
            if (newState == RecyclerView.SCROLL_STATE_IDLE && mScrolled) {
                mScrolled = false;
                snapToTargetExistingView();
            }
        }

        @Override
        public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
            if (dx != 0 || dy != 0) {
                mScrolled = true;
            }
        }
    };

说明:滚动监听器实现逻辑是,在正常滚动停止的时候调用了snapToTargetExistingView()方法对targetView进行滚动调整,以确保停止的位置是在对应的坐标上,这就是RecyclerView添加该OnScrollListener的目的。

(6)OnFlingListener

@Override
public boolean onFling(int velocityX, int velocityY) {
    RecyclerView.LayoutManager layoutManager = mRecyclerView.getLayoutManager();
    if (layoutManager == null) {
        return false;
    }
    RecyclerView.Adapter adapter = mRecyclerView.getAdapter();
    if (adapter == null) {
        return false;
    }
    int minFlingVelocity = mRecyclerView.getMinFlingVelocity();
    return (Math.abs(velocityY) > minFlingVelocity || Math.abs(velocityX) > minFlingVelocity)
            && snapFromFling(layoutManager, velocityX, velocityY);
}

说明:(1)获取RecyclerView要进行fling操作需要的最小速率,只有超过该速率,ItemView才会有足够的动力在手指离开屏幕时继续滚动下去。(2)这里会调用snapFromFling()这个方法,就是通过该方法实现平滑滚动并使得在滚动停止时itemView对齐到目的坐标位置。

(7)snapFromFling

private boolean snapFromFling(@NonNull RecyclerView.LayoutManager layoutManager, int velocityX,
        int velocityY) {
    if (!(layoutManager instanceof RecyclerView.SmoothScroller.ScrollVectorProvider)) {
        return false;
    }

    RecyclerView.SmoothScroller smoothScroller = createScroller(layoutManager);
    if (smoothScroller == null) {
        return false;
    }

    int targetPosition = findTargetSnapPosition(layoutManager, velocityX, velocityY);
    if (targetPosition == RecyclerView.NO_POSITION) {
        return false;
    }

    smoothScroller.setTargetPosition(targetPosition);
    layoutManager.startSmoothScroll(smoothScroller);
    return true;
}

说明:(1)创建一个平滑滚动器smoothScroller,用于对ItemView进行平滑滚动操作。(2)通过findTargetSnapPosition()方法,根据速率找到targetSnapPosition。(3)设置滚动器的滚动目标位置,并启动平滑滚动器开始滚动到目标位置。

 

 

 

你可能感兴趣的:(android)