RecyclerView scrollToPosition 不触发 scrollListener
RecyclerView scrollToPosition not trigger scrollListener
我正在使用带有 ScrollListener 的 RecyclerView:
mRecyclerView.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);
// Do my logic
}
});
当我用手指滚动时,滚动侦听器触发正常。
但是当我按顺序滚动时,就像那样:
mRecyclerView.scrollToPosition(LAST_POSITION);
没有触发滚动监听。
这是一个已知问题。这是因为 RecyclerView 不知道 LayoutManager 将如何处理滚动或者它是否会处理它。
在下一个版本中,如果第一个和/或最后一个子位置在布局后发生变化(这通常是调用滚动到位置的结果),您将收到对 onScrolled
的调用。
不幸的是,dx 和 dy 将为 0,因为 RecyclerView 并不真正知道布局管理器滚动了多少来处理 scrollTo
请求。或者,您也可以使用 ViewTreeObserver 的滚动回调。
它有点简陋,但您可以尝试 RecyclerView 的这个子类:
public class PatchedRecyclerView extends RecyclerView {
private OnScrollListener listener;
public PatchedRecyclerView(Context context) {
super(context);
}
public PatchedRecyclerView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public PatchedRecyclerView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
@Override
public void setOnScrollListener(OnScrollListener listener) {
super.setOnScrollListener(listener);
this.listener = listener;
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int preFirstChildPosition = getChildCount() != 0 ? getChildViewHolder(getChildAt(0)).getPosition() : -1;
int preLastChildPosition = getChildCount() != 0 ? getChildViewHolder(getChildAt(getChildCount() - 1)).getPosition() : -1;
super.onLayout(changed, l, t, r, b);
int postFirstChildPosition = getChildCount() != 0 ? getChildViewHolder(getChildAt(0)).getPosition() : -1;
int postLastChildPosition = getChildCount() != 0 ? getChildViewHolder(getChildAt(getChildCount() - 1)).getPosition() : -1;
// TODO: Insert proper DX and DY values
if (preFirstChildPosition != postFirstChildPosition || preLastChildPosition != postLastChildPosition)
listener.onScrolled(this, 0, 0);
}
}
我遇到了同样的问题并找到了解决方法,即在布局管理器中使用 smoothScrollToPosition。此方法触发滚动侦听器。所以基本上这就是我曾经拥有的:
recyclerView.scrollToPosition(recyclerAdapter.getItemCount() - 1);
现在我改成了这样:
recyclerView.getLayoutManager().smoothScrollToPosition(recyclerView, null, recyclerAdapter.getItemCount() - 1);
对我来说效果很好。
你的建议很好。
Button maisOpcoes = (Button) findViewById(R.id.maisOpcoes);
maisOpcoes.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
recyclerView.getLayoutManager().smoothScrollToPosition(recyclerView, null , recyclerViewTmd.getItemCount() - 1);
maisOpcoes.setVisibility(View.GONE);
}
});
int mFirst=0, mLast=0;
recyclerview.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) recyclerview.getLayoutManager();
mLast = llm.findLastCompletelyVisibleItemPosition();
mFirst = llm.findFirstCompletelyVisibleItemPosition();
}
});
imgRight.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
LinearLayoutManager llm = (LinearLayoutManager) recyclerview.getLayoutManager();
llm.scrollToPositionWithOffset(mLast + 1, List.length());
}
});
imgLeft.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
LinearLayoutManager llm = (LinearLayoutManager) recyclerview.getLayoutManager();
llm.scrollToPositionWithOffset(mFirst - 1, List.length());
}
});
要在回收器视图上显式触发 onScrollListener,请使用:
recyclerView.smoothScrollBy(x, y);
//x is the horizontal displacement and y is vertical displacement.
我无法使用 smoothScrollToPosition(我们过去遇到过一些问题)并且根据第一项的位置似乎不可靠,所以我最终使用了 yigit 的答案(它有已经发布),不幸的是它并不总是有效(我不确定为什么)。
所以我最终使用了一个我之前添加到 RecyclerView 的滚动监听器,遗憾的是我找不到这个解决方案的来源。
如果你需要一个固定的监听器,我认为你不应该使用这个监听器,幸运的是我只需要在特定时间监听。
覆盖 RecyclerView:
public class MyRecyclerView extends RecyclerView {
public interface OnScrollStoppedListener{
void onScrollStopped();
}
private static final int NEW_CHECK_INTERVAL = 100;
private OnScrollStoppedListener mOnScrollStoppedListener;
private Runnable mScrollerTask;
private int mInitialPosition;
public MyRecyclerView(Context context) {
super(context);
init();
}
public MyRecyclerView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init();
}
public MyRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
private void init() {
mScrollerTask = new Runnable() {
public void run() {
int newPosition = getScrollY();
if(mInitialPosition - newPosition == 0) {//has stopped
if(mOnScrollStoppedListener !=null) {
mOnScrollStoppedListener.onScrollStopped();
}
} else {
startScrollerTask();
}
}
};
}
public void setOnScrollStoppedListener(MyRecyclerView.OnScrollStoppedListener listener){
mOnScrollStoppedListener = listener;
}
public void startScrollerTask() {
mInitialPosition = getScrollY();
postDelayed(mScrollerTask, NEW_CHECK_INTERVAL);
}
}
用法:
myRecyclerView.setOnScrollStoppedListener(new MyRecyclerView.OnScrollStoppedListener() {
@Override
public void onScrollStopped() {
// Do your thing
}
});
myRecyclerView.startScrollerTask();
请尝试
LinearLayoutManager.scrollToPositionWithOffset(int, int).
LayoutManager.scrollToPosition()
可能效果不佳,但 LinearLayoutManager.scrollToPositionWithOffset()
可以。还有类似的 GridLayoutManager。
或者我们必须编写自定义的实现方式,因为 RecyclerView 不知道 LayoutManager 是如何滚动视图的。
class CustomLayoutManager extends LinearLayoutManager{
public void smoothScrollToPosition(RecyclerView recyclerView,
RecyclerView.State state, final int position) {
LinearSmoothScroller smoothScroller =
new LinearSmoothScroller(mContext) {
//Override the methods and write your implementation as per your requirement
//one which controls the direction and another one calculate the speed per Pixel
}
}
有时这会起作用
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
yourList.scrollToPosition(position);
}
}, 200);
我正在使用带有 ScrollListener 的 RecyclerView:
mRecyclerView.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);
// Do my logic
}
});
当我用手指滚动时,滚动侦听器触发正常。
但是当我按顺序滚动时,就像那样:
mRecyclerView.scrollToPosition(LAST_POSITION);
没有触发滚动监听。
这是一个已知问题。这是因为 RecyclerView 不知道 LayoutManager 将如何处理滚动或者它是否会处理它。
在下一个版本中,如果第一个和/或最后一个子位置在布局后发生变化(这通常是调用滚动到位置的结果),您将收到对 onScrolled
的调用。
不幸的是,dx 和 dy 将为 0,因为 RecyclerView 并不真正知道布局管理器滚动了多少来处理 scrollTo
请求。或者,您也可以使用 ViewTreeObserver 的滚动回调。
它有点简陋,但您可以尝试 RecyclerView 的这个子类:
public class PatchedRecyclerView extends RecyclerView {
private OnScrollListener listener;
public PatchedRecyclerView(Context context) {
super(context);
}
public PatchedRecyclerView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public PatchedRecyclerView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
@Override
public void setOnScrollListener(OnScrollListener listener) {
super.setOnScrollListener(listener);
this.listener = listener;
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int preFirstChildPosition = getChildCount() != 0 ? getChildViewHolder(getChildAt(0)).getPosition() : -1;
int preLastChildPosition = getChildCount() != 0 ? getChildViewHolder(getChildAt(getChildCount() - 1)).getPosition() : -1;
super.onLayout(changed, l, t, r, b);
int postFirstChildPosition = getChildCount() != 0 ? getChildViewHolder(getChildAt(0)).getPosition() : -1;
int postLastChildPosition = getChildCount() != 0 ? getChildViewHolder(getChildAt(getChildCount() - 1)).getPosition() : -1;
// TODO: Insert proper DX and DY values
if (preFirstChildPosition != postFirstChildPosition || preLastChildPosition != postLastChildPosition)
listener.onScrolled(this, 0, 0);
}
}
我遇到了同样的问题并找到了解决方法,即在布局管理器中使用 smoothScrollToPosition。此方法触发滚动侦听器。所以基本上这就是我曾经拥有的:
recyclerView.scrollToPosition(recyclerAdapter.getItemCount() - 1);
现在我改成了这样:
recyclerView.getLayoutManager().smoothScrollToPosition(recyclerView, null, recyclerAdapter.getItemCount() - 1);
对我来说效果很好。
你的建议很好。
Button maisOpcoes = (Button) findViewById(R.id.maisOpcoes);
maisOpcoes.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
recyclerView.getLayoutManager().smoothScrollToPosition(recyclerView, null , recyclerViewTmd.getItemCount() - 1);
maisOpcoes.setVisibility(View.GONE);
}
});
int mFirst=0, mLast=0;
recyclerview.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) recyclerview.getLayoutManager();
mLast = llm.findLastCompletelyVisibleItemPosition();
mFirst = llm.findFirstCompletelyVisibleItemPosition();
}
});
imgRight.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
LinearLayoutManager llm = (LinearLayoutManager) recyclerview.getLayoutManager();
llm.scrollToPositionWithOffset(mLast + 1, List.length());
}
});
imgLeft.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
LinearLayoutManager llm = (LinearLayoutManager) recyclerview.getLayoutManager();
llm.scrollToPositionWithOffset(mFirst - 1, List.length());
}
});
要在回收器视图上显式触发 onScrollListener,请使用:
recyclerView.smoothScrollBy(x, y);
//x is the horizontal displacement and y is vertical displacement.
我无法使用 smoothScrollToPosition(我们过去遇到过一些问题)并且根据第一项的位置似乎不可靠,所以我最终使用了 yigit 的答案(它有已经发布),不幸的是它并不总是有效(我不确定为什么)。
所以我最终使用了一个我之前添加到 RecyclerView 的滚动监听器,遗憾的是我找不到这个解决方案的来源。
如果你需要一个固定的监听器,我认为你不应该使用这个监听器,幸运的是我只需要在特定时间监听。
覆盖 RecyclerView:
public class MyRecyclerView extends RecyclerView {
public interface OnScrollStoppedListener{
void onScrollStopped();
}
private static final int NEW_CHECK_INTERVAL = 100;
private OnScrollStoppedListener mOnScrollStoppedListener;
private Runnable mScrollerTask;
private int mInitialPosition;
public MyRecyclerView(Context context) {
super(context);
init();
}
public MyRecyclerView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init();
}
public MyRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
private void init() {
mScrollerTask = new Runnable() {
public void run() {
int newPosition = getScrollY();
if(mInitialPosition - newPosition == 0) {//has stopped
if(mOnScrollStoppedListener !=null) {
mOnScrollStoppedListener.onScrollStopped();
}
} else {
startScrollerTask();
}
}
};
}
public void setOnScrollStoppedListener(MyRecyclerView.OnScrollStoppedListener listener){
mOnScrollStoppedListener = listener;
}
public void startScrollerTask() {
mInitialPosition = getScrollY();
postDelayed(mScrollerTask, NEW_CHECK_INTERVAL);
}
}
用法:
myRecyclerView.setOnScrollStoppedListener(new MyRecyclerView.OnScrollStoppedListener() {
@Override
public void onScrollStopped() {
// Do your thing
}
});
myRecyclerView.startScrollerTask();
请尝试
LinearLayoutManager.scrollToPositionWithOffset(int, int).
LayoutManager.scrollToPosition()
可能效果不佳,但 LinearLayoutManager.scrollToPositionWithOffset()
可以。还有类似的 GridLayoutManager。
或者我们必须编写自定义的实现方式,因为 RecyclerView 不知道 LayoutManager 是如何滚动视图的。
class CustomLayoutManager extends LinearLayoutManager{
public void smoothScrollToPosition(RecyclerView recyclerView,
RecyclerView.State state, final int position) {
LinearSmoothScroller smoothScroller =
new LinearSmoothScroller(mContext) {
//Override the methods and write your implementation as per your requirement
//one which controls the direction and another one calculate the speed per Pixel
}
}
有时这会起作用
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
yourList.scrollToPosition(position);
}
}, 200);