ChatGPT解决这个技术问题 Extra ChatGPT

滚动 RecyclerView 以在顶部显示所选项目

我正在寻找一种滚动 RecyclerView 以在顶部显示所选项目的方法。

ListView 中,我可以通过使用 scrollTo(x,y) 并获取需要居中的元素的顶部来做到这一点。

就像是:

@Override
public void onItemClick(View v, int pos){
    mylistView.scrollTo(0, v.getTop());
}

问题是 RecyclerView 在使用它的 scrollTo 方法时返回错误

RecyclerView 不支持滚动到绝对位置

如何滚动 RecyclerView 以将所选项目置于视图顶部?


m
mohammadReza Abiri

如果您使用的是 LinearLayoutManager 或 Staggered GridLayoutManager,它们每个都有一个 scrollToPositionWithOffset 方法,该方法既获取位置,也获取项目起点相对于 RecyclerView 起点的偏移量,看起来像将完成您需要的(将偏移量设置为 0 应与顶部对齐)。

例如:

//Scroll item 2 to 20 pixels from the top
linearLayoutManager.scrollToPositionWithOffset(2, 20);

如果有人需要这个来恢复 RecyclerView 的滚动位置,这就是如何保存滚动位置(scrollToPositionWithOffset 方法的两个参数): int index = linearLayoutManager.findFirstVisibleItemPosition();查看 v = linearLayoutManager.getChildAt(0); int top = (v == null) ? 0 : (v.getTop() - linearLayoutManager.getPaddingTop());
我似乎不知道什么时候调用scrollToPosition?如果选择侦听器位于单个视图上,如何通知 RecyclerView 对象(可以访问其布局管理器,可以调用scrollToPosition)选择项目?
@Nonos,您可以先将使用的 LayoutManager 存储在 RecylerView.setLayoutManager() 中,然后直接访问它。像这样: LinearLayoutManager llm = new LinearLayoutManager(this); mRecyclerView.setLayoutManager(llm); ...(稍后在代码中)... llm.scrollToPositionWithOffset(2, 20);
如果我想“顺利”滚动到顶部怎么办?
@Tiago,我还没有玩过它,但是这个由 3 部分组成的系列文章可能会有所帮助:blog.stylingandroid.com/scrolling-recyclerview-part-1
I
ItamarG3

如果您正在寻找垂直 LinearLayout 管理器,您可以使用自定义 LinearSmoothScroller 实现平滑滚动:

import android.content.Context;
import android.graphics.PointF;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.LinearSmoothScroller;
import android.support.v7.widget.RecyclerView;

public class SnappingLinearLayoutManager extends LinearLayoutManager {

    public SnappingLinearLayoutManager(Context context, int orientation, boolean reverseLayout) {
        super(context, orientation, reverseLayout);
    }

    @Override
    public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state,
                                       int position) {
        RecyclerView.SmoothScroller smoothScroller = new TopSnappedSmoothScroller(recyclerView.getContext());
        smoothScroller.setTargetPosition(position);
        startSmoothScroll(smoothScroller);
    }

    private class TopSnappedSmoothScroller extends LinearSmoothScroller {
        public TopSnappedSmoothScroller(Context context) {
            super(context);

        }

        @Override
        public PointF computeScrollVectorForPosition(int targetPosition) {
            return SnappingLinearLayoutManager.this
                    .computeScrollVectorForPosition(targetPosition);
        }

        @Override
        protected int getVerticalSnapPreference() {
            return SNAP_TO_START;
        }
    }
}

在回收视图中使用 layoutmanager 的实例,然后调用 recyclerView.smoothScrollToPosition(pos); 将平滑滚动到回收视图顶部的选定位置


