RecyclerView|RecyclerView 滑动到指定下标、快速导航
需求
城市列表快速定位:通过右边的地区字母快速导航到特定位置
通讯录的快速定位
分析思路
网上查看的文章说:分三种情况,即屏幕上方、屏幕中、屏幕下方;分别处理代码逻辑;
实际分析后发现完全可以使用比较简单的方式实现:
默认API
RecyclerView 提供了3种方法用于滑动到特定位置的API
API | 区别 |
---|---|
scrollBy(int x, int y) |
根据x、y轴的距离,滑动 |
smoothScrollToPosition(int position) |
平滑滚动到特定 position |
scrollToPosition(int position) |
滚动到特定position |
-
scrollBy()
方法可以通计算 child.getTop() 获得其需要滑动的距离,但是如果 child 在屏幕之外,需要的 child 未创建,无法获得 -
scrollToPosition(int position)
、smoothScrollToPosition(int position)
方法可以通过 position 滑动到特定下标,但是有个特点:- position < firstViewItemPosition,则列表向下滚动 目标 position 滚动到顶部位置
- firstVisibleItemPosition < position < lastVisibleItemPosition :列表不会滚动
- position > lastVisibleItemPosition : 列表向上滚动, 目标position 滚动到屏幕底部位置
那我们去分析一下 smoothScrollToPosition() 的具体实现,看下我们是否可以干预一下其滚动规则
源码分析
// RecyclerView.javapublic 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;
}// @VisibleForTesting LayoutManager mLayout;
// 这里调用了 LayouManager 去实现滚动。
mLayout.smoothScrollToPosition(this, mState, position);
}
// LayoutManager 类public void smoothScrollToPosition(RecyclerView recyclerView, State state, int position) {
// LayoutManager 中并没有实现代码,而是由 我们设置的 其子类来具体的实现的
Log.e(TAG, "You must override smoothScrollToPosition to support smooth scrolling");
}
// LinearLayoutManager 类@Override
public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state, int position) {
// 滚动控制器
LinearSmoothScroller linearSmoothScroller = new LinearSmoothScroller(recyclerView.getContext());
// 设置滚动目标 position
linearSmoothScroller.setTargetPosition(position);
// 由 LineraManager 调用
startSmoothScroll(linearSmoothScroller);
}
// LayoutManager 类public void startSmoothScroll(SmoothScroller smoothScroller) {// 数据以及状态的校验
if (mSmoothScroller != null && smoothScroller != mSmoothScroller && mSmoothScroller.isRunning()) {
mSmoothScroller.stop();
}
mSmoothScroller = smoothScroller;
// 触发滚动
mSmoothScroller.start(mRecyclerView, this);
}
到这里,我们可以发现,RecyclerView 的滚动触发是由 LayoutManager 调用,其控制器的定义是由具体的 Manager 来设置;
继续来分析 SmoothScroller 的源码
// 抽象类
public static abstract class SmoothScroller{}
/**
* 线性平滑滚动控制器。
*/
public class LinearSmoothScroller extends RecyclerView.SmoothScroller {// 其他源码 .../**
* 当滚动到子视图时,这种方法定义了:child 与 parent 是否应该左对齐或右对齐。
*
* When scrolling towards a child view, this method defines whether we should align the left
* or the right edge of the child with the parent RecyclerView.
*
* @return SNAP_TO_START, SNAP_TO_END or SNAP_TO_ANY;
*/
protected int getHorizontalSnapPreference() {
return mTargetVector == null || mTargetVector.x == 0 ? SNAP_TO_ANY :
mTargetVector.x > 0 ? SNAP_TO_END : SNAP_TO_START;
}/**
* 当滚动到子视图时,这种方法定义了:child 与 parent 是否应该顶对齐或底对齐。
*
* When scrolling towards a child view, this method defines whether we should align the top
* or the bottom edge of the child with the parent RecyclerView.
*
* @return SNAP_TO_START, SNAP_TO_END or SNAP_TO_ANY;
*/
protected int getVerticalSnapPreference() {
return mTargetVector == null || mTargetVector.y == 0 ? SNAP_TO_ANY :
mTargetVector.y > 0 ? SNAP_TO_END : SNAP_TO_START;
}// 其他源码 ...
}
通过注释就可以看出:上面两个方法控制了 childView 的对齐方式,且是 protected 修饰的方法,那我们完全可以通过自定义返回对齐方式
具体实现
// 定义滚动控制器
private LinearSmoothScroller smoothScroller;
smoothScroller= new LinearSmoothScroller(inflater.getContext()) {// 这里考虑的是垂直列表
@Override
protected int getVerticalSnapPreference() {// 固定返回顶对齐方式
return SNAP_TO_START;
}
};
// 设置滚动目标 position
smoothScroller.setTargetPosition(index);
// 主动触发滚动
recyclerView.getLayoutManager().startSmoothScroll(smoothScroller);
至此,即可实现文头的需求方案;
效果图 recyclerView导航效果图.gif
推荐阅读
- 2018-02-06第三天|2018-02-06第三天 不能再了,反思到位就差改变
- 一个小故事,我的思考。
- 第三节|第三节 快乐和幸福(12)
- 你到家了吗
- 遇到一哭二闹三打滚的孩子,怎么办┃山伯教育
- 死结。
- 赢在人生六项精进二阶Day3复盘
- 子龙老师语录
- 异地恋中,逐渐适应一个人到底意味着什么()
- 即将到手三百万