From 5e8bce587b0abe31a218ebe2982ddbb5cb51bc35 Mon Sep 17 00:00:00 2001 From: David Oliver Date: Fri, 15 Oct 2021 11:28:09 -0400 Subject: [PATCH] fix(listview): [Android] Correct scroll offsets if necessary when scrolling upwards in list --- .../VirtualizingPanelLayout.Android.cs | 26 ++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/src/Uno.UI/UI/Xaml/Controls/ListViewBase/VirtualizingPanelLayout.Android.cs b/src/Uno.UI/UI/Xaml/Controls/ListViewBase/VirtualizingPanelLayout.Android.cs index e2851b261b0f..fc315a610290 100644 --- a/src/Uno.UI/UI/Xaml/Controls/ListViewBase/VirtualizingPanelLayout.Android.cs +++ b/src/Uno.UI/UI/Xaml/Controls/ListViewBase/VirtualizingPanelLayout.Android.cs @@ -724,6 +724,8 @@ private int ComputeScrollRange(RecyclerView.State state, Orientation orientation remainingGroupExtent = remainingGroups * lastGroup.HeaderExtent; } + CorrectForEstimationErrors(); + var range = ContentOffset + remainingItemExtent + remainingGroupExtent + headerExtent + footerExtent + //TODO: An inline group header might actually be the view at the bottom of the viewport, we should take this into account GetChildEndWithMargin(base.GetChildAt(FirstItemView + ItemViewCount - 1)); @@ -732,6 +734,26 @@ private int ComputeScrollRange(RecyclerView.State state, Orientation orientation return range; } + /// + /// Correct the scroll offset, eg if items were added/removed or had their databound heights changed while they were scrolled out + /// of view. + /// + private void CorrectForEstimationErrors() + { + if (ContentOffset < 0) + { + // Scroll offset should always be non-negative + ContentOffset = 0; + } + + var firstVisible = GetFirstVisibleIndexPath(); + if (firstVisible.Row == 0 && firstVisible.Section == 0) + { + // If first item is in view, we can set ContentOffset exactly + ContentOffset = -GetContentStart(); + } + } + /// /// Update the internal state of the layout, as well as 'floating' views like group headers, when the scrolled offset changes. /// @@ -742,7 +764,9 @@ private int ComputeScrollRange(RecyclerView.State state, Orientation orientation private void ApplyOffset(int delta) { ContentOffset -= delta; - Debug.Assert(ContentOffset >= 0, "ContentOffset must be non-negative."); + + CorrectForEstimationErrors(); + foreach (var group in _groups) { group.Start += delta;