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

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

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

喜欢的东西:

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

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

RecyclerView不支持滚动到绝对位置

我如何滚动RecyclerView来把选定的项目放在视图的顶部?

281982 次浏览

如果你使用的是LinearLayoutManager或交错GridLayoutManager,它们各自都有一个scrollToPositionWithOffset方法,该方法既取该项开始的位置,也取该项从RecyclerView开始的偏移量,这似乎可以满足你的需要(将偏移量设置为0应该与顶部对齐)。

例如:

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

//滚动项目pos

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

在回收视图中使用布局管理器的实例,然后调用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());

好吧,就是这样,它对我来说很好

你只需要调用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。

我使用下面的代码来平滑滚动一个项目(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);

似乎能快速解决问题。

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

layoutmanager.scrollToPosition(int position);

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

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

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

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

只需简单地调用这个方法:

((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;
}
};
步骤2:你需要添加这段代码任何你想要执行滚动到特定位置的事件。 首先你需要设置目标位置为SmoothScroller
smoothScroller.setTargetPosition(position);

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

mRecyclerView.getLayoutManager().startSmoothScroll(smoothScroller);

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

recyclerView.smoothScrollToPosition(position);

没有错误

创建动画

简介

没有一个答案解释如何在顶部显示最后一项。因此,答案只适用于上面或下面仍然有足够的项来填充剩余的RecyclerView的项。例如,如果有59个元素,第56个元素被选中,它应该在顶部,如下图所示:

example

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

解决方案

我们可以使用linearLayoutManager.scrollToPositionWithOffset(pos, 0)RecyclerViewAdapter中的附加逻辑来处理这些情况——通过在最后一项下面添加自定义边距(如果最后一项不可见,则意味着有足够的空间填充RecyclerView)。自定义边距可以是根视图高度和项目高度之间的差值。因此,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);


...
}
...

如果你的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);
}

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

你可能会注意到,如果在adapter.submitList之后调用,则调用recyclerView.scrollToPosition(pos)(recyclerView.layoutManager as LinearLayoutManager).scrollToPositionWithOffset(pos, offset)将不起作用。这是因为differ在后台线程中查找更改,然后异步通知适配器更改。在一个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) }

请注意,如果scrollToPosition不工作,请注意你的RecyclerView是在一个NestedScrollView;参考这篇文章

这很简单

recyclerView.scrollToPosition(position)