Reputation: 4104
I want to track how much a scroll is needed for my users in my app. So I would like to count the amount of recyclerview items that have been displayed in a user's screen before he takes some action.
Is there a way to calculate this? Maybe working with visible items & scroll position?
I am using a RecyclerView
with a GridLayoutManager
Upvotes: 1
Views: 1881
Reputation: 1
As per @Rene Ferrari's answer i modified the code when scroll to top, you could do something like this in java:
private int oldFirstPos = -1, oldLastPos = -1,totalItemsViewed = 0;
rv.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
GridLayoutManager layoutManager = ((GridLayoutManager)recyclerView.getLayoutManager());
int currFirstPos = layoutManager.findFirstCompletelyVisibleItemPosition();
int currLastPos = layoutManager.findLastCompletelyVisibleItemPosition();
totalItemCount = layoutManager.getItemCount();
if(oldFirstPos == -1){
totalItemsViewed += currLastPos - currFirstPos + 1;
}else{
if(dy > 0){
totalItemsViewed += Math.abs(currLastPos - oldLastPos);
}else{
totalItemsViewed -= Math.abs(oldLastPos - currLastPos);
}
}
oldLastPos = currLastPos;
oldFirstPos = currFirstPos;
Log.e("totalItemsViewed",totalItemsViewed);
}
});
Upvotes: 0
Reputation: 4216
I just figured out a solution:
var oldFirstPos = -1
var oldLastPos = -1
var totalItemsViewed = 0
with(rvBasic) {
layoutManager = gridLayoutManager
adapter = BasicRecyclerViewAdapter(layoutInflater, items)
addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView?, dx: Int, dy: Int) {
super.onScrolled(recyclerView, dx, dy)
val currFirstPos = gridLayoutManager.findFirstCompletelyVisibleItemPosition()
val currLastPos = gridLayoutManager.findLastCompletelyVisibleItemPosition()
totalItemsViewed += when (oldFirstPos) {
-1 -> currLastPos - currFirstPos + 1
else -> when {
dy > 0 -> Math.abs(currLastPos - oldLastPos) //scrolling to bottom
else -> Math.abs(currFirstPos - oldFirstPos) //scrolling to top
}
}
oldLastPos = currLastPos
oldFirstPos = currFirstPos
Log.d("Items viewed", "items viewed: ${totalItemsViewed}")
}
})
}
My idea was the following: Grab the current firstVisibleCompletelyVisiblePosition
and lastCompletelyVisiblePosition
and then calculate the delta of those positions since the last scroll depending on the scroll direction. The initial scroll is identified by setting the oldFirstPos
to -1
(oldLastPos
could theoretically be any value), since the position in the GridLayoutManager
starts at 0.
To identify the amount initially visible - and therefore viewed - items the following simple formula is needed: currLastPos - currFirstPos + 1
(the +1
is there since the position starts at 0).
When the user now scrolls to the bottom dy gets greater than 0
and therefore the absolute change in the lastPos
is important for us. The same applies when the scroll is in the opposite direction. Though, with the key difference, that now the absolute change in the firstPos
is the change in the amount of visible items since the last onScrolled
was triggered.
Why is it important to adapt the position change calculation when the scroll direction changes?
Lets assume you have 19 items in your Grid with a spanCount of 4
, hence the last row will consist of 3 items. Scrolling down to it, the totalItemsViewed
will count up to 19. Now when you scroll back up you will miss an item, because if you scroll a little bit upwards the lastPos
will change from 19 to 16, but the firstPos
will change from (depending on how many items are visible at a time on the display) 12 to 8. Now you have basically lost an item. If your items % spanCount == 0
then you will be fine without adapting the change according to the scroll direction.
Note: this solution only works with a vertical GridLayoutManager (horizontal is similar though)
Upvotes: 1