我实现了一个水平滚动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。但是这个解决方案对我也有用,但正如我之前所说的那样,只是部分解决了。
如果有人找到了我的作品,我会感激它。这是gist到我的自定义代码ReyclerView
。
答案 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
进行此操作:
LinearSmoothScroller smoothScroller=new LinearSmoothScroller(activity){ @Override protected int getVerticalSnapPreference() { return LinearSmoothScroller.SNAP_TO_START; } };
smoothScroller.setTargetPosition(pos); // pos on which item you want to scroll recycler view recyclerView.getLayoutManager().startSmoothScroll(smoothScroller);
完成。
答案 16 :(得分:0)
有同样的问题。我的问题是,在我尝试滚动之后,我在异步任务中用数据重新填充了视图。从onPostExecute ofc修复了这个问题。延迟修复了这个问题,因为当滚动执行时,列表已经被重新填充。