真正有用的答案!我还在 TopSnappedSmoothScroller 中为水平列表覆盖 getHorizontalSnapPreference()
我是否正确地观察到这仅适用于在它们之后/下方仍有足够空间来填充剩余 RecyclerView 的项目?也就是说:这似乎不适用于最后一项 - 无论 SNAP_TO_START 是什么,RecyclerView 只会滚动直到该项目在底部可见,但不会一直向上移动。关于如何实现这一目标的任何想法? (我已经通过在 RecyclerView 上设置自定义填充来解决这个问题,但这似乎并不正确......)
有时项目会获得焦点,我有一个可绘制的焦点以用于项目视图。
@david.mihola 你找到“正确”的方法了吗?我已经为我的 recyclerview 设置了自定义填充,但我遇到了一些奇怪的问题......
H
HBQ

//滚动项目位置

linearLayoutManager.scrollToPositionWithOffset(pos, 0);

有什么办法可以顺利做到这一点?
@MarkBuikema 我以前发现过。谢谢你的关注,伙计
V
Victor Häggqvist

您只需调用 recyclerview.scrollToPosition(position)。没关系!

如果你想在适配器中调用它,只需让你的适配器有recyclerview的实例或包含recyclerview的activity或fragment,然后在其中实现方法getRecyclerview()

我希望它可以帮助你。


这对我有用 - recyclerView.scrollToPosition(0);把我放在首位。
不工作你必须使用 linearLayoutManager.scrollToPositionWithOffset(pos, 0);
@Anil Ugale 那是胡说八道。当然可以。您可以将任何 LayoutManager 用于 RecyclerView。为什么以后要滚动时要关心它?我认为你做错了什么。 Recyclerview.scrollToPosition(position) 是一个调用 LayoutManager.scrollToPosition(position) 的便捷方法。
@TheincredibleJan 它并非在所有情况下都有效,layoutManager 通常负责滚动无论如何只是为了您的信息。
这并不总是将请求的项目放在问题的顶部。
g
gianlucaparadise

如果要在不显示滚动动作的情况下自动滚动,则需要编写以下代码:

mRecyclerView.getLayoutManager().scrollToPosition(position);

如果要显示滚动动作,则需要添加以下代码。 =>第1步:您需要声明SmoothScroller。

RecyclerView.SmoothScroller smoothScroller = new
                LinearSmoothScroller(this.getApplicationContext()) {
                    @Override
                    protected int getVerticalSnapPreference() {
                        return LinearSmoothScroller.SNAP_TO_START;
                    }
                };

=>第2步:您需要将此代码添加到任何要执行滚动到特定位置的事件。 =>首先您需要将目标位置设置为 SmoothScroller。

smoothScroller.setTargetPosition(position);

=>然后你需要将 SmoothScroller 设置为 LayoutManager。

mRecyclerView.getLayoutManager().startSmoothScroll(smoothScroller);

惊人!最简单的解决方案是,如果您需要在 RecyclerView 顶部显示目标元素的平滑滚动!谢谢!
A
Amir Hossein Ghasemi

只需简单地调用此方法:

((LinearLayoutManager)recyclerView.getLayoutManager()).scrollToPositionWithOffset(yourItemPosition,0);

代替:

recyclerView.scrollToPosition(yourItemPosition);

“代替”? Distwo 没有提到“scrollToPosition”。如果他使用它,就不会有任何问题。 scrollToPosition() 符合预期。
奇怪的是,使用 scrollToPositionWithOffset 对我有用,而 recyclerview.scrollToPosition 没有。虽然,我会要求 Amir 将 RecyclerView 更改为 recyclerview,因为它给人的感觉是这些方法是静态的,而它们不是。
u
user2212515

与调速器相同

public class SmoothScrollLinearLayoutManager extends LinearLayoutManager {
private static final float MILLISECONDS_PER_INCH = 110f;
private Context mContext;

public SmoothScrollLinearLayoutManager(Context context,int orientation, boolean reverseLayout) {
    super(context,orientation,reverseLayout);
    mContext = context;
}

@Override
public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state,
                                   int position) {
    RecyclerView.SmoothScroller smoothScroller = new TopSnappedSmoothScroller(recyclerView.getContext()){
        //This controls the direction in which smoothScroll looks for your view
        @Override
        public PointF computeScrollVectorForPosition(int targetPosition) {
            return new PointF(0, 1);
        }

        //This returns the milliseconds it takes to scroll one pixel.
        @Override
        protected float calculateSpeedPerPixel(DisplayMetrics displayMetrics) {
            return MILLISECONDS_PER_INCH / displayMetrics.densityDpi;
        }
    };
    smoothScroller.setTargetPosition(position);
    startSmoothScroll(smoothScroller);
}


