public override void OnScrolled(RecyclerView recyclerView, int dx, int dy) { base.OnScrolled(recyclerView, dx, dy); // TODO: These offsets will be incorrect upon row size or count change. // They are currently provided in place of LayoutManager's default offset calculation // because it does not report accurate values in the presence of uneven rows. // See https://stackoverflow.com/questions/27507715/android-how-to-get-the-current-x-offset-of-recyclerview _horizontalOffset += dx; _verticalOffset += dy; var firstVisibleItemIndex = -1; var lastVisibleItemIndex = -1; var centerItemIndex = -1; if (recyclerView.GetLayoutManager() is LinearLayoutManager linearLayoutManager) { firstVisibleItemIndex = linearLayoutManager.FindFirstVisibleItemPosition(); lastVisibleItemIndex = linearLayoutManager.FindLastVisibleItemPosition(); centerItemIndex = CalculateCenterItemIndex(firstVisibleItemIndex, lastVisibleItemIndex, linearLayoutManager); } var itemsViewScrolledEventArgs = new ItemsViewScrolledEventArgs { HorizontalDelta = dx, VerticalDelta = dy, HorizontalOffset = _horizontalOffset, VerticalOffset = _verticalOffset, FirstVisibleItemIndex = firstVisibleItemIndex, CenterItemIndex = centerItemIndex, LastVisibleItemIndex = lastVisibleItemIndex }; _itemsView.SendScrolled(itemsViewScrolledEventArgs); // Don't send RemainingItemsThresholdReached event for non-linear layout managers // This can also happen if a layout pass has not happened yet if (lastVisibleItemIndex == -1) { return; } switch (_itemsView.RemainingItemsThreshold) { case -1: return; case 0: if (lastVisibleItemIndex == _itemsViewAdapter.ItemCount - 1) { _itemsView.SendRemainingItemsThresholdReached(); } break; default: if (_itemsViewAdapter.ItemCount - 1 - lastVisibleItemIndex <= _itemsView.RemainingItemsThreshold) { _itemsView.SendRemainingItemsThresholdReached(); } break; } }