diff --git a/src/Controls/src/Core/Handlers/Items/Android/GridLayoutSpanSizeLookup.cs b/src/Controls/src/Core/Handlers/Items/Android/GridLayoutSpanSizeLookup.cs index 90ed595e41f4..dd769fe734bc 100644 --- a/src/Controls/src/Core/Handlers/Items/Android/GridLayoutSpanSizeLookup.cs +++ b/src/Controls/src/Core/Handlers/Items/Android/GridLayoutSpanSizeLookup.cs @@ -12,6 +12,9 @@ public GridLayoutSpanSizeLookup(GridItemsLayout gridItemsLayout, RecyclerView re { _gridItemsLayout = gridItemsLayout; _recyclerView = recyclerView; + + SpanIndexCacheEnabled = true; + SpanGroupIndexCacheEnabled = true; } public override int GetSpanSize(int position) diff --git a/src/Controls/src/Core/Handlers/Items/Android/SpacingItemDecoration.cs b/src/Controls/src/Core/Handlers/Items/Android/SpacingItemDecoration.cs index ca2525e79658..7abaeb2345a9 100644 --- a/src/Controls/src/Core/Handlers/Items/Android/SpacingItemDecoration.cs +++ b/src/Controls/src/Core/Handlers/Items/Android/SpacingItemDecoration.cs @@ -13,8 +13,6 @@ public class SpacingItemDecoration : RecyclerView.ItemDecoration public int VerticalOffset { get; } - int _span = 1; - ItemsLayoutOrientation _orientation; public SpacingItemDecoration(Context context, IItemsLayout itemsLayout) @@ -39,7 +37,6 @@ public SpacingItemDecoration(Context context, IItemsLayout itemsLayout) case GridItemsLayout gridItemsLayout: horizontalOffset = gridItemsLayout.HorizontalItemSpacing / 2.0; verticalOffset = gridItemsLayout.VerticalItemSpacing / 2.0; - _span = gridItemsLayout.Span; _orientation = gridItemsLayout.Orientation; break; case LinearItemsLayout listItemsLayout: @@ -84,10 +81,24 @@ public override void GetItemOffsets(ARect outRect, AView view, RecyclerView pare outRect.Top = VerticalOffset; // Remove spacing on the outer edges so spacing only appears between items. - // A linear layout is effectively span=1, so the same math works for both. - int rowCol = _span <= 1 ? position : position / _span; - int totalRowsCols = _span <= 1 ? itemCount : (itemCount + _span - 1) / _span; - int lastRowCol = totalRowsCols - 1; + int rowCol; + int lastRowCol; + + if (parent.GetLayoutManager() is GridLayoutManager gridLayoutManager) + { + // Use SpanSizeLookup instead of position/spanCount so full-span items + // (group headers, footers, etc.) are accounted for when determining rows. + var spanSizeLookup = gridLayoutManager.GetSpanSizeLookup(); + int spanCount = gridLayoutManager.SpanCount; + rowCol = spanSizeLookup.GetSpanGroupIndex(position, spanCount); + lastRowCol = spanSizeLookup.GetSpanGroupIndex(itemCount - 1, spanCount); + } + else + { + // Linear layout: each item occupies exactly one row/column. + rowCol = position; + lastRowCol = itemCount - 1; + } if (_orientation == ItemsLayoutOrientation.Vertical) { diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/GroupedCollectionViewGridLayoutRendersCorrectly.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/GroupedCollectionViewGridLayoutRendersCorrectly.png new file mode 100644 index 000000000000..06a76bc22132 Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/GroupedCollectionViewGridLayoutRendersCorrectly.png differ diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue35700.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue35700.cs new file mode 100644 index 000000000000..f6d49cbef26d --- /dev/null +++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue35700.cs @@ -0,0 +1,91 @@ +using System.Collections.ObjectModel; +using Microsoft.Maui.Controls.Shapes; + +namespace Maui.Controls.Sample.Issues; + +[Issue(IssueTracker.Github, 35700, + "Grouped CollectionView items not rendered properly on Android with GridItemsLayout", + PlatformAffected.Android)] +public class Issue35700 : TestContentPage +{ + protected override void Init() + { + var collectionView = new CollectionView2 + { + AutomationId = "TestCollectionView", + IsGrouped = true, + HorizontalOptions = LayoutOptions.Fill, + Margin = new Thickness(5, 30, 5, 5), + ItemsLayout = new GridItemsLayout(ItemsLayoutOrientation.Vertical) + { + Span = 5, + VerticalItemSpacing = 30, + HorizontalItemSpacing = 10, + }, + GroupHeaderTemplate = new DataTemplate(() => + { + var label = new Label + { + HorizontalOptions = LayoutOptions.Fill, + HorizontalTextAlignment = TextAlignment.Start, + Padding = new Thickness(10), + FontSize = 18, + TextColor = Colors.White, + FontAttributes = FontAttributes.Bold, + BackgroundColor = Colors.Gray, + }; + label.SetBinding(Label.TextProperty, "Name"); + return label; + }), + ItemTemplate = new DataTemplate(() => + { + var label = new Label + { + HorizontalOptions = LayoutOptions.Center, + TextColor = Colors.White, + VerticalOptions = LayoutOptions.Center, + HorizontalTextAlignment = TextAlignment.Center, + }; + label.SetBinding(Label.TextProperty, "."); + + return new Border + { + StrokeShape = new RoundRectangle { CornerRadius = 10 }, + Padding = new Thickness(5), + MinimumWidthRequest = 50, + Stroke = Colors.Transparent, + BackgroundColor = Colors.Gray, + StrokeThickness = 1, + HorizontalOptions = LayoutOptions.Center, + Content = label, + }; + }), + }; + + collectionView.ItemsSource = new ObservableCollection + { + new NumberGroup35700("100s", new List + { + "100", "200", "300", "400", "500", + "600", "700", "800", "900", + }), + new NumberGroup35700("1000s", new List + { + "1000", "2000", "3000", "4000", "5000", + "6000", "7000", "8000", "9000", + }), + }; + + Content = collectionView; + } +} + +public class NumberGroup35700 : ObservableCollection +{ + public string Name { get; private set; } + + public NumberGroup35700(string name, List numbers) : base(numbers) + { + Name = name; + } +} diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue35700.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue35700.cs new file mode 100644 index 000000000000..525f10fbfbb9 --- /dev/null +++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue35700.cs @@ -0,0 +1,20 @@ +using NUnit.Framework; +using UITest.Appium; +using UITest.Core; + +namespace Microsoft.Maui.TestCases.Tests.Issues; + +public class Issue35700 : _IssuesUITest +{ + public Issue35700(TestDevice device) : base(device) { } + + public override string Issue => "Grouped CollectionView items not rendered properly on Android with GridItemsLayout"; + + [Test] + [Category(UITestCategories.CollectionView)] + public void GroupedCollectionViewGridLayoutRendersCorrectly() + { + App.WaitForElement("TestCollectionView"); + VerifyScreenshot(); + } +}