private class TopSnappedSmoothScroller extends LinearSmoothScroller {
    public TopSnappedSmoothScroller(Context context) {
        super(context);

    }

    @Override
    public PointF computeScrollVectorForPosition(int targetPosition) {
        return SmoothScrollLinearLayoutManager.this
                .computeScrollVectorForPosition(targetPosition);
    }

    @Override
    protected int getVerticalSnapPreference() {
        return SNAP_TO_START;
    }
}
}

p
psicopante

试试对我有用的东西很酷!

创建变量 private static int displayedposition = 0;

现在查看 RecyclerView 在 Activity 中的位置。

myRecyclerView.setOnScrollListener(new RecyclerView.OnScrollListener() {
            @Override
            public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
                super.onScrollStateChanged(recyclerView, newState);
            }

            @Override
            public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
                super.onScrolled(recyclerView, dx, dy);

                LinearLayoutManager llm = (LinearLayoutManager) myRecyclerView.getLayoutManager();


                displayedposition = llm.findFirstVisibleItemPosition();


            }
        });

将此语句放置在您希望它放置在您的视图中显示的先前站点的位置。

LinearLayoutManager llm = (LinearLayoutManager) mRecyclerView.getLayoutManager();
        llm.scrollToPositionWithOffset(displayedposition , youList.size());

就是这样,它对我来说很好用\o/


L
Lucem

我不知道为什么我没有找到最佳答案,但它真的很简单。

recyclerView.smoothScrollToPosition(position);

没有错误

创建动画


你会把它放在活动或适配器的哪里?
这不会将点击的项目滚动到顶部——这首先是问题所在。
D
Distwo

在单击按钮刷新 RecyclerView 后,我做了什么来恢复滚动位置:

if (linearLayoutManager != null) {

    index = linearLayoutManager.findFirstVisibleItemPosition();
    View v = linearLayoutManager.getChildAt(0);
    top = (v == null) ? 0 : (v.getTop() - linearLayoutManager.getPaddingTop());
    Log.d("TAG", "visible position " + " " + index);
}

else{
    index = 0;
}

linearLayoutManager = new LinearLayoutManager(getApplicationContext());
linearLayoutManager.scrollToPositionWithOffset(index, top);

在创建 linearLayoutManager 对象之前获取第一个可见项的偏移量,并在实例化它之后调用 LinearLayoutManager 对象的 scrollToPositionWithOffset。


B
Beloo

我可以在这里补充的是如何使它与 DiffUtilListAdapter 一起工作

您可能会注意到,如果在 adapter.submitList 之后直接调用,调用 recyclerView.scrollToPosition(pos)(recyclerView.layoutManager as LinearLayoutManager).scrollToPositionWithOffset(pos, offset) 将不起作用。这是因为diff在后台线程中寻找变化,然后异步通知适配器变化。在一个 SO 上,我看到了几个错误的答案,有不必要的延迟 &等来解决这个问题。

为了正确处理这种情况,submitList 有一个回调,在应用更改时调用该回调。

因此,在这种情况下,正确的 kotlin 实现是:

//memorise target item here and a scroll offset if needed
adapter.submitList(items) { 
    val pos = /* here you may find a new position of the item or just use just a static position. It depends on your case */
    recyclerView.scrollToPosition(pos) 
}
//or
adapter.submitList(items) { recyclerView.smoothScrollToPosition(pos) }
//or etc
adapter.submitList(items) { (recyclerView.layoutManager as LinearLayoutManager).scrollToPositionWithOffset(pos, offset) }

您提到了重要的一点:“更改发生在后台线程中,然后异步通知适配器有关更改”。 TNX。
A
Anatolii

