@Override
public void scrollTo(int x, int y) {
Log.w(TAG, "RecyclerView does not support scrolling to an absolute position. "
+ "Use scrollToPosition instead");
}
@Override
public void scrollBy(int x, int y) {
if (mLayout == null) {
Log.e(TAG, "Cannot scroll without a LayoutManager set. "
+ "Call setLayoutManager with a non-null argument.");
return;
}
if (mLayoutSuppressed) {
return;
}
final boolean canScrollHorizontal = mLayout.canScrollHorizontally();
final boolean canScrollVertical = mLayout.canScrollVertically();
if (canScrollHorizontal || canScrollVertical) {
scrollByInternal(canScrollHorizontal ? x : 0, canScrollVertical ? y : 0, null);
}
}
/**
* Convenience method to scroll to a certain position.
*
* RecyclerView does not implement scrolling logic, rather forwards the call to
* {@link RecyclerView.LayoutManager#scrollToPosition(int)}
* @param position Scroll to this adapter position
* @see RecyclerView.LayoutManager#scrollToPosition(int)
*/
public void scrollToPosition(int position) {
if (mLayoutSuppressed) {
return;
}
stopScroll();
if (mLayout == null) {
Log.e(TAG, "Cannot scroll to position a LayoutManager set. "
+ "Call setLayoutManager with a non-null argument.");
return;
}
mLayout.scrollToPosition(position);
awakenScrollBars();
}
可以看到最后调用了mLayout.scrollToPosition(position)。
/**
* Starts a smooth scroll to an adapter position.
*
* To support smooth scrolling, you must override
* {@link LayoutManager#smoothScrollToPosition(RecyclerView, State, int)} and create a
* {@link SmoothScroller}.
*
* {@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 (mLayoutSuppressed) {
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)。
/**
* Scroll to the specified adapter position with the given offset from resolved layout
* start. Resolved layout start depends on {@link #getReverseLayout()},
* {@link ViewCompat#getLayoutDirection(android.view.View)} and {@link #getStackFromEnd()}.
*
* For example, if layout is {@link #VERTICAL} and {@link #getStackFromEnd()} is true, calling
* scrollToPositionWithOffset(10, 20)
will layout such that
* item[10]
's bottom is 20 pixels above the RecyclerView's bottom.
*
* Note that scroll position change will not be reflected until the next layout call.
*
* If you are just trying to make a position visible, use {@link #scrollToPosition(int)}.
*
* @param position Index (starting at 0) of the reference item.
* @param offset The distance (in pixels) between the start edge of the item view and
* start edge of the RecyclerView.
* @see #setReverseLayout(boolean)
* @see #scrollToPosition(int)
*/
public void scrollToPositionWithOffset(int position, int offset) {
mPendingScrollPosition = position;
mPendingScrollPositionOffset = offset;
if (mPendingSavedState != null) {
mPendingSavedState.invalidateAnchor();
}
requestLayout();
}
我们了解了RecyclerView和LinearLayoutManager相关滑动方法的意义,接下来就可以实现滚动指定位置功能(置顶或指定条目处于可见位置),如图:
|
|
findView(R.id.btn_to_position1, new View.OnClickListener() {
@Override
public void onClick(View v) {
mManager.scrollToPositionWithOffset(5,0);
}
});
findView(R.id.btn_to_position1, new View.OnClickListener() {
@Override
public void onClick(View v) {
int dy = (int) (mRvTestScroll.getY() + mRvTestScroll.getChildAt(5).getY());
mRvTestScroll.scrollTo(0, dy);
}
});
LinearSmoothScroller smoothScroller = new LinearSmoothScroller(getContext()) {
@Override
protected int getHorizontalSnapPreference() {
return LinearSmoothScroller.SNAP_TO_START;
}
@Override
protected int getVerticalSnapPreference() {
return LinearSmoothScroller.SNAP_TO_START;
}
};
findView(R.id.btn_to_position1, new View.OnClickListener() {
@Override
public void onClick(View v) {
smoothScroller.setTargetPosition(5);
mManager.startSmoothScroll(smoothScroller);
}
});
其中LinearSmoothScroller.SNAP_TO_START就是我们的置顶操作(不管该item是否处于屏幕可见状态),它还有SNAP_TO_END和SNAP_TO_ANY,可以去看下具体的意义,然后就是将SmoothScroller设置给LinearLayoutManager
smoothScroller.setTargetPosition(5);
mManager.startSmoothScroll(smoothScroller);
这里我们也可以通过重写LinearLayoutManager里的smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state, final int position)方法实现,如下:
public class SmoothScrollLayoutManager extends LinearLayoutManager {
public SmoothScrollLayoutManager(Context context) {
super(context);
}
@Override
public void smoothScrollToPosition(RecyclerView recyclerView,
RecyclerView.State state, final int position) {
LinearSmoothScroller smoothScroller =
new LinearSmoothScroller(recyclerView.getContext()) {
// 返回:滑过1px时经历的时间(ms)。
@Override
protected float calculateSpeedPerPixel(DisplayMetrics displayMetrics) {
return 150f / displayMetrics.densityDpi;
}
@Override
protected int getHorizontalSnapPreference() {
return LinearSmoothScroller.SNAP_TO_START;
}
@Override
protected int getVerticalSnapPreference() {
return LinearSmoothScroller.SNAP_TO_START;
}
};
smoothScroller.setTargetPosition(position);
startSmoothScroll(smoothScroller);
}
}
调用方式可直接使用RecyclerView的smoothScrollToPosition(int position)方法,如下:
findView(R.id.btn_to_position1, new View.OnClickListener() {
@Override
public void onClick(View v) {
mRvTestScroll.smoothScrollToPosition(5);
}
});
这两个写法本质上也是一样,至于为什么,前面也说得很清楚了,这里就不再提了。到这里细心的同学肯定也看到LinearSmoothScroller里多了个calculateSpeedPerPixel(DisplayMetrics displayMetrics)方法,是的,这个玩意就是计算滚动速度的,如果我们需要平滑滚动的效果,可以实现这个方法控制我们想要的置顶效果。