有时我们会碰到一个让RecyclerView在固定时间滑动到指定位置的需求,网上的方法都是重写LinearSmoothScroller类的calculateTimeForScrolling()方法,虽然可以修改时长,但还是不能精确到具体的时间。本文将会教大家如何去精确设置RecyclerView自动滑动的时间。
之前看到这篇文章:android之ViewPager修改滑动速度,发现是可以通过反射hook代码的方式修改ViewPager的滑动速度的,于是我就在想是否也可以通过反射的方式修改RecyclerView的滑动速度呢?经过多次尝试,终于可以通过以下方法去实现了:
继承OverScroller类,重写startScroll()方法,传递一个固定时长,示例代码如下:
public class UltraOverScroller extends OverScroller {
private int mDuration = -1;
public UltraOverScroller(Context context) {
this(context, null);
}
public UltraOverScroller(Context context, Interpolator interpolator) {
super(context, interpolator);
}
@Override
public void startScroll(int startX, int startY, int dx, int dy, int duration) {
if (mDuration > 0) {
// Ignore received duration, use fixed one instead
super.startScroll(startX, startY, dx, dy, mDuration);
} else {
super.startScroll(startX, startY, dx, dy, duration);
}
}
@Override
public void startScroll(int startX, int startY, int dx, int dy) {
if (mDuration > 0) {
// Ignore received duration, use fixed one instead
super.startScroll(startX, startY, dx, dy, mDuration);
} else {
super.startScroll(startX, startY, dx, dy);
}
}
/**
* Sets the scroll speed.
*
* @param duration The default duration to scroll.
*/
public void setScrollDuration(int duration) {
mDuration = duration;
}
}
实现一个类,继承RecyclerView,在该类中创建一个LinearSmoothScroller成员变量,并实现一个设置滑动速度的方法,该方法使用反射的方式实现,示例代码如下:
public class UltraRecyclerView extends RecyclerView {
private LinearSmoothScroller mSmoothScroller;
public UltraRecyclerView(Context context) {
super(context);
init(context);
}
public UltraRecyclerView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init(context);
}
public UltraRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context);
}
private void init(Context context) {
mSmoothScroller = new LinearSmoothScroller(context);
}
private void setScrollSpeed(int duration, Interpolator interpolator) {
try {
Field mViewFlinger = RecyclerView.class.getDeclaredField("mViewFlinger");
mViewFlinger.setAccessible(true);
Class viewFlingerClass = Class.forName("android.support.v7.widget" +
".RecyclerView$ViewFlinger");
Field mScroller = viewFlingerClass.getDeclaredField("mScroller");
mScroller.setAccessible(true);
Field mInterpolator = viewFlingerClass.getDeclaredField("mInterpolator");
mInterpolator.setAccessible(true);
Field mDecelerateInterpolator = LinearSmoothScroller.class.getDeclaredField(
"mDecelerateInterpolator");
mDecelerateInterpolator.setAccessible(true);
mInterpolator.set(mViewFlinger.get(this),
mDecelerateInterpolator.get(mSmoothScroller));
if (duration >= 0) {
UltraOverScroller overScroller = new UltraOverScroller(getContext(), interpolator);
overScroller.setScrollDuration(duration);
mScroller.set(mViewFlinger.get(this), overScroller);
} else {
OverScroller overScroller = new OverScroller(getContext(), interpolator);
mScroller.set(mViewFlinger.get(this), overScroller);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
这里我们不仅可以设置滑动的速度,还可以设置加速器。
最后可以在UltraRecyclerView中添加一个滑动到某个位置的方法,示例代码如下:
public void smoothScrollToPosition(int position, int speed, Interpolator interpolator) {
if (getLayoutManager() != null) {
mSmoothScroller.setTargetPosition(position);
setScrollSpeed(speed, interpolator);
getLayoutManager().startSmoothScroll(mSmoothScroller);
}
}
然后就可以调用这个方法来实现以指定的时间自动滑动到指定的位置了。
该功能我已经放到我的开源项目UltraRecyclerView里了,源码位置如下:https://github.com/jimmysuncpt/UltraRecyclerView/blob/master/ultrarecyclerview/src/main/java/com/jimmysun/ultrarecyclerview/UltraRecyclerView.java,当然在我项目里的代码和文章里的示例代码会略有不同。
至于实现的原理是什么,一时想不起来了,等以后有时间再跟一遍源码,然后再把原理写给大家吧!