介绍

没有一个答案解释了如何在顶部显示最后一项。因此,答案仅适用于在其上方或下方仍有足够项目来填充剩余 RecyclerView 的项目。例如,如果有 59 个元素并且选择了第 56 个元素,则它应该位于顶部,如下图所示:

https://i.stack.imgur.com/oGfwI.png

那么,让我们在下一段中看看如何实现这一点。

解决方案

我们可以通过使用 linearLayoutManager.scrollToPositionWithOffset(pos, 0)RecyclerViewAdapter 中的附加逻辑来处理这些情况 - 通过在最后一项下方添加自定义边距(如果最后一项不可见,则意味着有足够的空间填充 {3 })。自定义边距可能是根视图高度和项目高度之间的差异。因此,您的 RecyclerView Adapter 如下所示:

...
@Override
public void onBindViewHolder(ViewHolder holder, final int position) {
    ...

    int bottomHeight = 0;
    int itemHeight = holder.itemView.getMeasuredHeight();
    // if it's the last item then add a bottom margin that is enough to bring it to the top
    if (position == mDataSet.length - 1) {
        bottomHeight = Math.max(0, mRootView.getMeasuredHeight() - itemHeight);
    }
    RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)holder.itemView.getLayoutParams();
    params.setMargins(0, 0, params.rightMargin, bottomHeight);
    holder.itemView.setLayoutParams(params);

    ...
} 
...

M
Mohamed Nageh

如果您的 LayoutManagerLinearLayoutManager,您可以在其上使用 scrollToPositionWithOffset(position,0);,它将使您的项目成为列表中的第一个可见项目。否则,您可以直接在 RecyclerView 上使用 smoothScrollToPosition

我最终使用了下面的代码。

 RecyclerView.LayoutManager layoutManager = mainList.getLayoutManager();
        if (layoutManager instanceof LinearLayoutManager) {
            // Scroll to item and make it the first visible item of the list.
            ((LinearLayoutManager) layoutManager).scrollToPositionWithOffset(position, 0);
        } else {
            mainList.smoothScrollToPosition(position);
        }

S
Sandeep Singh

在特定位置滚动,这对我有很大帮助。通过单击侦听器,您可以获得适配器中的位置

layoutmanager.scrollToPosition(int position);

L
Linh

在我的情况下,我的 RecyclerView 有一个像这样的填充顶部

<android.support.v7.widget.RecyclerView
     ...
     android:paddingTop="100dp"
     android:clipToPadding="false"
/>

然后要将项目滚动到顶部,我需要

recyclerViewLinearLayoutManager.scrollToPositionWithOffset(position, -yourRecyclerView.getPaddingTop());

F
FxRi4

请注意,如果 scrollToPosition 不起作用,请注意您的 RecyclerView 在 NestedScrollView 内; refer to this post


T
Tarif Chakder

这很简单

recyclerView.scrollToPosition(position)

但这不会滚动到屏幕顶部它可以在底部中间它只会显示选定的位置项
如果你想使用 recyclerView.scrollToPosition(0)
我有 10 个项目,我点击了某个按钮,如果你写 recyclerview.scrollToPosition(7),我需要滚动到第 7 项,它会滚动那个位置,但是如果我希望那个项目显示在屏幕顶部呢?
f
feheren.fekete

我使用下面的代码将项目(thisView)平滑滚动到顶部。它也适用于具有不同高度视图的 GridLayoutManager:

View firstView = mRecyclerView.getChildAt(0);
int toY = firstView.getTop();
int firstPosition = mRecyclerView.getChildAdapterPosition(firstView);
View thisView = mRecyclerView.getChildAt(thisPosition - firstPosition);
int fromY = thisView.getTop();

mRecyclerView.smoothScrollBy(0, fromY - toY);

似乎工作得很好,可以快速解决。


thisPosition 的值是多少?
这是您要平滑滚动到的位置
为什么不简单地使用 smoothScrollToPosition(int position) 而不进行任何计算?从哪里开始并不重要,不是吗?