最近在开发的时候,遇到了需要通过代码使得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中,也包含了滚动的方法,且调用对应的方法也能实现滚动:
实际上,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为例,看看调用滑动方法的效果:
可以发现,点击对应按钮调用滑动方法时,有时会无效。这是因为默认情况下,如果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);
效果如下:
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);
效果如下:
参考博客:
RecyclerView自动滑动到指定的position
RecyclerView 滑动控制笔记
RecyclerView-How To smooth scroll to top of item on a certain position?