Skip to content

[Android] Fix increasing bottom gap in CollectionView while scrolling#35457

Open
praveenkumarkarunanithi wants to merge 4 commits into
dotnet:mainfrom
praveenkumarkarunanithi:fix-34634
Open

[Android] Fix increasing bottom gap in CollectionView while scrolling#35457
praveenkumarkarunanithi wants to merge 4 commits into
dotnet:mainfrom
praveenkumarkarunanithi:fix-34634

Conversation

@praveenkumarkarunanithi
Copy link
Copy Markdown
Contributor

Note

Are you waiting for the changes in this PR to be merged?
It would be very helpful if you could test the resulting artifacts from this PR and let us know in a comment if this change resolves your issue.
Thank you!

Root Cause

While scrolling long CollectionView content on Android, an extra bottom gap appears and progressively increases during scrolling. This regression was introduced by PR #33908, which removed the IMauiRecyclerView exclusion in MauiWindowInsetListener.FindListenerForView so item templates could participate in safe-area inset handling.

As a side effect, RecyclerView item views began receiving the window-inset listener by default, allowing safe-area padding to be applied on item roots. Since RecyclerView reuses item views during scrolling, recycled views could retain stale inset-derived padding from previous bindings, resulting in a progressively growing bottom gap.

Description of Change

Reintroduced the IMauiRecyclerView guard in MauiWindowInsetListener.FindListenerForView, while preserving the per-item SafeAreaEdges capability added in PR #33908 for explicitly opted-in item roots.

MauiWindowInsetListener.cs

  • FindListenerForView — when the parent is an IMauiRecyclerView, the inset listener is skipped unless the item root explicitly customizes SafeAreaEdges. This prevents recycled-item padding drift while preserving intentional safe-area scenarios.

  • HasExplicitSafeAreaEdges (new helper) — returns true only when the platform view maps to an ISafeAreaElement whose current SafeAreaEdges differs from its default value, indicating an explicit developer opt-in. Default values are cached in a static ConcurrentDictionary<Type, SafeAreaEdges> using GetOrAdd to avoid repeated computation or per-call allocations. The comparison uses IEquatable<SafeAreaEdges>.Equals(...) on a hoisted local value to avoid boxing, repeated property reads, and to remain stable for future SafeAreaEdges type evolution.

Issues Fixed

Fixes #34634

Tested the behaviour in the following platforms

  • Android
  • Windows
  • iOS
  • Mac

Regression

This regression was introduced by PR #33908.

Screenshots

Before Issue Fix After Issue Fix
BeforeFix.mov
AfterFix.mov

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 15, 2026

🚀 Dogfood this PR with:

⚠️ WARNING: Do not do this without first carefully reviewing the code of this PR to satisfy yourself it is safe.

curl -fsSL https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.sh | bash -s -- 35457

Or

  • Run remotely in PowerShell:
iex "& { $(irm https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.ps1) } 35457"

@dotnet-policy-service dotnet-policy-service Bot added the partner/syncfusion Issues / PR's with Syncfusion collaboration label May 15, 2026
@github-actions github-actions Bot added the area-controls-collectionview CollectionView, CarouselView, IndicatorView label May 15, 2026
@PureWeen PureWeen added backport/suggested The PR author or issue review has suggested that the change should be backported. i/regression This issue described a confirmed regression on a currently supported version platform/android t/bug Something isn't working labels May 15, 2026
@praveenkumarkarunanithi praveenkumarkarunanithi changed the title [WIP][Android] Fix increasing bottom gap in CollectionView while scrolling [Android] Fix increasing bottom gap in CollectionView while scrolling May 18, 2026
@NirmalKumarYuvaraj NirmalKumarYuvaraj marked this pull request as ready for review May 18, 2026 09:47
@sheiksyedm
Copy link
Copy Markdown
Contributor

/azp run maui-pr-uitests , maui-pr-devicetests

@azure-pipelines
Copy link
Copy Markdown

