recyclerView滚动处理

写这一章是因为需求:平滑滚动,而且滚动到的item要停留到顶部

非平滑滚动

使用LayoutManager.scrollToPositionWithOffset 方法即可

平滑滚动

smoothScrollToPosition:如果视图已经在屏幕上出现了,那么,不会触发滚动
解决方案
1. 自定义LinearSmoothScroller 重写getVerticalSnapPreference方法
java
abstract class MyLinearSmoothScroller extends LinearSmoothScroller {
private boolean isFromEnd;
public MyLinearSmoothScroller(Context context) {
super(context);
}
protected int getVerticalSnapPreference() {
return isFromEnd ? SNAP_TO_END : SNAP_TO_START;
}
public void setFromEnd(boolean fromEnd) {
isFromEnd = fromEnd;
}
}

2. 自定义LinearLayoutManager,重写smoothScrollToPosition方法
java
public class MyLinearlayoutManager extends LinearLayoutManager {
public MyLinearlayoutManager(Context context) {
super(context);
}
@Override
public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state,
int position) {
// 使用自定义的滚动,修改了getVerticalSnapPreference方法,达到view显示在屏幕上了了也能滑动的目的,其余影响未知。
MyLinearSmoothScroller linearSmoothScroller =
new MyLinearSmoothScroller(recyclerView.getContext()) {
@Override
public PointF computeScrollVectorForPosition(int targetPosition) {
return MyLinearlayoutManager.this
.computeScrollVectorForPosition(targetPosition);
}
};
linearSmoothScroller.setFromEnd(getStackFromEnd());
linearSmoothScroller.setTargetPosition(position);
startSmoothScroll(linearSmoothScroller);
}
@Override
public void setSmoothScrollbarEnabled(boolean enabled) {
super.setSmoothScrollbarEnabled(enabled);
}
}

3. 改动造成的其余影响:暂时未发现

分析

SmoothScroller的start方法中会先根据position找到targetView
java
mTargetView = findViewByPosition(getTargetPosition());

然后mRecyclerView.mViewFlinger.postOnAnimation()方法会走到ViewFlinger.run;接下来smoothScroller.onAnimation(0, 0);然后,tagetView存在,且position相同,则stop;sotp中把mRunning = false,所以下面一个循环又不能进入了。

java
if (mTargetView != null) {
// verify target position
if (getChildPosition(mTargetView) == mTargetPosition) {
onTargetFound(mTargetView, recyclerView.mState, mRecyclingAction);
mRecyclingAction.runIfNecessary(recyclerView);
stop();
} else {
Log.e(TAG, "Passed over target position while smooth scrolling.");
mTargetView = null;
}
}
if (mRunning) {
onSeekTargetStep(dx, dy, recyclerView.mState, mRecyclingAction);
boolean hadJumpTarget = mRecyclingAction.hasJumpTarget();
mRecyclingAction.runIfNecessary(recyclerView);
if (hadJumpTarget) {
// It is not stopped so needs to be restarted
if (mRunning) {
mPendingInitialRun = true;
recyclerView.mViewFlinger.postOnAnimation();
} else {
stop(); // done
}
}
}

time>0 跳过,则不会update,runIfNecessary方法中的change为false
java
onTargetFound
final int dx = calculateDxToMakeVisible(targetView, getHorizontalSnapPreference());
final int dy = calculateDyToMakeVisible(targetView, getVerticalSnapPreference());
final int distance = (int) Math.sqrt(dx * dx + dy * dy);
final int time = calculateTimeForDeceleration(distance);
if (time > 0) {
action.update(-dx, -dy, time, mDecelerateInterpolator);
}

当看到下面一个方法的时候,没有滑动,返回了0,由此推断,修改此处,有可能就能解决问题。
java
calculateDyToMakeVisible // 竖直方向的一些计算
calculateDtToFit // fit: 适合
public int calculateDtToFit(int viewStart, int viewEnd, int boxStart, int boxEnd, int
snapPreference) {
switch (snapPreference) {
case SNAP_TO_START:
return boxStart - viewStart; // 如果view在屏幕中,则是负数
case SNAP_TO_END:
return boxEnd - viewEnd; // 如果view在屏幕中,则是正数
case SNAP_TO_ANY:
final int dtStart = boxStart - viewStart; // 如果view在屏幕中,则是负数
if (dtStart > 0) {
return dtStart;
}
final int dtEnd = boxEnd - viewEnd; // 如果view在屏幕中,则是正数
if (dtEnd < 0) {
return dtEnd;
}
break;
default:
throw new IllegalArgumentException("snap preference should be one of the"
+ " constants defined in SmoothScroller, starting with SNAP_");
}
return 0;
}

java
protected int getVerticalSnapPreference() {
return mTargetVector == null || mTargetVector.y == 0 ? SNAP_TO_ANY :
mTargetVector.y > 0 ? SNAP_TO_END : SNAP_TO_START;
}

mTargetVector 为null
getVerticalSnapPreference的值是SNAP_TO_ANY
calculateDtToFit 这个方法返回了0,导致time>0,导致没有设置change = true;
所以runIfNecessary中的滚动没有触发

java
private void runIfNecessary(RecyclerView recyclerView) {
if (mJumpToPosition >= 0) {
final int position = mJumpToPosition;
mJumpToPosition = NO_POSITION;
recyclerView.jumpToPositionForSmoothScroller(position);
changed = false;
return;
}
if (changed) {
validate();
if (mInterpolator == null) {
if (mDuration == UNDEFINED_DURATION) {
recyclerView.mViewFlinger.smoothScrollBy(mDx, mDy);
} else {
recyclerView.mViewFlinger.smoothScrollBy(mDx, mDy, mDuration);
}
} else {
recyclerView.mViewFlinger.smoothScrollBy(mDx, mDy, mDuration, mInterpolator);
}
consecutiveUpdates ++;
if (consecutiveUpdates > 10) {
// A new action is being set in every animation step. This looks like a bad
// implementation. Inform developer.
Log.e(TAG, "Smooth Scroll action is being updated too frequently. Make sure"
+ " you are not changing it unless necessary");
}
changed = false;
} else {
consecutiveUpdates = 0;
}
}

而mTargetVector 在这个方法中设置updateActionForInterimTarget,
updateActionForInterimTarget 只在onSeekTargetStep中调用,
onSeekTargetStep只在SmoothScroller.onAnimation中if(mRunning)调用

而在targetView不为null的时候,也就没有执行targetVector的赋值 执行了stop,将mRunning变成false 所以都会跳过,最终不会滚动

相关文章
相关标签/搜索