到了这里我们发现对于不同情况,RecyclerView内部处理是不一样的,所以为了解决实际问题,看源码是必不可少的,接下来我们就一起跟着源码走一遍。来看看RecyclerView具体的滚动实现。(这里需要提醒大家的是这里我采用的是LinearLayoutManager,本文章都是基于LinearLayoutManager进行分析的)
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);
}
mRecycler.smoothScrollToPosition()方法时,内部调用了LayoutManager的smoothScrollToPosition方法,LayoutManager中smoothScrollToPosition没有实现,具体实现在其子类中,这里我们使用的是LinearLayoutManager,所以我们来看看内部是怎么实现的。
@Override
public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state,
int position) {
LinearSmoothScroller scroller = new LinearSmoothScroller(recyclerView.getContext());
scroller.setTargetPosition(position);//设定目标位置
startSmoothScroll(scroller);
}
这里我们可以看到,这里导致RecyclerView滑动的是LinearSmoothScroller,而LinearSmoothScroller的父类是RecyclerView.SmoothScroller,看到这里我相信大家都会感到一丝熟悉,因为我们在对控件内内容进行移动的时候,我们都会使用到一个类,那就是Scroller。这里RecyclerView也自定了一个滑动Scroller。肯定是与滑动其内部视图相关的。
public void startSmoothScroll(SmoothScroller smoothScroller) {
if (mSmoothScroller != null && smoothScroller != mSmoothScroller
&& mSmoothScroller.isRunning()) {
mSmoothScroller.stop();
}
mSmoothScroller = smoothScroller;
mSmoothScroller.start(mRecyclerView, this);
}
继续走startSmoothScroll,方法内部判断了如果正在计算坐标值就停止,然后调用start()方法重新开始计算坐标值。接着开始看start()方法。
void start(RecyclerView recyclerView, LayoutManager layoutManager) {
mRecyclerView = recyclerView;
mLayoutManager = layoutManager;
if (mTargetPosition == RecyclerView.NO_POSITION) {
throw new IllegalArgumentException("Invalid target position");
}
mRecyclerView.mState.mTargetPosition = mTargetPosition;
mRunning = true;//设置当前scroller已经开始执行
mPendingInitialRun = true;
mTargetView = findViewByPosition(getTargetPosition());//根据目标位置查找相应View,
onStart();
mRecyclerView.mViewFlinger.postOnAnimation();
}
在start方法中,会标识当前scroller的执行状态,同时会根据滚动的位置去寻找对应的目标视图。这里需要着重提示一下,findViewByPosition()这个方法,该方法会在Recycler的可见范围内去查询是否有目标位置对应的视图,例如,现在RecyclerView的可见范围为1-9,目标位置为10,那么mTargetView =null,如果可见范围为9-20,目标位置为1,那么mTargetView =null。
最终调用RecyclerView的内部类 ViewFlinger的postOnAnimation()方法。
class ViewFlinger implements Runnable {
....省略部分代码
void postOnAnimation() {
if (mEatRunOnAnimationRequest) {
mReSchedulePostAnimationCallback = true;
} else {
removeCallbacks(this);