RecyclerView - 滚动到每次都不起作用的位置

时间:2016-04-05 12:14:59

标签: android android-recyclerview linearlayoutmanager

我实现了一个水平滚动RecyclerView。我的RecyclerView使用了LinearLayoutManager,我遇到的问题是当我尝试使用scrollToPosition(position)smoothScrollToPosition(position)或来自LinearLayoutManager' s时scrollToPositionWithOffset(position)。对我来说都不适用。滚动调用无法滚动到所需位置,或者它不会调用OnScrollListener

到目前为止,我已经尝试了很多不同的代码组合,我不能在这里发布它们。以下是对我有用的(但只是部分):

public void smoothUserScrollTo(final int position) {

    if (position < 0 || position > getAdapter().getItemCount()) {
        Log.e(TAG, "An attempt to scroll out of adapter size has been stopped.");
        return;
    }

    if (getLayoutManager() == null) {
        Log.e(TAG, "Cannot scroll to position a LayoutManager is not set. " +
                "Call setLayoutManager with a non-null layout.");
        return;
    }

    if (getChildAdapterPosition(getCenterView()) == position) {
        return;
    }

    stopScroll();

    scrollToPosition(position);

    if (lastScrollPosition == position) {

        addOnLayoutChangeListener(new OnLayoutChangeListener() {
            @Override
            public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {

                if (left == oldLeft && right == oldRight && top == oldTop && bottom == oldBottom) {
                    removeOnLayoutChangeListener(this);

                    updateViews();

                    // removing the following line causes a position - 3 effect.
                    scrollToView(getChildAt(0));
                }
            }
        });
    }

    lastScrollPosition = position;
}

@Override
public void scrollToPosition(int position) {
    if (position < 0 || position > getAdapter().getItemCount()) {
        Log.e(TAG, "An attempt to scroll out of adapter size has been stopped.");
        return;
    }

    if (getLayoutManager() == null) {
        Log.e(TAG, "Cannot scroll to position a LayoutManager is not set. " +
                "Call setLayoutManager with a non-null layout.");
        return;
    }

//      stopScroll();

        ((LinearLayoutManager) getLayoutManager()).scrollToPositionWithOffset(position, 0);
//        getLayoutManager().scrollToPosition(position);
    }

由于this,我选择scrollToPositionWithOffset(),但情况可能不同,因为我使用的是LinearLayoutManager而不是GridLayoutManager。但是这个解决方案对我也有用,但正如我之前所说的那样,只是部分解决了。

  • 当滚动调用从第0位到totalSize时 - 7滚动就像魅力一样。
  • 当滚动从totalSize - 7到totalSize - 3时,我第一次只滚动到列表中的第7个最后一项。然而,第二次我可以滚动
  • 从totalSize - 3滚动到totalSize时,我开始出现意外行为。

如果有人找到了我的作品,我会感激它。这是gist到我的自定义代码ReyclerView

17 个答案:

答案 0 :(得分:36)

几个星期前我遇到了同样的问题,发现只有一个非常糟糕的解决方案才能解决它。不得不使用200-300毫秒的postDelayed

new Handler().postDelayed(new Runnable() {
    @Override
    public void run() {
        yourList.scrollToPosition(position);
    }
}, 200);

如果您找到了更好的解决方案,请告诉我们!祝你好运!

答案 1 :(得分:15)

事实证明,在我使用

之前,我遇到了类似的问题
player.collided

当只放入objectlist大小时,它总是保持在顶部。直到我决定将大小设置为变量。再说一遍,那是行不通的。然后我假设它可能在没有告诉我的情况下处理了一个outofboundsexception。所以我把它减去1.然后就可以了。

答案 2 :(得分:3)

这些方法似乎都不适合我。只有以下单行代码才能运行

((LinearLayoutManager)mRecyclerView.getLayoutManager()).scrollToPositionWithOffset(adapter.currentPosition(),200);

第二个参数是指offset,它实际上是项目视图的起始边缘和RecyclerView的起始边缘之间的距离(以像素为单位)。我为它提供了一个常数值,以使顶部项目也可见。

检查here

上的更多参考资料

答案 3 :(得分:2)

所以对我来说,问题是我在 NestedScrollView 中有一个 RecyclerView 。花了我一些时间找出问题所在。解决方案是(科特林):

val childY = recycler_view.y + recycler_view.getChildAt(position).y
nested_scrollview.smoothScrollTo(0, childY.toInt())

