问题
I have a recyclerview with horizontal layout and only one view is visible at a time:
mRecyclerView = findViewById(R.id.rvmain);
mRecyclerView.setOnFlingListener(null);
final SnapHelper snapHelper = new LinearSnapHelper();
snapHelper.attachToRecyclerView(mRecyclerView);
mLayoutManager = new LinearLayoutManager(this,LinearLayoutManager.HORIZONTAL,false);
mRecyclerView.setLayoutManager(mLayoutManager);
mAdapter = new MainActivityRVAdapter(postsModels,MainActivity.this);
mRecyclerView.setAdapter(mAdapter);
using onScrolllistener, everytime I scroll I want to know the starting position and end position. I am using the below code:
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
if(count == 0) {
View centerView = snapHelper.findSnapView(mLayoutManager);
if(centerView != null){
initial_position = mLayoutManager.getPosition(centerView);
//initial_position2 = ((LinearLayoutManager)mRecyclerView.getLayoutManager()).findFirstVisibleItemPosition();
Log.e("Initial Item Position:",""+initial_position);
//Log.e("Initial Item Position2:",""+initial_position2);
}
count ++;
}
// get newstate position
if(newState == AbsListView.OnScrollListener.SCROLL_STATE_IDLE) {
View centerView = snapHelper.findSnapView(mLayoutManager);
if(centerView != null){
int pos = mLayoutManager.getPosition(centerView);
count = 0; // in idle state clear the count again
Log.e("Snapped Item Position:",""+pos);
}
}
}
The result i get is:
E/Initial Item Position:: 0
E/Snapped Item Position:: 1
E/Initial Item Position:: 1
E/Snapped Item Position:: 1
E/Initial Item Position:: 1
E/Snapped Item Position:: 1
E/Initial Item Position:: 1
E/Snapped Item Position:: 1
And it returns positions multiple times. I wanted to check the difference between final and initial positions. I wanted only the start and end so that i can compare and check i.e:
E/Initial Item Position:: 0 and
E/Snapped Item Position:: 1
回答1:
I've met the same problem. And what i found:
RecyclerView.OnScrollListener
calls onScrolled(...) multiple times while in SCROLL_STATE_DRAGGING or SCROLL_STATE_SETTLING.
I started listen to a second calback:onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState)
We are interesting in a final state SCROLL_STATE_IDLE.
So in this case we have to override onScrollStateChanged(...)
, check and ignore all states except SCROLL_STATE_IDLE
and get final position while idle.
But as described in docs, onScrolled(...)
will also be called if visible item range changes after a layout calculation. In that case, dx and dy will be 0.
On practice i found that if we call adapter.notifyDataSetChanged()
and visible position is "0" (ie. on first start), onScrollStateChanged(...)
will not be called and onScrolled(...)
will be called once with dx == dy == 0.
A final variant could be as following:
private int recyclerVisiblePosition;
@Override
public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
if (newState != RecyclerView.SCROLL_STATE_IDLE) {
return;
}
getNewPosition(recyclerView);
}
@Override
public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
if (dx == 0 && dy == 0) {
getNewPosition(recyclerView);
}
}
private void getNewPosition(@NonNull final RecyclerView recyclerView) {
final LinearLayoutManager layoutManager = ((LinearLayoutManager)recyclerView.getLayoutManager());
if (layoutManager != null) {
recyclerVisiblePosition = layoutManager.findLastVisibleItemPosition();
}
}
来源:https://stackoverflow.com/questions/49151476/recyclerview-onscrolllistener-being-called-multiple-times-for-once-instance-of