Skip to content

Commit 262f346

Browse files
Fix off-by-one in AdjustGroupIndex, correct position < 0 handling, and clarify dataIndex semantics in FindNextDataIndex.
1 parent d4c6b1c commit 262f346

3 files changed

Lines changed: 24 additions & 16 deletions

File tree

src/Controls/src/Core/Handlers/Items/Android/RecyclerViewScrollListener.cs

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -96,9 +96,9 @@ protected virtual (int First, int Center, int Last) GetVisibleItemsIndex(Recycle
9696
if (itemsSource is not UngroupedItemsSource && itemsSource is IGroupableItemsViewSource groupable)
9797
{
9898
return (
99-
AdjustGroupIndex(groupable, firstVisibleItemIndex, hasHeader, hasFooter, itemsCount, true),
100-
AdjustGroupIndex(groupable, centerItemIndex, hasHeader, hasFooter, itemsCount, true),
101-
AdjustGroupIndex(groupable, lastVisibleItemIndex, hasHeader, hasFooter, itemsCount, false)
99+
AdjustGroupIndex(groupable, firstVisibleItemIndex, hasHeader, hasFooter, itemsCount, snapForward: true),
100+
AdjustGroupIndex(groupable, centerItemIndex, hasHeader, hasFooter, itemsCount, snapForward: true),
101+
AdjustGroupIndex(groupable, lastVisibleItemIndex, hasHeader, hasFooter, itemsCount, snapForward: false)
102102
);
103103
}
104104

@@ -124,17 +124,24 @@ protected virtual (int First, int Center, int Last) GetVisibleItemsIndex(Recycle
124124
return (firstVisibleItemIndex, centerItemIndex, lastVisibleItemIndex);
125125
}
126126

127-
static int AdjustGroupIndex(IGroupableItemsViewSource source, int position, bool hasHeader, bool hasFooter, int count, bool isStart)
127+
/// <param name="snapForward">
128+
/// When the adapter position falls on a group header or group footer,
129+
/// true = snap to the first data item in the following group (use for FirstVisible/Center),
130+
/// false = snap to the last data item in the preceding group (use for LastVisible).
131+
/// </param>
132+
static int AdjustGroupIndex(IGroupableItemsViewSource source, int position, bool hasHeader, bool hasFooter, int count, bool snapForward)
128133
{
129-
if (position < 0 || position >= count)
134+
if (position < 0)
130135
{
131-
return Math.Max(0, GetGroupedDataCount(source) - 1);
136+
return 0;
132137
}
133138

134-
// Adjust for header if present
135-
position = position - (hasHeader && position > 0 ? 1 : 0);
139+
if (position >= count)
140+
{
141+
return Math.Max(0, GetGroupedDataCount(source) - 1);
142+
}
136143

137-
int dataIndex = hasHeader ? 1 : 0, currentItem = hasHeader ? 1 : 0;
144+
int dataIndex = 0, currentItem = hasHeader ? 1 : 0;
138145

139146
// Iterate through items until we reach the target position
140147
while (currentItem <= position && currentItem < count)
@@ -159,7 +166,7 @@ static int AdjustGroupIndex(IGroupableItemsViewSource source, int position, bool
159166
// If position is a group header/footer, find the nearest data item
160167
else if (currentItem == position)
161168
{
162-
return isStart
169+
return snapForward
163170
? FindNextDataIndex(source, currentItem, hasFooter, count, dataIndex)
164171
: FindPrevDataIndex(source, currentItem, hasHeader);
165172
}
@@ -188,6 +195,8 @@ static int GetGroupedDataCount(IGroupableItemsViewSource source)
188195
return dataCount;
189196
}
190197

198+
// dataIndex: the 0-based data item index to assign to the next valid item found.
199+
// Returned without incrementing because the item following a header/footer inherits this index.
191200
static int FindNextDataIndex(IGroupableItemsViewSource source, int start, bool hasFooter, int count, int dataIndex)
192201
{
193202
for (int i = start + 1; i < count; i++)

src/Controls/tests/TestCases.HostApp/Issues/Issue17664.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
namespace Maui.Controls.Sample.Issues;
44

5-
[Issue(IssueTracker.Github, 17664, "Incorrect ItemsViewScrolledEventArgs in CollectionView when IsGrouped is set to true", PlatformAffected.iOS | PlatformAffected.Android)]
5+
[Issue(IssueTracker.Github, 17664, "Incorrect ItemsViewScrolledEventArgs in CollectionView when IsGrouped is set to true", PlatformAffected.Android)]
66
public class Issue17664 : ContentPage
77
{
88
CollectionView _collectionView;

src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue17664.cs

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
#if TEST_FAILS_ON_IOS && TEST_FAILS_ON_CATALYST // PR Link - https://github.com/dotnet/maui/pull/34240
1+
#if TEST_FAILS_ON_IOS && TEST_FAILS_ON_CATALYST && TEST_FAILS_ON_WINDOWS // iOS/MacCatalyst fix: https://github.com/dotnet/maui/pull/34240
2+
// Windows: The Scrolled event is not consistently triggered in the CI environment during automated
3+
// scrolling, so the label text is never updated. This is a test infrastructure limitation on Windows;
4+
// the fix itself (RecyclerViewScrollListener.cs) is Android-only and works correctly on Android.
25
using NUnit.Framework;
36
using UITest.Appium;
47
using UITest.Core;
@@ -20,10 +23,6 @@ public void VerifyGroupedCollectionViewVisibleItemIndices()
2023
App.WaitForElement("Issue17664ScrollBtn");
2124
App.Tap("Issue17664ScrollBtn");
2225

23-
#if WINDOWS
24-
Thread.Sleep(1000);
25-
#endif
26-
2726
var resultItem = App.WaitForElement("Issue17664DescriptionLabel").GetText();
2827
Assert.That(resultItem, Is.EqualTo("Category C item #2"));
2928
}

0 commit comments

Comments
 (0)