RecyclerView 列表、网格滑动到指定位置

RecyclerView (GridLayout) 滑动到指定位置

我的 RecyclerView 才不会这么笨!

RecyclerView 布局有三类,列表(横竖)、网格、瀑布流(
不规则),让某一个 item 显示在最上方(非最后一项),使用 scrollPosition 将某一个位置设置可见第一项,或者计算显示的 item 距离第一项距离,用 scrollBy 移动过去,常用使用下面几个方法实现该功能:

LayoutManager
1. scrollToPosition(int position)
2.smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state,
int position)

RecyclerView 滑动实际是调用设置的 LayoutManager 的滑动 toPosition.
1. scrollBy(int x, int y)
2. smoothScrollToPosition(int position)
3. scrollToPosition(int position)
注:RecyclerView 的 scrollTo(int x, int y) 这个方法不具备滑动的功能,具体见源码:

public void scrollTo(int x, int y) {   
    Log.w(TAG, "RecyclerView does not support scrolling to an absolute position. Use scrollToPosition instead");
}

如果要使 RecyclerView 滑动到某一个位置,会有下面几种情况:
1. 目标项在屏幕内
2. 目标项在屏幕上,即显示的位置小于当前显示的第一项位置
3. 目标项在屏幕下,即显示项在大于当前屏幕显示最后一项

处理思路:

  • 条件 1,使用 scrollToPosition / smoothScrollToPosition 均无效,因为目标项已经在屏幕可见范围,所以不会有滑动效果,因此需要计算目标项距离父布局的高度, scrollBy(0,dy)
  • 条件 2,使用 scrollToPosition /smoothScrollToPosition,在不可见项在上方,这两个方法均可用,一个没有滑动效果,一个有。
  • 条件 3,使用 scrollToPosition / smoothScrollToPosition,但会出现一个问题,目标项会显示在屏幕中,但不是在列表的第一项,仅是显示而已。注释显示可使用 scrollToPositionWithOffset 代替,需要的话可以尝试一下
  • 在条件 3 下,滑动目标项到屏幕中,但不是显示在第一的位置(假设目标相不在列表最后一屏中),即到了条件 1 的情况,这时需要监听滑动状态,计算缺少的滑动距离,将最后一点距离 scrollBy(0,dy) 过去。
    使用 “smoothScrollToPosition ” 是触发滑动器 SmoothScroller 来重绘界面,滑动结束会触发滑动监听器,而 scrollToPosition 是界面的直接重新绘制,不涉及滑动,也就无法获取滑动结束的状态,这就是不使用 scrollToPosition 方法的原因。

条件3 效果:
条件3 效果

修复条件3 效果:
修复条件3 效果

代码块:

    //条件3 是否需增加滑动距离
    private boolean isMove = false;
    //条件3 滑动的位置
    private int scrollPosition =-1;

     private void moveToPosition(int position) {
        if (position > data.size())//越界判断
            return;
        LinearLayoutManager layoutManager = (LinearLayoutManager) recyclerView.getLayoutManager();
        int first = layoutManager.findFirstVisibleItemPosition();
        int end = layoutManager.findLastVisibleItemPosition();
        if (first == -1 || end == -1)
            return;
        if (position <= first) {//条件2
            layoutManager.scrollToPosition(position);
        } else if (position >= end) {//条件3
            isMove = true;
            scrollPosition = position;
            layoutManager.smoothScrollToPosition(recyclerView, null, position);
        } else {//条件1,中间部分
            int n = position - layoutManager.findFirstVisibleItemPosition();
            if (n > 0 && n < data.size()) {
                int top = layoutManager.findViewByPosition(position).getTop();
                recyclerView.scrollBy(0, top);
            }
        }
    }

滑动监听器

private RecyclerView.OnScrollListener onScrollListener = new RecyclerView.OnScrollListener() {

        @Override
        public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
            super.onScrollStateChanged(recyclerView, newState);
            RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
            if (isMove && newState == RecyclerView.SCROLL_STATE_IDLE) {//滑动结束
                isMove = false;
                int top = layoutManager.findViewByPosition(scrollPosition).getTop();
                recyclerView.scrollBy(0, top);
            }
        }
    };

关于条件 3 为什么 RecyclerView 指将目标项显示在屏幕就结束了呢?
定位 scrollToPosition / smoothScrollToPosition

  1. scrollToPosition
    在 LinearLayoutManager 中滑动无效,以后再补充 ==
  2. smoothScrollToPosition
    RecyclerView 源码,在内部类 ViewFlinger 中 onAnimation(int dx, int dy),代码块如下:
private void onAnimation(int dx, int dy) {
            ...
            if (mTargetView != null) {
                // verify target position
                //如果目标项被找到,则停止,即目标项在屏幕中,就结束
                //这也解释条件1 下,scrollToPosition / smoothScrollToPosition 无效原因
                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;
                }
            }
            ...
        }

这两个滑动添加后,即可解决滑动导致的位置不准,飘的的问题,如果有解答你的疑虑,是我荣幸。

代码已上传至 github 在AndroidDemo 包 RecyclerViewScroll 中 点击这里跳转
也可下载整个工程 ,里面包含部分 kotlin 配置,如果不能用,删除 kotlin 配置即可。

猜你喜欢

转载自blog.csdn.net/ganfanzhou/article/details/79332913
今日推荐