M Rajoy
M Rajoy

Reputation: 4104

Counting how many RecyclerView items the user has seen

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

Answers (2)

Pauras_
Pauras_

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

Rene Ferrari
Rene Ferrari

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

Related Questions