Azure Pipelines successfully started running 2 pipeline(s).

PureWeen added a commit that referenced this pull request May 18, 2026
Root cause: SKILL.md line 23 (now removed) explicitly listed
s/needs-repro, s/needs-info, s/needs-attention, and the p/* priority
labels as 'useful label families' the agent may apply. The PR-specific
caveat only excluded these on PRs, not on issues. The labeler dutifully
followed the spec and applied a noisy set of triage labels to issues
(observed on #35448: s/needs-repro, untriaged,
s/needs-verification, ⌚ Not Triaged, s/needs-info).

These labels are all managed by repo triage automation
(dotnet-policy-service[bot]) and human triagers — they are NOT content-
derivable. The labeler's job is to assign content-derived labels only.

SKILL.md changes:
- Remove triage/priority labels from the 'useful label families' list.
- Keep i/regression with a tightened scope ('only when reporter
  explicitly states regression').
- Add an explicit 'Triage / workflow labels' section enumerating the
  full off-limits list (s/needs-*, s/triaged, s/verified, s/no-repro,
  s/not-a-bug, s/duplicate, s/pr-needs-author-input, untriaged,
  ⌚ Not Triaged, p/0..p/3). Rule applies to both issues AND PRs.
- Add corresponding bullet in 'What NOT to do' section.

eval.yaml changes (#35448 scenario):
- Rename: 'Cross-platform only issue - no platform labels' →
  'Issue with explicit platforms gets platform labels but no triage
  workflow labels'. Old framing was wrong — issue body's 'Affected
  platforms' field explicitly lists iOS+Android, so per SKILL.md the
  labeler MUST apply those platform labels.
- Flip platform/ios + platform/android from negative to positive
  assertions (matches SKILL.md issue-platform rule).
- Add negative assertions for s/needs-info, s/needs-repro,
  s/needs-verification, s/needs-attention, untriaged,
  ⌚ Not Triaged, p/0, p/1.

eval.yaml changes (#35457 PR scenario):
- Rename: 'PR should not get s/needs-info or s/needs-repro' →
  'PR does not get triage workflow labels' (broader scope per SKILL).
- Add positive assertion (platform/android) so a noop response can't
  vacuously pass the test.
- Add negative assertions for s/needs-verification, s/needs-attention,
  s/pr-needs-author-input, untriaged, ⌚ Not Triaged.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
PureWeen added a commit that referenced this pull request May 18, 2026
Per user directive: the agentic-labeler must apply ONLY area-* and
platform/* labels. Everything else (t/*, i/*, s/*, p/*, partner/*,
perf/*, backport/*, regressed-in-*, version/*, untriaged,
:watch: Not Triaged) is forbidden.

SKILL.md changes:
- Add prominent '🚨 Scope' section at top making the restriction the
  first rule the labeler reads, with explicit enumeration of forbidden
  label families.
- Simplify 'Label discovery' section (no longer enumerates extra label
  families beyond area-*/platform/*).
- Tighten 'What NOT to do' with a single rule that prohibits all non-
  area-*/platform/* labels.
- Update noop guidance: if the only candidates fall outside area-*/
  platform/*, noop instead of applying them.

eval.yaml changes:
- Add negative assertions for t/bug, i/regression, partner/syncfusion,
  and perf/memory-leak in the issue #35448, PR #35457, and prompt-
  injection scenarios so the eval catches over-application of forbidden
  label families.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@kubaflo
Copy link
Copy Markdown
Contributor

kubaflo commented May 19, 2026

/review -b feature/regression-check -p android

@MauiBot MauiBot added s/agent-review-incomplete s/agent-reviewed PR was reviewed by AI agent workflow (full 4-phase review) labels May 19, 2026
@kubaflo
Copy link
Copy Markdown
Contributor

kubaflo commented May 19, 2026

/review -b feature/regression-check -p android

@kubaflo
Copy link
Copy Markdown
Contributor

kubaflo commented May 23, 2026

/review -b feature/refactor-copilot-yml

Copy link
Copy Markdown
Collaborator

@MauiBot MauiBot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Expert Review — 1 findings

See inline comments for details.

Comment thread src/Core/src/Platform/Android/MauiWindowInsetListener.cs Outdated
@kubaflo
Copy link
Copy Markdown
Contributor

kubaflo commented May 24, 2026

/review -b feature/refactor-copilot-yml

Copy link
Copy Markdown
Collaborator

@MauiBot MauiBot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Expert Review — 1 findings

See inline comments for details.

Comment thread src/Core/src/Platform/Android/MauiWindowInsetListener.cs Outdated
Copy link
Copy Markdown
Contributor

@kubaflo kubaflo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you please check the ai's suggestions?

@kubaflo
Copy link
Copy Markdown
Contributor

kubaflo commented May 26, 2026

/review -b feature/refactor-copilot-yml

Copy link
Copy Markdown
Collaborator

@MauiBot MauiBot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Expert Review — 2 findings

See inline comments for details.


// IMauiRecyclerView (Core) — true while the EmptyView adapter is active so that
// SafeArea inset logic can allow listeners on EmptyView items (#34634 gate exception).
bool IMauiRecyclerView.IsShowingEmptyView => _emptyViewAdapter != null && GetAdapter() == _emptyViewAdapter;
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[critical] Compile correctness — This explicit implementation targets Microsoft.Maui.IMauiRecyclerView.IsShowingEmptyView, but the Core IMauiRecyclerView interface is still empty. This will not compile (IMauiRecyclerView does not contain a definition for IsShowingEmptyView), and the read in MauiWindowInsetListener has the same problem. Add the member to the Core interface or avoid reading it through that interface.

if (MauiWindowInsetListener.FindListenerForView(view) is MauiWindowInsetListener localListener)
// applyRecyclerViewGate=true: skip RecyclerView data-item views with default SafeAreaEdges
// to prevent recycled views from accumulating stale inset-derived padding (#34634/#34635).
if (MauiWindowInsetListener.FindListenerForView(view, applyRecyclerViewGate: true) is MauiWindowInsetListener localListener)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[major] SafeArea/Android listener lifecycle — Because default RecyclerView item roots are skipped only during TrySetMauiWindowInsetListener, a later change from default SafeAreaEdges to an explicit value is ignored. MapSafeAreaEdges can find/reset the parent listener, but it does not attach this listener and LayoutViewGroup/ContentViewGroup only request insets when _isInsetListenerSet is already true. Concrete scenario: a recycled item root attaches with default edges, then a binding/style sets SafeAreaEdges explicitly; the item still has no listener until detach/reattach.

@kubaflo
Copy link
Copy Markdown
Contributor

kubaflo commented May 27, 2026

/review -b feature/refactor-copilot-yml

Copy link
Copy Markdown
Collaborator

@MauiBot MauiBot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 Automated review — alternative fix proposed

The expert-reviewer evaluation compared the PR fix against #3 automatically generated candidates and selected try-fix-3 as the strongest fix.

Why: try-fix-3 won because it is the only candidate with passing Android regression evidence, is scoped to CollectionView item reuse where stale safe-area padding occurs, and avoids the raw PR's compile and listener lifecycle issues.

Please consider applying the candidate diff below (or use it as guidance). Once you push an update, this workflow will re-trigger and re-evaluate.

Candidate diff (`try-fix-3`)
diff --git a/src/Controls/src/Core/Handlers/Items/Android/Adapters/ItemsViewAdapter.cs b/src/Controls/src/Core/Handlers/Items/Android/Adapters/ItemsViewAdapter.cs
index 43408e3339..694288cb5e 100644
--- a/src/Controls/src/Core/Handlers/Items/Android/Adapters/ItemsViewAdapter.cs
+++ b/src/Controls/src/Core/Handlers/Items/Android/Adapters/ItemsViewAdapter.cs
@@ -68,6 +68,7 @@ namespace Microsoft.Maui.Controls.Handlers.Items
 					textViewHolder.TextView.Text = ItemsSource.GetItem(position).ToString();
 					break;
 				case TemplatedItemViewHolder templatedItemViewHolder:
+					templatedItemViewHolder.ResetSafeAreaForReuse();
 					BindTemplatedItemViewHolder(templatedItemViewHolder, ItemsSource.GetItem(position));
 					break;
 			}
diff --git a/src/Controls/src/Core/Handlers/Items/Android/ItemContentView.cs b/src/Controls/src/Core/Handlers/Items/Android/ItemContentView.cs
index c07bbfa766..c4dd2dc029 100644
--- a/src/Controls/src/Core/Handlers/Items/Android/ItemContentView.cs
+++ b/src/Controls/src/Core/Handlers/Items/Android/ItemContentView.cs
@@ -2,7 +2,9 @@
 using System;
 using Android.Content;
 using Android.Views;
+using AndroidX.Core.View;
 using Microsoft.Maui.Graphics;
+using Microsoft.Maui.Platform;
 using AView = Android.Views.View;
 
 namespace Microsoft.Maui.Controls.Handlers.Items
@@ -77,6 +79,32 @@ namespace Microsoft.Maui.Controls.Handlers.Items
 			_previousPixelHeight = -1;
 		}
 
+		internal void ResetSafeAreaForReuse()
+		{
+			var platformView = PlatformView;
+
+			if (platformView is null)
+			{
+				return;
+			}
+
+			var listener = MauiWindowInsetListener.FindListenerForView(platformView);
+			if (listener is null)
+			{
+				return;
+			}
+
+			var hasExplicitSafeAreaEdges = View is ISafeAreaElement safeAreaElement &&
+				!safeAreaElement.SafeAreaEdges.Equals(safeAreaElement.SafeAreaEdgesDefaultValueCreator());
+			if (!listener.IsViewTracked(platformView) && !hasExplicitSafeAreaEdges)
+			{
+				return;
+			}
+
+			listener.ResetAppliedSafeAreas(platformView);
+			ViewCompat.RequestApplyInsets(platformView);
+		}
+
 		internal void HandleItemSizingStrategy(Action<Size> reportMeasure, Size? size)
 		{
 			_reportMeasure = new WeakReference(reportMeasure);
diff --git a/src/Controls/src/Core/Handlers/Items/Android/TemplatedItemViewHolder.cs b/src/Controls/src/Core/Handlers/Items/Android/TemplatedItemViewHolder.cs
index 184d53455b..9980f676a5 100644
--- a/src/Controls/src/Core/Handlers/Items/Android/TemplatedItemViewHolder.cs
+++ b/src/Controls/src/Core/Handlers/Items/Android/TemplatedItemViewHolder.cs
@@ -52,6 +52,11 @@ namespace Microsoft.Maui.Controls.Handlers.Items
 			_selectedTemplate = null; // force templateChanging=true on next Bind() to recreate the view
 		}
 
+		internal void ResetSafeAreaForReuse()
+		{
+			_itemContentView.ResetSafeAreaForReuse();
+		}
+
 		public void Bind(object itemBindingContext, ItemsView itemsView,
 			Action<Size> reportMeasure = null, Size? size = null)
 		{

@MauiBot MauiBot added the s/agent-fix-win AI found a better alternative fix than the PR label May 27, 2026
@MauiBot
Copy link
Copy Markdown
Collaborator

MauiBot commented May 27, 2026

🤖 AI Summary

👋 @praveenkumarkarunanithi — new AI review results are available. Please review the latest session below.

📊 Review Session67db65d · fix update · 2026-05-27 11:57 UTC
🚦 Gate — Test Before & After Fix

Gate Result: ⚠️ SKIPPED

No tests were detected in this PR.

Recommendation: Add tests to verify the fix using the write-tests-agent.


🧪 UI Tests — CollectionView,Editor,RefreshView,SafeAreaEdges,ScrollView,ViewBaseTests

Detected UI test categories: CollectionView,Editor,RefreshView,SafeAreaEdges,ScrollView,ViewBaseTests

🧪 UI Test Execution Results

FAILED — 0 passed, 2 failed, 0 skipped (platform: android)

Category Result Tests Duration Notes
CollectionView ❌ FAILED 0/1 — build/deploy failed before per-test results 391.1s exit code 1
ViewBaseTests ❌ FAILED 0/1 — build/deploy failed before per-test results 204.9s exit code 1

Failed test details

CollectionView — build/deploy failed (no per-test results)

Last 30 lines of build/test stdout:

##vso[build.updatebuildnumber]10.0.80-ci+azdo.14205618
  Essentials -> /home/vsts/work/1/s/artifacts/bin/Essentials/Debug/net10.0-android36.0/Microsoft.Maui.Essentials.dll
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14205618
/home/vsts/work/1/s/src/Core/src/Platform/Android/MauiWindowInsetListener.cs(127,24): error CS1061: 'IMauiRecyclerView' does not contain a definition for 'IsShowingEmptyView' and no accessible extension method 'IsShowingEmptyView' accepting a first argument of type 'IMauiRecyclerView' could be found (are you missing a using directive or an assembly reference?) [/home/vsts/work/1/s/src/Core/src/Core.csproj::TargetFramework=net10.0-android36.0]

Build FAILED.

/home/vsts/work/1/s/src/Core/src/Platform/Android/MauiWindowInsetListener.cs(127,24): error CS1061: 'IMauiRecyclerView' does not contain a definition for 'IsShowingEmptyView' and no accessible extension method 'IsShowingEmptyView' accepting a first argument of type 'IMauiRecyclerView' could be found (are you missing a using directive or an assembly reference?) [/home/vsts/work/1/s/src/Core/src/Core.csproj::TargetFramework=net10.0-android36.0]
    0 Warning(s)
    1 Error(s)

Time Elapsed 00:00:29.86
* daemon not running; starting now at tcp:5037
* daemon started successfully
  Determining projects to restore...
  All projects are up-to-date for restore.
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14205618
  Graphics -> /home/vsts/work/1/s/artifacts/bin/Graphics/Debug/net10.0-android36.0/Microsoft.Maui.Graphics.dll
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14205618
  Essentials -> /home/vsts/work/1/s/artifacts/bin/Essentials/Debug/net10.0-android36.0/Microsoft.Maui.Essentials.dll
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14205618
/home/vsts/work/1/s/src/Core/src/Platform/Android/MauiWindowInsetListener.cs(127,24): error CS1061: 'IMauiRecyclerView' does not contain a definition for 'IsShowingEmptyView' and no accessible extension method 'IsShowingEmptyView' accepting a first argument of type 'IMauiRecyclerView' could be found (are you missing a using directive or an assembly reference?) [/home/vsts/work/1/s/src/Core/src/Core.csproj::TargetFramework=net10.0-android36.0]

Build FAILED.

/home/vsts/work/1/s/src/Core/src/Platform/Android/MauiWindowInsetListener.cs(127,24): error CS1061: 'IMauiRecyclerView' does not contain a definition for 'IsShowingEmptyView' and no accessible extension method 'IsShowingEmptyView' accepting a first argument of type 'IMauiRecyclerView' could be found (are you missing a using directive or an assembly reference?) [/home/vsts/work/1/s/src/Core/src/Core.csproj::TargetFramework=net10.0-android36.0]
    0 Warning(s)
    1 Error(s)

Time Elapsed 00:00:29.90
ViewBaseTests — build/deploy failed (no per-test results)

Last 30 lines of build/test stdout:

##vso[build.updatebuildnumber]10.0.80-ci+azdo.14205618
  Essentials -> /home/vsts/work/1/s/artifacts/bin/Essentials/Debug/net10.0-android36.0/Microsoft.Maui.Essentials.dll
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14205618
/home/vsts/work/1/s/src/Core/src/Platform/Android/MauiWindowInsetListener.cs(127,24): error CS1061: 'IMauiRecyclerView' does not contain a definition for 'IsShowingEmptyView' and no accessible extension method 'IsShowingEmptyView' accepting a first argument of type 'IMauiRecyclerView' could be found (are you missing a using directive or an assembly reference?) [/home/vsts/work/1/s/src/Core/src/Core.csproj::TargetFramework=net10.0-android36.0]

Build FAILED.

/home/vsts/work/1/s/src/Core/src/Platform/Android/MauiWindowInsetListener.cs(127,24): error CS1061: 'IMauiRecyclerView' does not contain a definition for 'IsShowingEmptyView' and no accessible extension method 'IsShowingEmptyView' accepting a first argument of type 'IMauiRecyclerView' could be found (are you missing a using directive or an assembly reference?) [/home/vsts/work/1/s/src/Core/src/Core.csproj::TargetFramework=net10.0-android36.0]
    0 Warning(s)
    1 Error(s)

Time Elapsed 00:00:31.07
* daemon not running; starting now at tcp:5037
* daemon started successfully
  Determining projects to restore...
  All projects are up-to-date for restore.
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14205618
  Graphics -> /home/vsts/work/1/s/artifacts/bin/Graphics/Debug/net10.0-android36.0/Microsoft.Maui.Graphics.dll
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14205618
  Essentials -> /home/vsts/work/1/s/artifacts/bin/Essentials/Debug/net10.0-android36.0/Microsoft.Maui.Essentials.dll
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14205618
/home/vsts/work/1/s/src/Core/src/Platform/Android/MauiWindowInsetListener.cs(127,24): error CS1061: 'IMauiRecyclerView' does not contain a definition for 'IsShowingEmptyView' and no accessible extension method 'IsShowingEmptyView' accepting a first argument of type 'IMauiRecyclerView' could be found (are you missing a using directive or an assembly reference?) [/home/vsts/work/1/s/src/Core/src/Core.csproj::TargetFramework=net10.0-android36.0]

Build FAILED.

/home/vsts/work/1/s/src/Core/src/Platform/Android/MauiWindowInsetListener.cs(127,24): error CS1061: 'IMauiRecyclerView' does not contain a definition for 'IsShowingEmptyView' and no accessible extension method 'IsShowingEmptyView' accepting a first argument of type 'IMauiRecyclerView' could be found (are you missing a using directive or an assembly reference?) [/home/vsts/work/1/s/src/Core/src/Core.csproj::TargetFramework=net10.0-android36.0]
    0 Warning(s)
    1 Error(s)

Time Elapsed 00:00:30.33

Failures here are informational only — they do not block the gate or affect try-fix candidate scoring.


🔍 Regression Cross-Reference

🔍 Regression Cross-Reference

🟢 No regression risks detected. No labeled bug-fix PRs in the last 6 months touched the modified files.


🔍 Pre-Flight — Context & Validation

Issue: #34634 - [.NET 10] Increasing gap in the bottom while scrolling.
PR: #35457 - [Android] Fix increasing bottom gap in CollectionView while scrolling
Platforms Affected: Android
Files Changed: 2 implementation, 0 test

Key Findings

  • The PR targets an Android CollectionView recycling regression where RecyclerView item roots can retain inset-derived padding while scrolling.
  • The implementation gates SafeArea listener attachment for default RecyclerView item roots while preserving explicit SafeAreaEdges opt-in and EmptyView handling.
  • Existing review feedback reports two unresolved issues: IMauiRecyclerView.IsShowingEmptyView is read/implemented but the Core interface is empty, and SafeAreaEdges changes after attachment can still miss listener attach/detach handling.
  • Gate was already skipped because no tests were detected in this PR; no gate verification was re-run.

Code Review Summary

Verdict: NEEDS_CHANGES
Confidence: high
Errors: 2 | Warnings: 0 | Suggestions: 0

Key code review findings:

  • src/Controls/src/Core/Handlers/Items/Android/MauiRecyclerView.cs:43 explicitly implements IMauiRecyclerView.IsShowingEmptyView, but src/Core/src/Core/IMauiRecyclerView.cs does not define that member; MauiWindowInsetListener also reads it.
  • src/Core/src/Platform/Android/MauiWindowInsetListener.cs:528 applies the RecyclerView/default SafeAreaEdges gate only during initial listener attachment, so a recycled item root that changes from default to explicit SafeAreaEdges after attach can remain without a listener.

Fix Candidates

# Source Approach Test Result Files Changed Notes
PR PR #35457 Gate initial listener attachment for RecyclerView item roots unless SafeAreaEdges is explicit; exempt EmptyView items. ⚠️ SKIPPED (Gate: no tests detected) src/Core/src/Platform/Android/MauiWindowInsetListener.cs, src/Controls/src/Core/Handlers/Items/Android/MauiRecyclerView.cs Original PR has unresolved compile/lifecycle review findings.

🔬 Code Review — Deep Analysis

Code Review — PR #35457

Independent Assessment

What this changes: Android inset listener attachment is gated for RecyclerView item roots unless SafeAreaEdges is explicit; EmptyView is exempted.
Inferred motivation: Prevent recycled CollectionView item views from retaining stale safe-area padding while preserving explicit per-item safe-area opt-in.

Reconciliation with PR Narrative

Author claims: Fixes increasing Android CollectionView bottom gap introduced by #33908.
Agreement/disagreement: Root cause matches the code. However, current implementation still has correctness issues.

Findings

❌ Error — Core interface does not define IsShowingEmptyView

src/Controls/src/Core/Handlers/Items/Android/MauiRecyclerView.cs:43 explicitly implements IMauiRecyclerView.IsShowingEmptyView, but src/Core/src/Core/IMauiRecyclerView.cs is empty. MauiWindowInsetListener also reads this member. This will not compile.

❌ Error — SafeAreaEdges transitions after attach are not handled

src/Core/src/Platform/Android/MauiWindowInsetListener.cs:528 applies the RecyclerView gate only during initial listener attachment. If an item root attaches with default SafeAreaEdges, then later changes to explicit SafeAreaEdges, no listener is installed. The reverse transition can leave an existing listener active.

Devil's Advocate

The reset paths now call FindListenerForView without the gate, which fixes part of the earlier stale-padding concern. But that does not solve listener attach/detach when SafeAreaEdges crosses explicit/default after the view is already attached.

Verdict: NEEDS_CHANGES

Confidence: high
Summary: CI is failing, and the code has a concrete compile issue plus a lifecycle correctness gap. Findings were saved to CustomAgentLogsTmp/PRState/35457/PRAgent/inline-findings.json; no GitHub comments were posted.


🔧 Fix — Analysis & Comparison

Fix Candidates

# Source Approach Test Result Files Changed Notes
1 try-fix Lifecycle-transition guard in LayoutViewGroup / ContentViewGroup plus dynamic attach/remove from MapSafeAreaEdges. ❌ FAIL 4 candidate files Build succeeded, but Android device-test invocation failed before tests ran because the filter used an unescaped `
2 try-fix Application-time gate in HandleWindowInsets: keep listeners attached, skip/reset default RecyclerView item roots during inset application. ❌ FAIL 5 candidate files Build succeeded and approach looked promising, but the test command exited 1 because the filter ran the full Core device suite and hit unrelated SwitchHandlerTests.ThumbColorUpdatesCorrectly; cannot satisfy all-tests-passed stop condition.
3 try-fix CollectionView recycling-layer reset: clear/reapply safe-area state when templated Android item views are rebound. ✅ PASS 3 files Controls.DeviceTests Android run completed successfully: 955 run, 942 passed, 0 failed; XHarness exit code 0.
PR PR #35457 Gate initial listener attachment for RecyclerView item roots unless SafeAreaEdges is explicit; exempt EmptyView items. ⚠️ SKIPPED (Gate) 2 files Original PR has unresolved compile/lifecycle findings from code review and no detected tests in gate.

Cross-Pollination

Model Round New Ideas? Details
claude-opus-4.6 1 Yes Proposed lifecycle-transition handling rather than PR's listener-lookup gate. Failed due malformed Android test filter, not validated.
claude-opus-4.7 1 Yes Proposed application-time gating rather than attachment-time gating. Failed due unrelated test failure in the executed Core suite.
gpt-5.3-codex 1 Yes Proposed CollectionView recycling-layer reset, avoiding listener lookup, lifecycle transition, and application-time gate strategies. Passed targeted Android Controls device-test validation.

Exhausted: No
Selected Fix: Candidate #3 — it passed the Android regression command and is narrower than the PR's Core listener-attachment gate. It confines the alternative fix to Android CollectionView recycling paths, avoids the PR's missing IMauiRecyclerView.IsShowingEmptyView interface compile issue, and avoids changing broad MauiWindowInsetListener behavior for non-CollectionView views.


📋 Report — Final Recommendation

Comparative Candidate Report - PR #35457

Ranking

Rank Candidate Regression result Assessment
1 try-fix-3 PASS Narrow Android CollectionView recycling-layer reset. It passed the targeted Android Controls device-test run and avoids changing global/Core inset listener attachment semantics.
2 pr-plus-reviewer Not run Fixes the PR's compile error and addresses the expert review lifecycle concerns, but remains an unvalidated conceptual PR-derived candidate and still carries broader Core inset-listener risk.
3 try-fix-2 FAIL Architecturally plausible application-time gate with clean self-review, but the executed regression command exited with tests failed, so it must rank below passing candidates.
4 try-fix-1 FAIL Handles attach and mapper transitions, but its Android test invocation failed before validating the candidate.
5 pr SKIPPED / NEEDS_CHANGES Raw PR has no detected tests, a concrete compile issue, and unresolved listener lifecycle/explicitness issues from expert review.

Candidate comparison

pr targets the right root cause: recycled Android CollectionView item roots can retain safe-area padding after PR #33908 allowed per-item inset handling. However, it gates listener lookup at initial attachment and adds an explicit implementation for an interface member that does not exist, so it is not acceptable as submitted.

pr-plus-reviewer improves the PR by adding the missing interface member and requiring listener re-evaluation across RecyclerView rebind / SafeAreaEdges transitions. That would address the expert review's critical and major findings, but it was not validated and still requires a careful definition of "explicit SafeAreaEdges" because value comparison cannot distinguish an explicit default-valued setting.

try-fix-1 and try-fix-2 explored useful alternatives, but both have failed regression status. Per the ranking rule, failed candidates are lower than candidates that passed. try-fix-2 is less concerning than try-fix-1 because its only failed test was reported as unrelated, but its command still exited failed.

try-fix-3 is the strongest candidate. It resets applied safe-area state at the CollectionView item reuse layer, requests inset reapplication when the current item needs it, and leaves EmptyView/global inset listener behavior unchanged. It is also the only candidate with a successful Android device-test result.

Winner

Winner: try-fix-3

Rationale: it is the only candidate with passing Android regression evidence, it is scoped directly to RecyclerView item reuse where the stale-padding bug occurs, and it avoids the raw PR's compile/lifecycle issues.


Copy link
Copy Markdown
Contributor

@kubaflo kubaflo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you check the ai's suggestions?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area-controls-collectionview CollectionView, CarouselView, IndicatorView backport/suggested The PR author or issue review has suggested that the change should be backported. i/regression This issue described a confirmed regression on a currently supported version partner/syncfusion Issues / PR's with Syncfusion collaboration platform/android s/agent-fix-win AI found a better alternative fix than the PR s/agent-reviewed PR was reviewed by AI agent workflow (full 4-phase review) t/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[.NET 10] Increasing gap in the bottom while scrolling.

6 participants