[Android] Fix for CollectionView.EmptyView does not remeasure its height when the parent layout changes dynamically, causing incorrect sizing.#33559
Conversation
|
/azp run |
|
Azure Pipelines successfully started running 3 pipeline(s). |
There was a problem hiding this comment.
Pull request overview
Fixes an Android CollectionView EmptyView sizing issue where the empty view can retain stale measured dimensions after the parent layout size changes.
Changes:
- Android handler: detect RecyclerView size changes and trigger an EmptyView layout pass when the size updates.
- Add a new issue page (
Issue33324) to reproduce the scenario via dynamic layout changes. - Add a UI screenshot test and new Android/iOS snapshot baselines for
EmptyViewShouldRemeasureWhenParentLayoutChanges.
Reviewed changes
Copilot reviewed 3 out of 5 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| src/Controls/src/Core/Handlers/Items/ItemsViewHandler.Android.cs | Forces the EmptyView ViewHolder to re-layout when RecyclerView size changes. |
| src/Controls/tests/TestCases.HostApp/Issues/Issue33324.cs | Adds a repro page that changes layout dynamically to validate EmptyView remeasurement behavior. |
| src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue33324.cs | Adds a screenshot-based UI test for the repro scenario. |
| src/Controls/tests/TestCases.Android.Tests/snapshots/android/EmptyViewShouldRemeasureWhenParentLayoutChanges.png | Adds Android baseline screenshot for the new test. |
| src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/EmptyViewShouldRemeasureWhenParentLayoutChanges.png | Adds iOS baseline screenshot for the new test. |
| { | ||
| App.WaitForElement("LoadItemsButton"); | ||
| App.Tap("LoadItemsButton"); | ||
| App.WaitForElement("SecondCollectionView"); |
There was a problem hiding this comment.
After tapping "LoadItemsButton", this waits for "SecondCollectionView", which is already present before the tap. That means the test can take the screenshot before the layout change occurs (or before items become visible), making the test ineffective/flaky. Consider waiting for a UI change caused by the command (e.g., the now-visible "FirstCollectionView" inside the previously-hidden container, or a specific item) before calling VerifyScreenshot().
| App.WaitForElement("SecondCollectionView"); | |
| App.WaitForElement("FirstCollectionView"); |
| double tolerance = 0.001; | ||
| var widthChanged = Math.Abs(emptyViewAdapter.RecyclerViewWidth - width) > tolerance; | ||
| var heightChanged = Math.Abs(emptyViewAdapter.RecyclerViewHeight - height) > tolerance; |
There was a problem hiding this comment.
The size-change tolerance is a magic number. Consider extracting it to a named const/static readonly (and adding a brief comment explaining the unit/why that tolerance was chosen) so future changes are easier and intent is clearer.
|
/rebase |
…View dimensions change in ItemsViewHandler for Android
612e1d6 to
34a69d5
Compare
8498a4e to
9ac6edf
Compare
…ght when the parent layout changes dynamically, causing incorrect sizing. (#33559) <!-- Please let the below note in for people that find this PR --> > [!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](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from this PR and let us know in a comment if this change resolves your issue. Thank you! <!-- !!!!!!! MAIN IS THE ONLY ACTIVE BRANCH. MAKE SURE THIS PR IS TARGETING MAIN. !!!!!!! --> ### Root Cause: The issue occurs because of Android RecyclerView reusing ViewHolders with previously measured dimensions and not automatically remeasuring them when only the layout size changes, causing the EmptyView to retain its old dimensions instead of adapting to the new available space. ### Fix Description: The fix involves explicitly forcing the EmptyView to remeasure when the RecyclerView dimensions change by detecting width or height updates, locating the corresponding EmptyView ViewHolder, and requesting a layout pass so it is resized to match the new available space. When the ViewHolder isn't immediately available, the layout request is deferred to the next UI loop iteration. ### Issues Fixed Fixes #33324 ### Tested the behaviour in the following platforms - [x] Android - [x] Windows - [x] iOS - [x] Mac ### Output Screenshot Before Issue Fix | After Issue Fix | |----------|----------| |<video width="100" height="100" alt="Before Fix" src="https://github.com/user-attachments/assets/fcdb9637-56b8-4dd3-8a23-1dc4e881bb36">|<video width="100" height="100" alt="After Fix" src="https://github.com/user-attachments/assets/3ff0bee9-8c80-4d3f-8042-acff1807ece6">|
…ght when the parent layout changes dynamically, causing incorrect sizing. (#33559) <!-- Please let the below note in for people that find this PR --> > [!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](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from this PR and let us know in a comment if this change resolves your issue. Thank you! <!-- !!!!!!! MAIN IS THE ONLY ACTIVE BRANCH. MAKE SURE THIS PR IS TARGETING MAIN. !!!!!!! --> ### Root Cause: The issue occurs because of Android RecyclerView reusing ViewHolders with previously measured dimensions and not automatically remeasuring them when only the layout size changes, causing the EmptyView to retain its old dimensions instead of adapting to the new available space. ### Fix Description: The fix involves explicitly forcing the EmptyView to remeasure when the RecyclerView dimensions change by detecting width or height updates, locating the corresponding EmptyView ViewHolder, and requesting a layout pass so it is resized to match the new available space. When the ViewHolder isn't immediately available, the layout request is deferred to the next UI loop iteration. ### Issues Fixed Fixes #33324 ### Tested the behaviour in the following platforms - [x] Android - [x] Windows - [x] iOS - [x] Mac ### Output Screenshot Before Issue Fix | After Issue Fix | |----------|----------| |<video width="100" height="100" alt="Before Fix" src="https://github.com/user-attachments/assets/fcdb9637-56b8-4dd3-8a23-1dc4e881bb36">|<video width="100" height="100" alt="After Fix" src="https://github.com/user-attachments/assets/3ff0bee9-8c80-4d3f-8042-acff1807ece6">|
…ght when the parent layout changes dynamically, causing incorrect sizing. (#33559) <!-- Please let the below note in for people that find this PR --> > [!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](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from this PR and let us know in a comment if this change resolves your issue. Thank you! <!-- !!!!!!! MAIN IS THE ONLY ACTIVE BRANCH. MAKE SURE THIS PR IS TARGETING MAIN. !!!!!!! --> ### Root Cause: The issue occurs because of Android RecyclerView reusing ViewHolders with previously measured dimensions and not automatically remeasuring them when only the layout size changes, causing the EmptyView to retain its old dimensions instead of adapting to the new available space. ### Fix Description: The fix involves explicitly forcing the EmptyView to remeasure when the RecyclerView dimensions change by detecting width or height updates, locating the corresponding EmptyView ViewHolder, and requesting a layout pass so it is resized to match the new available space. When the ViewHolder isn't immediately available, the layout request is deferred to the next UI loop iteration. ### Issues Fixed Fixes #33324 ### Tested the behaviour in the following platforms - [x] Android - [x] Windows - [x] iOS - [x] Mac ### Output Screenshot Before Issue Fix | After Issue Fix | |----------|----------| |<video width="100" height="100" alt="Before Fix" src="https://github.com/user-attachments/assets/fcdb9637-56b8-4dd3-8a23-1dc4e881bb36">|<video width="100" height="100" alt="After Fix" src="https://github.com/user-attachments/assets/3ff0bee9-8c80-4d3f-8042-acff1807ece6">|
…ght when the parent layout changes dynamically, causing incorrect sizing. (#33559) <!-- Please let the below note in for people that find this PR --> > [!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](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from this PR and let us know in a comment if this change resolves your issue. Thank you! <!-- !!!!!!! MAIN IS THE ONLY ACTIVE BRANCH. MAKE SURE THIS PR IS TARGETING MAIN. !!!!!!! --> ### Root Cause: The issue occurs because of Android RecyclerView reusing ViewHolders with previously measured dimensions and not automatically remeasuring them when only the layout size changes, causing the EmptyView to retain its old dimensions instead of adapting to the new available space. ### Fix Description: The fix involves explicitly forcing the EmptyView to remeasure when the RecyclerView dimensions change by detecting width or height updates, locating the corresponding EmptyView ViewHolder, and requesting a layout pass so it is resized to match the new available space. When the ViewHolder isn't immediately available, the layout request is deferred to the next UI loop iteration. ### Issues Fixed Fixes #33324 ### Tested the behaviour in the following platforms - [x] Android - [x] Windows - [x] iOS - [x] Mac ### Output Screenshot Before Issue Fix | After Issue Fix | |----------|----------| |<video width="100" height="100" alt="Before Fix" src="https://github.com/user-attachments/assets/fcdb9637-56b8-4dd3-8a23-1dc4e881bb36">|<video width="100" height="100" alt="After Fix" src="https://github.com/user-attachments/assets/3ff0bee9-8c80-4d3f-8042-acff1807ece6">|
…ght when the parent layout changes dynamically, causing incorrect sizing. (#33559) <!-- Please let the below note in for people that find this PR --> > [!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](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from this PR and let us know in a comment if this change resolves your issue. Thank you! <!-- !!!!!!! MAIN IS THE ONLY ACTIVE BRANCH. MAKE SURE THIS PR IS TARGETING MAIN. !!!!!!! --> ### Root Cause: The issue occurs because of Android RecyclerView reusing ViewHolders with previously measured dimensions and not automatically remeasuring them when only the layout size changes, causing the EmptyView to retain its old dimensions instead of adapting to the new available space. ### Fix Description: The fix involves explicitly forcing the EmptyView to remeasure when the RecyclerView dimensions change by detecting width or height updates, locating the corresponding EmptyView ViewHolder, and requesting a layout pass so it is resized to match the new available space. When the ViewHolder isn't immediately available, the layout request is deferred to the next UI loop iteration. ### Issues Fixed Fixes #33324 ### Tested the behaviour in the following platforms - [x] Android - [x] Windows - [x] iOS - [x] Mac ### Output Screenshot Before Issue Fix | After Issue Fix | |----------|----------| |<video width="100" height="100" alt="Before Fix" src="https://github.com/user-attachments/assets/fcdb9637-56b8-4dd3-8a23-1dc4e881bb36">|<video width="100" height="100" alt="After Fix" src="https://github.com/user-attachments/assets/3ff0bee9-8c80-4d3f-8042-acff1807ece6">|
…ght when the parent layout changes dynamically, causing incorrect sizing. (#33559) <!-- Please let the below note in for people that find this PR --> > [!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](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from this PR and let us know in a comment if this change resolves your issue. Thank you! <!-- !!!!!!! MAIN IS THE ONLY ACTIVE BRANCH. MAKE SURE THIS PR IS TARGETING MAIN. !!!!!!! --> ### Root Cause: The issue occurs because of Android RecyclerView reusing ViewHolders with previously measured dimensions and not automatically remeasuring them when only the layout size changes, causing the EmptyView to retain its old dimensions instead of adapting to the new available space. ### Fix Description: The fix involves explicitly forcing the EmptyView to remeasure when the RecyclerView dimensions change by detecting width or height updates, locating the corresponding EmptyView ViewHolder, and requesting a layout pass so it is resized to match the new available space. When the ViewHolder isn't immediately available, the layout request is deferred to the next UI loop iteration. ### Issues Fixed Fixes #33324 ### Tested the behaviour in the following platforms - [x] Android - [x] Windows - [x] iOS - [x] Mac ### Output Screenshot Before Issue Fix | After Issue Fix | |----------|----------| |<video width="100" height="100" alt="Before Fix" src="https://github.com/user-attachments/assets/fcdb9637-56b8-4dd3-8a23-1dc4e881bb36">|<video width="100" height="100" alt="After Fix" src="https://github.com/user-attachments/assets/3ff0bee9-8c80-4d3f-8042-acff1807ece6">|
…ght when the parent layout changes dynamically, causing incorrect sizing. (#33559) <!-- Please let the below note in for people that find this PR --> > [!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](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from this PR and let us know in a comment if this change resolves your issue. Thank you! <!-- !!!!!!! MAIN IS THE ONLY ACTIVE BRANCH. MAKE SURE THIS PR IS TARGETING MAIN. !!!!!!! --> ### Root Cause: The issue occurs because of Android RecyclerView reusing ViewHolders with previously measured dimensions and not automatically remeasuring them when only the layout size changes, causing the EmptyView to retain its old dimensions instead of adapting to the new available space. ### Fix Description: The fix involves explicitly forcing the EmptyView to remeasure when the RecyclerView dimensions change by detecting width or height updates, locating the corresponding EmptyView ViewHolder, and requesting a layout pass so it is resized to match the new available space. When the ViewHolder isn't immediately available, the layout request is deferred to the next UI loop iteration. ### Issues Fixed Fixes #33324 ### Tested the behaviour in the following platforms - [x] Android - [x] Windows - [x] iOS - [x] Mac ### Output Screenshot Before Issue Fix | After Issue Fix | |----------|----------| |<video width="100" height="100" alt="Before Fix" src="https://github.com/user-attachments/assets/fcdb9637-56b8-4dd3-8a23-1dc4e881bb36">|<video width="100" height="100" alt="After Fix" src="https://github.com/user-attachments/assets/3ff0bee9-8c80-4d3f-8042-acff1807ece6">|
…ght when the parent layout changes dynamically, causing incorrect sizing. (#33559) <!-- Please let the below note in for people that find this PR --> > [!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](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from this PR and let us know in a comment if this change resolves your issue. Thank you! <!-- !!!!!!! MAIN IS THE ONLY ACTIVE BRANCH. MAKE SURE THIS PR IS TARGETING MAIN. !!!!!!! --> ### Root Cause: The issue occurs because of Android RecyclerView reusing ViewHolders with previously measured dimensions and not automatically remeasuring them when only the layout size changes, causing the EmptyView to retain its old dimensions instead of adapting to the new available space. ### Fix Description: The fix involves explicitly forcing the EmptyView to remeasure when the RecyclerView dimensions change by detecting width or height updates, locating the corresponding EmptyView ViewHolder, and requesting a layout pass so it is resized to match the new available space. When the ViewHolder isn't immediately available, the layout request is deferred to the next UI loop iteration. ### Issues Fixed Fixes #33324 ### Tested the behaviour in the following platforms - [x] Android - [x] Windows - [x] iOS - [x] Mac ### Output Screenshot Before Issue Fix | After Issue Fix | |----------|----------| |<video width="100" height="100" alt="Before Fix" src="https://github.com/user-attachments/assets/fcdb9637-56b8-4dd3-8a23-1dc4e881bb36">|<video width="100" height="100" alt="After Fix" src="https://github.com/user-attachments/assets/3ff0bee9-8c80-4d3f-8042-acff1807ece6">|
.NET MAUI inflight/candidate introduces significant improvements across all platforms with focus on quality, performance, and developer experience. This release includes 20 commits with various improvements, bug fixes, and enhancements. ## Blazor - Fix for BlazorWebView Back Navigation Issues on Android 13+ After Predictive Back Gesture Changes by @SuthiYuvaraj in #33213 <details> <summary>🔧 Fixes</summary> - [Back navigation different between .net 9 and .net 10 blazor hybrid](#32767) </details> ## CollectionView - [Android] Fix for CollectionView.EmptyView does not remeasure its height when the parent layout changes dynamically, causing incorrect sizing. by @BagavathiPerumal in #33559 <details> <summary>🔧 Fixes</summary> - [`CollectionView.EmptyView` does not remeasure its height when the parent layout changes dynamically, causing incorrect sizing.](#33324) </details> - [Android] Fixed CollectionView reordering last item by @vitalii-vov in #17825 <details> <summary>🔧 Fixes</summary> - [Android app crashes when dragging into CollectionView](#17823) </details> ## DateTimePicker - [iOS] Fix VoiceOver focus not shifting to Picker/DatePicker/TimePicker popups by @kubaflo in #33152 <details> <summary>🔧 Fixes</summary> - [Voiceover does not automatically shift focus to the "Category" popup when it opens.: A11y_Developer balance version .NET 10_Project_ScreenReader](#30746) </details> ## Dialogalert - [iOS 26] Fix DisplayPromptAsync maxLength not enforced due to new multi-range delegate by @Shalini-Ashokan in #33616 <details> <summary>🔧 Fixes</summary> - [[iOS 26.1] DisplayPromptAsync ignores maxLength and does not respect RTL FlowDirection](#33549) </details> ## Flyout - [iOS] Shell: Account for SafeArea when positioning flyout footer by @kubaflo in #32891 <details> <summary>🔧 Fixes</summary> - [[IOS] Footer not displaying in iOS when StackOrientation.Horizontal is set on FlyoutFooter](#26395) </details> ## Fonts - Hide obsolete FontSize values from IDE autocomplete by @noiseonwires in #33694 ## Gestures - Android pan fixes by @BurningLights in #21547 <details> <summary>🔧 Fixes</summary> - [Flickering occurs while updating the width of ContentView through PanGestureRecognizer.](#20772) </details> ## Navigation - Shell: Add duplicate route validation for sibling elements by @SubhikshaSf4851 in #32296 <details> <summary>🔧 Fixes</summary> - [OnNavigatedTo is not called when navigating from a specific page](#14000) </details> ## Picker - Improved Unfocus support for Picker on Mac Catalyst by @kubaflo in #33127 <details> <summary>🔧 Fixes</summary> - [When using voiceover unable to access expanded list of project combo box: A11y_.NET maui_user can creat a tak_Screen reader](#30897) - [Task and Project controls are not accessible with keyboard:A11y_.NET maui_User can create a new task_Keyboard](#30891) </details> ## SafeArea - [iOS] SafeArea: Return Empty for non-ISafeAreaView views (opt-in model) by @praveenkumarkarunanithi in #33526 <details> <summary>🔧 Fixes</summary> - [[iOS] SafeArea is not applied when a ContentPage uses a ControlTemplate](#33458) </details> ## Shell - [iOS] Fix ObjectDisposedException in TraitCollectionDidChange on window disposal by @jeremy-visionaid in #33353 <details> <summary>🔧 Fixes</summary> - [Intermittent crash on exit on MacCatalyst - ObjectDisposedException](#33352) </details> - [Issue-Resolver] Explicit fallback for BackButtonBehavior lookup by @kubaflo in #33204 <details> <summary>🔧 Fixes</summary> - [Setting BackButtonBehavior to not visible or not enabled does not work](#28570) - [BackButtonBehavior not bound](#33139) </details> ## Templates - [Templates] Remove redundant SemanticProperties.Description attribute by @kubaflo in #33621 <details> <summary>🔧 Fixes</summary> - [Task and Project controls are not accessible with keyboard:A11y_.NET maui_User can create a new task_Keyboard](#30891) - [Unable to select "Tags" when Voiceover is turned on.: A11y_Developer balance version .NET 10_Project_ScreenReader](#30749) </details> ## Theme - [Windows] Fix runtime theme update for controls and TitleBar by @Tamilarasan-Paranthaman in #31714 <details> <summary>🔧 Fixes</summary> - [[Windows][MacOS?] Change title bar color when switching light/dark theme at runtime](#12507) - [OS system components ignore app theme](#22058) - [[Mac Catalyst][Windows] TitleBar not reacting on UserAppTheme changes](#30518) - [In dark theme "Back" and "hamburger" button icon color contrast with background color is less than 3:1: A11y_.NET maui_User can get all the insights of Dashboard_Non text Contrast](#30807) - [`Switch` is invisible on `PointOver` when theme has changed](#31819) </details> ## Theming - [XSG] Fix Style Setters referencing source-generated bindable properties by @simonrozsival in #33562 ## Titlebar - [Windows] Fix TitleBar.IsVisible = false the caption buttons become unresponsive by @devanathan-vaithiyanathan in #33256 <details> <summary>🔧 Fixes</summary> - [When TitleBar.IsVisible = false the caption buttons become unresponsive on Windows](#33171) </details> ## WebView - Fix WebView JavaScript string escaping for backslashes and quotes by @StephaneDelcroix in #33726 ## Xaml - [XSG] Fix NaN value in XAML generating invalid code by @StephaneDelcroix in #33533 <details> <summary>🔧 Fixes</summary> - [[XSG] NaN value in XAML generates invalid code](#33532) </details> <details> <summary>📦 Other (1)</summary> - Remove InternalsVisibleTo attributes for .NET MAUI Community Toolkit by @jfversluis via @Copilot in #33442 </details> **Full Changelog**: main...inflight/candidate
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:
The issue occurs because of Android RecyclerView reusing ViewHolders with previously measured dimensions and not automatically remeasuring them when only the layout size changes, causing the EmptyView to retain its old dimensions instead of adapting to the new available space.
Fix Description:
The fix involves explicitly forcing the EmptyView to remeasure when the RecyclerView dimensions change by detecting width or height updates, locating the corresponding EmptyView ViewHolder, and requesting a layout pass so it is resized to match the new available space. When the ViewHolder isn't immediately available, the layout request is deferred to the next UI loop iteration.
Issues Fixed
Fixes #33324
Tested the behaviour in the following platforms
Output Screenshot
CollectionViewLayoutIssue-BeforeFix.mov
CollectionViewLayoutIssue-AfterFix.mov