帮你解决一下RecyclerView滑动到item顶部

最近在开发的时候,遇到了需要通过代码使得RecyclerView能够滑到指定item顶部位置的需求,在查看源码之后,发现RecyclerView已经提供了实现滑动到指定位置的方法,下面是可实现方法:

//平滑滚动
recyclerView.smoothScrollToPosition(position);
//非平滑滚动
recyclerView.scrollToPosition(position);

LinearLayoutManager manager = (LinearLayoutManager)recyclerView.getLayoutManager();
//平滑滚动
manager.scrollToPosition(position)

当然,除了上述方法以外,RecyclerView还有scrollBy、smoothScrollBy这两个方法(RecyclerView不支持scrollTo),可以实现滑动到指定位置,但是使用这三个方法滑动到对应item位置,需要计算item的高度或宽度,实现起来过于复杂。
在LayoutManager中,也包含了滚动的方法,且调用对应的方法也能实现滚动:

调用manager中的滚动方法

实际上,RecyclerView的滑动方法,都是通过代理manager的滑动方法实现的,下面是RecyclerView中smoothScrollToPosition(int position)的源码:

/**
 * Starts a smooth scroll to an adapter position.
 * <p>
 * To support smooth scrolling, you must override
 * {@link LayoutManager#smoothScrollToPosition(RecyclerView, State, int)} and create a
 * {@link SmoothScroller}.
 * <p>
 * {@link LayoutManager} is responsible for creating the actual scroll action. If you want to
 * provide a custom smooth scroll logic, override
 * {@link LayoutManager#smoothScrollToPosition(RecyclerView, State, int)} in your
 * LayoutManager.
 *
 * @param position The adapter position to scroll to
 * @see LayoutManager#smoothScrollToPosition(RecyclerView, State, int)
 */
public void smoothScrollToPosition(int position) {
        if (mLayoutFrozen) {
            return;
        }
        if (mLayout == null) {
            Log.e(TAG, "Cannot smooth scroll without a LayoutManager set. "
                    + "Call setLayoutManager with a non-null argument.");
            return;
        }
        layoutManger.smoothScrollToPosition(this, mState, position);
    }

虽然使用LayoutManager中的滚动方法,一样可以实现滑动,但是一般情况下,如果RecyclerView包含与LayoutManager同样的方法,要优先选择使用RecyclerView中的方法。
好了,废话不多说,下面以smoothScrollToPosition为例,看看调用滑动方法的效果:

调用smoothScroll方法滚动到指定item

可以发现,点击对应按钮调用滑动方法时,有时会无效。这是因为默认情况下,如果item可见,调用滑动到该item的方法时,该方法将不执行滑动。这就说明,调用scrollToPosition或者smoothScrollToPosition并不能保证能够滑到item的顶部。这就尴尬了,这个问题该怎么解决呢?
查阅资料后,发现可以通过下面两种方法解决:

1.LinearLayoutManger.scrollToPositionWithOffset(int position, int offset)

该方法是在LinearLayoutManager中,其中offset为滑动偏移量,当设置offset为0时,会滑动position对应的item的顶部,此方法与scrollToPosition一样,为非平滑滚动:

LinearLayoutManager manager = (LinearLayoutManager)recyclerView.getLayoutManager();
manager.scrollToPositionWithOffset(position, 0);

效果如下:
调用scrollToPositionWithOffset方法

2.复写LinearSmoothScoller的getVerticalSnapPreference()

此解决办法来源于stackOverFlow,只要复写LinearSmoothScroller的getVerticalSnapPreference(),即可实现平滑滚动到指定item顶部。首先我们看看getVerticalSnapPreference()方法:

/**
     * When scrolling towards a child view, this method defines whether we should align the top
     * or the bottom edge of the child with the parent RecyclerView.
     *
     * @return SNAP_TO_START, SNAP_TO_END or SNAP_TO_ANY; depending on the current target vector
     * @see #SNAP_TO_START
     * @see #SNAP_TO_END
     * @see #SNAP_TO_ANY
     */
    protected int getVerticalSnapPreference() {
        return mTargetVector == null || mTargetVector.y == 0 ? SNAP_TO_ANY :
                mTargetVector.y > 0 ? SNAP_TO_END : SNAP_TO_START;
    }

看上面的注释,可以知道,SNAP_TO_START代表滑动到item顶部,因此只要使该方法固定返回SNAP_TO_START,就能实现滑动到item顶部:

package cn.axen.mvp.scroller

import android.content.Context
import android.support.v7.widget.LinearSmoothScroller

public class TopLinearSmoothScroller extends LinearSmoothScroller {
    public TopLinearSmoothScroller(Context context) {
        super(context)
    }
    
    @Override
    public int getVerticalSnapPreference(): Int {
        return SNAP_TO_START
    }
}

最后,复写LinearLayoutManager的smoothScrollToPosition方法,调用RecyclerView的smoothScrollToPosition方法:
LinearLayoutManager manager = new LinearLayoutManager(this) {
@Override
public void smoothScrollToPosition(RecyclerView view, RecyclerView.State state, int position) {
TopLinearSmoothScroller scroller = new TopLinearSmoothScroller(view.getContext());
scroller.setTargetPosition(position);
startSmoothScroll(smoothScroller);
}
};
recyclerView.smoothScrollToPosition(position);

效果如下:
重写后的效果

免费获取安卓开发架构的资料(包括Fultter、高级UI、性能优化、架构师课程、 NDK、混合式开发(ReactNative+Weex)和一线互联网公司关于android面试的题目汇总可以加群:936332305 / 群链接:点击链接加入群聊【安卓开发架构】:https://jq.qq.com/?_wv=1027&k=515xp64

在这里插入图片描述