From 2d7fe691d33785c045f12293271ff8fcb99e5c39 Mon Sep 17 00:00:00 2001 From: Shane Neuville Date: Mon, 4 Mar 2024 17:02:14 -0600 Subject: [PATCH 1/2] Don't measure during the layout pass --- .../Issues/Issue20920.cs | 32 +++++++++++++++++ .../tests/UITests/Tests/Issues/Issue20920.cs | 27 +++++++++++++++ .../src/Platform/Android/MauiScrollView.cs | 34 +++++++++++++++---- .../net-android/PublicAPI.Unshipped.txt | 1 + 4 files changed, 87 insertions(+), 7 deletions(-) create mode 100644 src/Controls/samples/Controls.Sample.UITests/Issues/Issue20920.cs create mode 100644 src/Controls/tests/UITests/Tests/Issues/Issue20920.cs diff --git a/src/Controls/samples/Controls.Sample.UITests/Issues/Issue20920.cs b/src/Controls/samples/Controls.Sample.UITests/Issues/Issue20920.cs new file mode 100644 index 000000000000..8bf180e58b9d --- /dev/null +++ b/src/Controls/samples/Controls.Sample.UITests/Issues/Issue20920.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Microsoft.Maui.Controls; + +namespace Maui.Controls.Sample.Issues +{ + [Issue(IssueTracker.None, 20920, "Nested ScrollView does not work in Android", PlatformAffected.Android)] + public class Issue20920 : TestContentPage + { + public static Label DestructorCount = new Label() { Text = "0" }; + protected override void Init() + { + Content = new ScrollView() + { + Orientation = Microsoft.Maui.ScrollOrientation.Horizontal, + Content = new ScrollView() + { + Orientation = Microsoft.Maui.ScrollOrientation.Vertical, + Content = new Image() + { + Margin = 100, + AutomationId = "dotnet_bot", + Source = "dotnet_bot.png", + HeightRequest = 1000, + WidthRequest = 1000 + } + } + }; + } + } +} \ No newline at end of file diff --git a/src/Controls/tests/UITests/Tests/Issues/Issue20920.cs b/src/Controls/tests/UITests/Tests/Issues/Issue20920.cs new file mode 100644 index 000000000000..668c04b93909 --- /dev/null +++ b/src/Controls/tests/UITests/Tests/Issues/Issue20920.cs @@ -0,0 +1,27 @@ +using System.Drawing; +using NUnit.Framework; +using UITest.Appium; +using UITest.Core; + +namespace Microsoft.Maui.AppiumTests.Issues; + +public class Issue20920 : _IssuesUITest +{ + public Issue20920(TestDevice device) : base(device) { } + + public override string Issue => "Nested ScrollView does not work in Android"; + + [Test] + public void ScrollingBothDirectionsWithNestedScrollViews() + { + var initialPosition = App.WaitForElement("dotnet_bot").GetRect(); + + App.ScrollDown("dotnet_bot"); + App.ScrollRight("dotnet_bot"); + + var afterScrollPosition = App.WaitForElement("dotnet_bot").GetRect(); + + Assert.Less(afterScrollPosition.X, initialPosition.X); + Assert.Less(afterScrollPosition.Y, initialPosition.Y); + } +} \ No newline at end of file diff --git a/src/Core/src/Platform/Android/MauiScrollView.cs b/src/Core/src/Platform/Android/MauiScrollView.cs index 4ad2083e2064..0f182b7d22f6 100644 --- a/src/Core/src/Platform/Android/MauiScrollView.cs +++ b/src/Core/src/Platform/Android/MauiScrollView.cs @@ -85,6 +85,7 @@ public void SetContent(View content) public void SetOrientation(ScrollOrientation orientation) { + bool orientationChanged = _scrollOrientation != orientation; _scrollOrientation = orientation; if (orientation == ScrollOrientation.Horizontal || orientation == ScrollOrientation.Both) @@ -109,6 +110,12 @@ public void SetOrientation(ScrollOrientation orientation) AddView(_hScrollView); } + // If the user has changed between horiztonal and both we want to request a new layout + // so the Horizontal Layout can be adjusted to satisfy the new orientation. + else if(orientationChanged) + { + PlatformInterop.RequestLayoutIfNeeded(this); + } } else { @@ -143,7 +150,7 @@ public override bool OnInterceptTouchEvent(MotionEvent? ev) public override bool OnTouchEvent(MotionEvent? ev) { - if (ev == null || !Enabled) + if (ev == null || !Enabled || _scrollOrientation == ScrollOrientation.Neither) return false; if (ShouldSkipOnTouch) @@ -184,6 +191,25 @@ void IScrollBarView.AwakenScrollBars() bool IScrollBarView.ScrollBarsInitialized { get; set; } + protected override void OnMeasure(int widthMeasureSpec, int heightMeasureSpec) + { + base.OnMeasure(widthMeasureSpec, heightMeasureSpec); + + // If we have bidirectional scrolling then we can just let everything flow through naturally. + // The HorizontalScrollView will automatically size its height to the content and thus enable + // vertical scolling + // If we're only enabling horizontal scrolling then we want to force the horizontal scrollView + // to be the same size as the NestedScrollView this way it can't be scrolled vertically + if (_hScrollView?.Parent == this && _content is not null && !_isBidirectional) + { + var hScrollViewHeight = this.MeasuredHeight; + var hScrollViewWidth = this.MeasuredWidth; + + _hScrollView.Measure(MeasureSpec.MakeMeasureSpec(hScrollViewWidth, MeasureSpecMode.Exactly), + MeasureSpec.MakeMeasureSpec(hScrollViewHeight, MeasureSpecMode.Exactly)); + } + } + protected override void OnLayout(bool changed, int left, int top, int right, int bottom) { base.OnLayout(changed, left, top, right, bottom); @@ -197,12 +223,6 @@ protected override void OnLayout(bool changed, int left, int top, int right, int //if we are scrolling both ways we need to lay out our MauiHorizontalScrollView with more than the available height //so its parent the NestedScrollView can scroll vertically hScrollViewHeight = _isBidirectional ? Math.Max(hScrollViewHeight, scrollViewContentHeight) : hScrollViewHeight; - - // Ensure that the horizontal scrollview has been measured at the actual target size - // so that it updates its internal bookkeeping to use the full size - _hScrollView.Measure(MeasureSpec.MakeMeasureSpec(right - left, MeasureSpecMode.Exactly), - MeasureSpec.MakeMeasureSpec(hScrollViewHeight, MeasureSpecMode.Exactly)); - _hScrollView.Layout(0, 0, hScrollViewWidth, hScrollViewHeight); } diff --git a/src/Core/src/PublicAPI/net-android/PublicAPI.Unshipped.txt b/src/Core/src/PublicAPI/net-android/PublicAPI.Unshipped.txt index e8b26bb171ca..0e4bd6077a93 100644 --- a/src/Core/src/PublicAPI/net-android/PublicAPI.Unshipped.txt +++ b/src/Core/src/PublicAPI/net-android/PublicAPI.Unshipped.txt @@ -65,6 +65,7 @@ override Microsoft.Maui.Layouts.FlexBasis.Equals(object? obj) -> bool override Microsoft.Maui.Layouts.FlexBasis.GetHashCode() -> int override Microsoft.Maui.MauiAppCompatActivity.DispatchTouchEvent(Android.Views.MotionEvent? e) -> bool override Microsoft.Maui.Platform.ContentViewGroup.GetClipPath(int width, int height) -> Android.Graphics.Path? +override Microsoft.Maui.Platform.MauiScrollView.OnMeasure(int widthMeasureSpec, int heightMeasureSpec) -> void override Microsoft.Maui.Platform.NavigationViewFragment.OnDestroy() -> void override Microsoft.Maui.PlatformContentViewGroup.JniPeerMembers.get -> Java.Interop.JniPeerMembers! override Microsoft.Maui.PlatformContentViewGroup.ThresholdClass.get -> nint From 01a7dde421def65b01bb4bfd59e4dfd0ea1c69db Mon Sep 17 00:00:00 2001 From: Shane Neuville Date: Mon, 4 Mar 2024 20:38:24 -0600 Subject: [PATCH 2/2] Update Issue20920.cs --- src/Controls/tests/UITests/Tests/Issues/Issue20920.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Controls/tests/UITests/Tests/Issues/Issue20920.cs b/src/Controls/tests/UITests/Tests/Issues/Issue20920.cs index 668c04b93909..20b7e215c14e 100644 --- a/src/Controls/tests/UITests/Tests/Issues/Issue20920.cs +++ b/src/Controls/tests/UITests/Tests/Issues/Issue20920.cs @@ -14,6 +14,8 @@ public Issue20920(TestDevice device) : base(device) { } [Test] public void ScrollingBothDirectionsWithNestedScrollViews() { + // TODO: Correct this test for other platforms + this.IgnoreIfPlatforms(new TestDevice[] { TestDevice.Mac, TestDevice.Windows, TestDevice.iOS }); var initialPosition = App.WaitForElement("dotnet_bot").GetRect(); App.ScrollDown("dotnet_bot"); @@ -24,4 +26,4 @@ public void ScrollingBothDirectionsWithNestedScrollViews() Assert.Less(afterScrollPosition.X, initialPosition.X); Assert.Less(afterScrollPosition.Y, initialPosition.Y); } -} \ No newline at end of file +}