smoothScrollToPosition()
在使用RecyclerView的时候,有一些场景需要RecyclerView能自动滑动到指定的item,这种情况可以调用RecyclerView的以下方法来实现:
scrollToPosition(int position)
smoothScrollToPosition(int position)
这两个方法传入需要滑动到的item的position就可以实现跳转到相应的item,区别是scrollToPosition()会立即跳转到相应item,不会有滑动的效果,smoothScrollToPosition()跳转的同时,会有滑动的效果,但是滑动速度很快,用户体验效果不好,如何能控制速度达到一个好的效果呢?
先来看下smoothScrollToPosition()的源码:
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;
}
mLayout.smoothScrollToPosition(this, mState, position);
}
它最终是调用了mLayout.smoothScrollToPosition(this, mState, position),mLayout是RecyclerView的布局管理器,比如线性布局管理器LinearLayoutManager:
mRecyclerView.setLayoutManager(mLinearLayoutManager);
来看下smoothScrollToPosition()在线性布局管理器中的实现:
@Override
public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state,
int position) {
LinearSmoothScroller linearSmoothScroller =
new LinearSmoothScroller(recyclerView.getContext());
linearSmoothScroller.setTargetPosition(position);
startSmoothScroll(linearSmoothScroller);
}
使用的是LinearSmoothScroller进行滑动操作,再来看LinearSmoothScroller类,它有一个获取滑动速度的方法:
protected float calculateSpeedPerPixel(DisplayMetrics displayMetrics) {
return MILLISECONDS_PER_INCH / displayMetrics.densityDpi;
}
所以我们可以想办法重写这个方法来改变RecyclerView滑动的速度。
我们自定义一个LinearLayoutManager:
public class ScrollLinearLayoutManager extends LinearLayoutManager {
private static final float MILLISECONDS_PER_INCH = 25f;
public ScrollLinearLayoutManager(Context context) {
super(context);
}
@Override
public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state, final int position) {
LinearSmoothScroller linearSmoothScroller = new LinearSmoothScroller(recyclerView.getContext())
{
@Nullable
@Override
public PointF computeScrollVectorForPosition(int targetPosition) {
return ScrollLinearLayoutManager.this.computeScrollVectorForPosition(targetPosition);
}
@Override
protected float calculateSpeedPerPixel(DisplayMetrics displayMetrics) {
return MILLISECONDS_PER_INCH / displayMetrics.densityDpi;
}
};
linearSmoothScroller.setTargetPosition(position);
startSmoothScroll(linearSmoothScroller);
}
}
重写smoothScrollToPosition()方法,然后重新实例化一个LinearSmoothScroller,并重写calculateSpeedPerPixel()方法,
这样,可以调整MILLISECONDS_PER_INCH的值,来实现RecyclerView滑动速度的调整。
补充
由当前item到目标item的中间要滑动的item数量可能每次都不一样,这样固定的移动速度也会导致用户体验不好,所以可以根据需要滑动的item数量来动态的调整滑动速度,
我们再修改下calculateSpeedPerPixel():
protected float calculateSpeedPerPixel(DisplayMetrics displayMetrics) {
View view = getChildAt(0);
if(view != null) {
final int firstChildPos = getPosition(getChildAt(0)); //获取当前item的position
int delta = Math.abs(position - firstChildPos);//算出需要滑动的item数量
if(delta == 0)
delta = 1;
return (MILLISECONDS_PER_INCH/delta) / displayMetrics.densityDpi;
}
else
{
return MILLISECONDS_PER_INCH / displayMetrics.densityDpi;
}
}
这样当需要滑动的item数量越多,滑动速度越快,item数量少时,滑动会较慢,这样就会有一个好的用户体验。