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

Android 滚动RecyclerView可在顶部显示所选项目,android,scroll,android-recyclerview,Android,Scroll,Android Recyclerview,我正在寻找一种滚动RecyclerView以在顶部显示所选项目的方法 在列表视图中我可以通过使用滚动到(x,y)并获得需要居中的元素的顶部来实现这一点 比如: @Override public void onItemClick(View v, int pos){ mylistView.scrollTo(0, v.getTop()); } 问题是RecyclerView在使用它的scrollTo方法时返回一个错误 RecyclerView不支持滚动到绝对位置 如何滚动RecyclerVi

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

列表视图中
我可以通过使用
滚动到(x,y)
并获得需要居中的元素的顶部来实现这一点

比如:

@Override
public void onItemClick(View v, int pos){
    mylistView.scrollTo(0, v.getTop());
}
问题是
RecyclerView
在使用它的
scrollTo
方法时返回一个错误

RecyclerView不支持滚动到绝对位置


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

如果您使用的是
LinearLayoutManager
或交错
GridLayoutManager
,则它们都有一个方法,该方法同时获取项目开始位置以及从
RecyclerView
开始的偏移量,这似乎可以满足您的需要(将偏移设置为0应该与顶部对齐)

例如:

//Scroll item 2 to 20 pixels from the top
linearLayoutManager.scrollToPositionWithOffset(2, 20);
//滚动项目位置

linearLayoutManager.scrollToPositionWithOffset(pos, 0);

如果您正在寻找垂直线性布局管理器,则可以使用自定义的
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;
        }
    }
}

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

与调速器相同

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;
    }
}
}

试试对我有用的,酷

创建一个变量
private static int displayedposition=0

现在了解您的RecyclerView在活动中的位置

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/

你只需要调用
recyclerview.scrollToPosition(position)
。那很好

如果要在适配器中调用它,只需让适配器具有recyclerview实例或包含recyclerview的活动或片段,然后在其中实现方法
getRecyclerview()


我希望它能对您有所帮助。

在刷新单击的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。

我使用下面的代码将项(此视图)平滑滚动到顶部。
它也适用于具有不同高度视图的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);

对于快速解决方案来说,似乎已经足够好了。

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

layoutmanager.scrollToPosition(int position);

在我的例子中,我的
RecyclerView
有一个这样的填充顶部

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

只需简单地调用此方法:

((LinearLayoutManager)recyclerView.getLayoutManager()).scrollToPositionWithOffset(yourItemPosition,0);
而不是:

recyclerView.scrollToPosition(yourItemPosition);

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

mRecyclerView.getLayoutManager().scrollToPosition(position);
如果要显示滚动运动,则需要添加以下代码。 =>步骤1:您需要声明SmoothScroller

RecyclerView.SmoothScroller smoothScroller = new
                LinearSmoothScroller(this.getApplicationContext()) {
                    @Override
                    protected int getVerticalSnapPreference() {
                        return LinearSmoothScroller.SNAP_TO_START;
                    }
                };
smoothScroller.setTargetPosition(position);
=>第2步:您需要在任何要执行的事件中添加此代码,然后滚动到特定位置。 =>首先需要将目标位置设置为SmoothScroller

RecyclerView.SmoothScroller smoothScroller = new
                LinearSmoothScroller(this.getApplicationContext()) {
                    @Override
                    protected int getVerticalSnapPreference() {
                        return LinearSmoothScroller.SNAP_TO_START;
                    }
                };
smoothScroller.setTargetPosition(position);
=>然后需要将SmoothScroller设置为LayoutManager

mRecyclerView.getLayoutManager().startSmoothScroll(smoothScroller);

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

recyclerView.smoothScrollToPosition(position);
没有错误

创建动画

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

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

解决方案 我们可以使用
linearLayoutManager.scrollToPositionWithOffset(pos,0)
RecyclerView
适配器中的附加逻辑来处理这些情况,方法是在最后一项下方添加自定义边距(如果最后一项不可见,则表示有足够的空间填充
RecyclerView
)。自定义边距可以是根视图高度和项目高度之间的差值。因此,
RecyclerView
适配器将如下所示:

...
@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);

    ...
} 
...

如果您的
LayoutManager
LinearLayoutManager
,则可以使用
scrollToPositionWithOffset(位置,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);
        }

这里我要补充的是如何使它与
DiffUtil
ListAdapter

您可能会注意到,如果在
适配器.提交列表
之后调用
recyclerView.scrollToPosition(pos)
(recyclerView.layoutManager作为LinearLayoutManager)。scrollToPositionWithOffset(pos,offset)
将不起作用。这是因为differ在后台线程中查找更改,然后异步通知适配器更改。因此,我看到了几个错误的答案和不必要的延迟等来解决这个问题

为了正确处理这种情况,
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) }

请注意,如果
scrollToPosition
不工作,请注意您的RecyclerView位于NestedScrollView中

如果任何人在恢复RecyclerView的滚动位置时需要此选项,这就是保存滚动位置的方法(scrollToPositionWithOffset方法的两个参数):int index=linearLayoutManager.findFirstVisibleItemPosition();视图v=linearLayoutManager.getChildAt(0);int top=(v==null)?0:(v.getTop()-linearLayoutManager.getPaddin