Java (对Himagi https://stackoverflow.com/a/50367883/2917564的积分)

float y = recyclerView.getY() + recyclerView.getChildAt(selectedPosition).getY();    
scrollView.smoothScrollTo(0, (int) y);

技巧是将嵌套的滚动视图滚动到Y而不是RecyclerView。在Android 5.0的Samsung J5和配备Android 9的Huawei P30 pro上运行良好。

答案 4 :(得分:1)

在创建循环/圆形适配器时,我遇到了同样的问题,考虑到位置初始化为0,我只能向下滚动而不能向上滚动。我最初考虑过使用罗伯特的方法,但是由于Handler仅被触发一次,所以它太不可靠了,如果我不走运,在某些情况下该职位不会被初始化。

为解决此问题,我创建了一个间隔Observable,它检查每个XXX的时间量,以查看初始化是否成功,然后进行处置。对于我的用例,这种方法非常可靠。

private fun initialisePositionToAllowBidirectionalScrolling(layoutManager: LinearLayoutManager, realItemCount: Int) {
        val compositeDisposable = CompositeDisposable() // Added here for clarity, make this into a private global variable and clear in onDetach()/onPause() in case auto-disposal wouldn't ever occur here
        val initPosition = realItemCount * 1000

        Observable.interval(INIT_DELAY_MS, TimeUnit.MILLISECONDS)
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe ({
                    if (layoutManager.findFirstVisibleItemPosition() == 0) {
                        layoutManager.scrollToPositionWithOffset(initPosition, 0)

                        if (layoutManager.findFirstCompletelyVisibleItemPosition() == initPosition) {
                            Timber.d("Adapter initialised, setting position to $initPosition and disposing interval subscription!")
                            compositeDisposable.clear()
                        }
                    }
                }, {
                    Timber.e("Failed to initialise position!\n$it")
                    compositeDisposable.clear()
                }).let { compositeDisposable.add(it) }
    }

答案 5 :(得分:1)

这对我有用

Handler().postDelayed({
                    (recyclerView.getLayoutManager() as LinearLayoutManager).scrollToPositionWithOffset( 0, 0)

}, 100)

答案 6 :(得分:1)

这非常适合滚动到回收站中的最后一个项目时

        Handler handler = new Handler();

        handler.postDelayed(new Runnable() {
            @Override
            public void run() {
                if (((LinearLayoutManager) recyclerView.getLayoutManager())
                        .findLastVisibleItemPosition() != adapter.getItemCount() - 1) {
                    recyclerView.scrollToPosition(adapter.getItemCount() - 1);
                    handler.postDelayed(this, 200);
                }
            }
        }, 200 /* change it if you want*/);

答案 7 :(得分:0)

我也遇到了类似的问题(必须在列表更新时滚动到顶部),但以上选项都没有 100% 有效

不过我终于在 https://dev.to/aldok/how-to-scroll-recyclerview-to-a-certain-position-5ck4 archive link

找到了一个可行的解决方案
摘要

scrollToPosition 似乎只在基础数据集准备就绪时起作用。

因此 postDelay 可以(随机)工作,但这取决于设备/应用程序的速度。如果超时时间太短,则失败。 smoothScrollToPosition 也仅在适配器不太忙时才有效(请参阅 https://stackoverflow.com/a/61403576/11649486

要观察数据集何时准备就绪,可以添加 AdapterDataObserver 并覆盖某些方法。

解决我的问题的代码:

adapter.registerAdapterDataObserver( object : RecyclerView.AdapterDataObserver() {
    override fun onItemRangeInserted(
        positionStart: Int,
        itemCount: Int
    ) {
        // This will scroll to the top when new data was inserted
        recyclerView.scrollToPosition(0)
    }
}

答案 8 :(得分:0)

这是在这个日期使用 kotlin 的最终解决方案......如果你导航到另一个片段并返回并且你的 recyclerview 重置到第一个位置,只需在 onCreateView 或任何你需要的地方添加这一行就可以调用适配器......

pagingAdapter.stateRestorationPolicy=RecyclerView.Adapter.StateRestorationPolicy.PREVENT_WHEN_EMPTY

顺便说一句,pagingAdapter 是我的 diffUtil 适配器。

答案 9 :(得分:0)

非常奇怪的错误,无论如何,我设法解决了这个问题,而没有发布或延迟发布,如下所示:

list.scrollToPosition(position - 1)
list.smoothScrollBy(1, 0)

希望它也能对某人有所帮助。

答案 10 :(得分:0)

在Fragment或Activity中使用Kotlin协程,并使用lifecycleScope,因为在销毁生命周期时会取消在此范围内启动的任何协程。

  lifecycleScope.launch {
                delay(100)
                recyclerView.scrollToPosition(0)

答案 11 :(得分:0)

答案是使用Post方法,它将确保正确执行任何操作

答案 12 :(得分:0)

也许做起来不是那么优雅,但这总是对我有用。向RecyclerView添加新方法,并使用由scrollToPosition插入的方法:

public void myScrollTo(int pos){
    stopScroll();
    ((LinearLayoutManager)getLayoutManager()).scrollToPositionWithOffset(pos,0);
}

答案 13 :(得分:0)

如果您在nestedScrollView中使用recyclerview,则必须滚动nestScrollview

nestedScrollview.smoothScrollTo(0,0)

答案 14 :(得分:0)

可接受的答案会起作用,但也可能会中断。出现此问题的主要原因是,在要求滚动视图时,回收站视图可能尚未准备好。最好的解决方案是等待回收器视图准备就绪,然后滚动。幸运的是,android提供了一种这样的选项。下面的解决方案是针对Kotlin的,您可以尝试使用相同的Java替代方法,它将起作用。

newsRecyclerView.post {
    layoutManager?.scrollToPosition(viewModel.selectedItemPosition)
}

post runnable方法可用于每个View元素,并在视图准备就绪后执行,从而确保在需要时准确执行代码。

答案 15 :(得分:0)

在我的情况下,您可以每次使用LinearSmoothScroller进行此操作:

  1. 首先创建LinearSmoothScroller的实例:
  LinearSmoothScroller smoothScroller=new LinearSmoothScroller(activity){
            @Override
            protected int getVerticalSnapPreference() {
                return LinearSmoothScroller.SNAP_TO_START;
            }
        };
  1. 然后,当您要将回收站视图滚动到任何位置时,请执行以下操作:
smoothScroller.setTargetPosition(pos);  // pos on which item you want to scroll recycler view
recyclerView.getLayoutManager().startSmoothScroll(smoothScroller);

完成。

答案 16 :(得分:0)

有同样的问题。我的问题是,在我尝试滚动之后,我在异步任务中用数据重新填充了视图。从onPostExecute ofc修复了这个问题。延迟修复了这个问题,因为当滚动执行时,列表已经被重新填充。