RecyclerView的好朋友 — SnapHelpter

本文详细解析了SnapHelper的工作原理及其实现方式,包括calculateDistanceToFinalSnap、findSnapView和findTargetSnapPosition三个核心方法,并提供了一个自定义SnapHelper的实例。

SnapHelpter,相信很多人可能都不知道它或者没怎么关注过它,但是通过它实现的效果肯定都见过。比如短视频应用中切换视频时一划划一页的效果,这可不是ViewPager实现的啊,使用ViewPager实现的话成本太高,所以这类效果都是通过RecyclerVIew + SnapHelper来实现的,拿刚才讲的短视频切换效果来说,使用的就是RecyclerVIew和SnapHelper的子类PagerSnapHelper来实现的。

一、SnapHelper初解

说了这些,那么SnapHelper到底是什么东西呢?见名思意,Snap,翻译成中文有‘移到某位置’的意思,那么SnapHelper可以理解为‘移到某位置的帮手’,而这个被移到某位置的东西显然就是RecyclerVIew中的Item。

public abstract class SnapHelper extends RecyclerView.OnFlingListener {
   
   

//....

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

@Nullable
public abstract View findSnapView(LayoutManager layoutManager);

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

可以看到SnapHelper是一个抽象类,并继承了RecyclerView.OnFlingListener这个类,其中还包括三个抽象方法,我们通过实现这三个方法,就可以帮助RecyclerView移动item到‘某位置’。

为了更好理解SnapHelper的这三个方法,先说说RecyclerView.OnFlingListener这个类。

public abstract static class OnFlingListener {
   
   

    /**
     * 可用于实现自定义投掷行为
     *
     * @param velocityX X轴上的抛掷速度
     * @param velocityY Y轴上的抛掷速度
     *
     * @return 如果处理了投掷,则为 true,否则为 false。
     */
    public abstract boolean onFling(int velocityX, int velocityY);
}

这也是个抽象类,并且里面只有一个抽象方法,那这个类又是干啥的呢?我们都知道RecyclerView是可以滑动的,在我们手指离开屏幕后,RecyclerView还会继续顺着我们手指的方向再滑动一段距离,这个操作就是通过实现OnFlingListener接口来做到的。

SnapHelper继承了OnFlingListener实现了onFling方法,并在调用attachToRecyclerView()方法的时候将OnFlingListener设置给了RecyclerView。

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();
        }
    }

/**
 * 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);
}

二、三个方法

接着我们继续看SnapHelper中的三个抽象方法。

1、calculateDistanceToFinalSnap()
/**
 * 计算将目标item移动到最终位置所需距离
 *
 * @param layoutManager 
 * @param targetView 需要被移动的item
 *
 * @return 输出坐标将结果,out[0] 是水平轴上的距离,out[1] 是垂直轴上的距离。
 */
@SuppressWarnings("WeakerAccess")
@Nullable
public abstract int[] calculateDistanceToFinalSnap(@NonNull LayoutManager layoutManager,
        @NonNull View targetView);public abstract int[] calculateDistanceToFinalSnap(@NonNull RecyclerView.LayoutManager layoutManager,@NonNull View targetView);

这个方法是SnapHelper中另外两个抽象方法findSnapView()和findTargetSnapPosition()的下游方法,其参数中的targetView就是这两个方法提供的

通过findSnapView()提供

void snapToTargetExistingView() {
   
   
   	/***/
    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]);
    }
}

通过findTargetSnapPosition()提供

private boolean snapFromFling
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值