diff --git a/src/Controls/samples/Controls.Sample.Sandbox/MainPage.xaml b/src/Controls/samples/Controls.Sample.Sandbox/MainPage.xaml index e1762ab92d18..64f7ee97ccec 100644 --- a/src/Controls/samples/Controls.Sample.Sandbox/MainPage.xaml +++ b/src/Controls/samples/Controls.Sample.Sandbox/MainPage.xaml @@ -3,4 +3,10 @@ xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="Maui.Controls.Sample.MainPage" xmlns:local="clr-namespace:Maui.Controls.Sample"> + + + + + + \ No newline at end of file diff --git a/src/Controls/src/Core/Compatibility/Handlers/ListView/Android/TextCellRenderer.cs b/src/Controls/src/Core/Compatibility/Handlers/ListView/Android/TextCellRenderer.cs index c9226d6ae8f7..3f89fa8d0238 100644 --- a/src/Controls/src/Core/Compatibility/Handlers/ListView/Android/TextCellRenderer.cs +++ b/src/Controls/src/Core/Compatibility/Handlers/ListView/Android/TextCellRenderer.cs @@ -88,7 +88,7 @@ void UpdateMainText() View.SetMainTextColor(cell.TextColor); - PlatformInterop.RequestLayoutIfNeeded(View); + PlatformInterop2.RequestLayoutIfNeeded(View); } // ensure we don't get other people's BaseCellView's diff --git a/src/Controls/src/Core/Handlers/Items/Android/ItemContentView.cs b/src/Controls/src/Core/Handlers/Items/Android/ItemContentView.cs index 88ec1ed7fc81..21a9ece2fd62 100644 --- a/src/Controls/src/Core/Handlers/Items/Android/ItemContentView.cs +++ b/src/Controls/src/Core/Handlers/Items/Android/ItemContentView.cs @@ -129,7 +129,7 @@ void ElementMeasureInvalidated(object sender, System.EventArgs e) { if (this.IsAlive()) { - PlatformInterop.RequestLayoutIfNeeded(this); + PlatformInterop2.RequestLayoutIfNeeded(this); } else if (sender is VisualElement ve) { diff --git a/src/Core/src/Platform/Android/ContentViewGroup.cs b/src/Core/src/Platform/Android/ContentViewGroup.cs index a7d1194d1058..6e1e6c00e0cd 100644 --- a/src/Core/src/Platform/Android/ContentViewGroup.cs +++ b/src/Core/src/Platform/Android/ContentViewGroup.cs @@ -58,6 +58,16 @@ Graphics.Size CrossPlatformArrange(Graphics.Rect bounds) protected override void OnMeasure(int widthMeasureSpec, int heightMeasureSpec) { + { + if (CrossPlatformLayout is IViewHandler handler && + handler.VirtualView is IScrollView scrollView && + scrollView.Orientation == ScrollOrientation.Horizontal) + { + System.Diagnostics.Debug.WriteLine($""); + System.Diagnostics.Debug.WriteLine($"OnMeasure Start {scrollView} {widthMeasureSpec} {heightMeasureSpec}"); + } + } + if (CrossPlatformMeasure == null) { base.OnMeasure(widthMeasureSpec, heightMeasureSpec); @@ -85,6 +95,15 @@ protected override void OnMeasure(int widthMeasureSpec, int heightMeasureSpec) platformHeight = Math.Max(MinimumHeight, platformHeight); SetMeasuredDimension((int)platformWidth, (int)platformHeight); + + { + if (CrossPlatformLayout is IViewHandler handler && + handler.VirtualView is IScrollView scrollView && + scrollView.Orientation == ScrollOrientation.Horizontal) + { + System.Diagnostics.Debug.WriteLine($"OnMeasure End {scrollView} {platformWidth} {platformHeight}"); + } + } } protected override void OnLayout(bool changed, int left, int top, int right, int bottom) @@ -97,6 +116,14 @@ protected override void OnLayout(bool changed, int left, int top, int right, int var destination = _context.ToCrossPlatformRectInReferenceFrame(left, top, right, bottom); CrossPlatformArrange(destination); + + if (CrossPlatformLayout is IViewHandler handler && + handler.VirtualView is IScrollView scrollView && + scrollView.Orientation == ScrollOrientation.Horizontal) + { + System.Diagnostics.Debug.WriteLine($"OnLayout {scrollView} {destination} {left} {top} {right} {bottom}"); + System.Diagnostics.Debug.WriteLine($""); + } } internal IBorderStroke? Clip diff --git a/src/Core/src/Platform/Android/MauiScrollView.cs b/src/Core/src/Platform/Android/MauiScrollView.cs index 4ad2083e2064..a9f5476386ec 100644 --- a/src/Core/src/Platform/Android/MauiScrollView.cs +++ b/src/Core/src/Platform/Android/MauiScrollView.cs @@ -126,7 +126,7 @@ public override bool OnInterceptTouchEvent(MotionEvent? ev) { // See also MauiHorizontalScrollView notes in OnInterceptTouchEvent - if (ev == null) + /*if (ev == null) return false; // set the start point for the bidirectional scroll; @@ -136,14 +136,14 @@ public override bool OnInterceptTouchEvent(MotionEvent? ev) { LastY = ev.RawY; LastX = ev.RawX; - } + }*/ return base.OnInterceptTouchEvent(ev); } public override bool OnTouchEvent(MotionEvent? ev) { - if (ev == null || !Enabled) + /*if (ev == null || !Enabled) return false; if (ShouldSkipOnTouch) @@ -172,7 +172,7 @@ public override bool OnTouchEvent(MotionEvent? ev) } // Fall through to base.OnTouchEvent, it'll take care of the Y scrolling } - } + }*/ return base.OnTouchEvent(ev); } @@ -188,7 +188,7 @@ protected override void OnLayout(bool changed, int left, int top, int right, int { base.OnLayout(changed, left, top, right, bottom); - if (_hScrollView?.Parent == this && _content is not null) + /*if (_hScrollView?.Parent == this && _content is not null) { var scrollViewContentHeight = _content.Height; var hScrollViewHeight = bottom - top; @@ -204,7 +204,7 @@ protected override void OnLayout(bool changed, int left, int top, int right, int MeasureSpec.MakeMeasureSpec(hScrollViewHeight, MeasureSpecMode.Exactly)); _hScrollView.Layout(0, 0, hScrollViewWidth, hScrollViewHeight); - } + }*/ if (CrossPlatformArrange == null) { @@ -333,7 +333,7 @@ public override void Draw(Canvas? canvas) { try { - canvas?.ClipRect(canvas?.ClipBounds!); + //canvas?.ClipRect(canvas?.ClipBounds!); base.Draw(canvas!); } @@ -348,7 +348,7 @@ public override void Draw(Canvas? canvas) public override bool OnInterceptTouchEvent(MotionEvent? ev) { - if (ev == null || _parentScrollView == null) + /*if (ev == null || _parentScrollView == null) return false; // TODO ezhart 2021-07-12 The previous version of this checked _renderer.Element.InputTransparent; we don't have acces to that here, @@ -363,14 +363,14 @@ public override bool OnInterceptTouchEvent(MotionEvent? ev) { _parentScrollView.LastY = ev.RawY; _parentScrollView.LastX = ev.RawX; - } + }*/ return base.OnInterceptTouchEvent(ev); } public override bool OnTouchEvent(MotionEvent? ev) { - if (ev == null || _parentScrollView == null) + /*if (ev == null || _parentScrollView == null) return false; if (!_parentScrollView.Enabled) @@ -396,7 +396,7 @@ public override bool OnTouchEvent(MotionEvent? ev) _parentScrollView.ScrollBy(0, (int)dY); // Fall through to base.OnTouchEvent, it'll take care of the X scrolling } - } + }*/ return base.OnTouchEvent(ev); } diff --git a/src/Core/src/Platform/Android/ViewExtensions.cs b/src/Core/src/Platform/Android/ViewExtensions.cs index da702477f4d8..b208978d82fa 100644 --- a/src/Core/src/Platform/Android/ViewExtensions.cs +++ b/src/Core/src/Platform/Android/ViewExtensions.cs @@ -23,6 +23,39 @@ namespace Microsoft.Maui.Platform { + + internal static class PlatformInterop2 + { + public static void RequestLayoutIfNeeded(View view) + { + // If the view isn't currently in the layout process, then we simply request + // that layout happen next time around + if (!view.IsInLayout) { + view.RequestLayout(); + return; + } + + /* + Something is requesting layout while the view is already in the middle of a layout pass. This is most + likely because a layout-affecting property has been data bound to another layout-affecting property, e.g. + binding the width of a child control to the ActualWidth of its parent. + + If we simply call `requestLayout()` again right now, it will set a flag which will be cleared at the end + of the current layout pass, and the view will not be laid out with the updated values. + Instead, we post the layout request to the UI thread's queue, ensuring that it will happen after the current + layout pass has finished. Layout will happen again with the updated values. + */ + + + view.Post(() => + { + if (!view.IsInLayout) { + view.RequestLayout(); + } + }); + } + } + public static partial class ViewExtensions { public static void Initialize(this AView platformView, IView view) @@ -339,19 +372,19 @@ public static void UpdateAutomationId(this AView platformView, IView view) public static void InvalidateMeasure(this AView platformView, IView view) { - PlatformInterop.RequestLayoutIfNeeded(platformView); + PlatformInterop2.RequestLayoutIfNeeded(platformView); } public static void UpdateWidth(this AView platformView, IView view) { // GetDesiredSize will take the specified Width into account during the layout - PlatformInterop.RequestLayoutIfNeeded(platformView); + PlatformInterop2.RequestLayoutIfNeeded(platformView); } public static void UpdateHeight(this AView platformView, IView view) { // GetDesiredSize will take the specified Height into account during the layout - PlatformInterop.RequestLayoutIfNeeded(platformView); + PlatformInterop2.RequestLayoutIfNeeded(platformView); } public static void UpdateMinimumHeight(this AView platformView, IView view) @@ -360,7 +393,7 @@ public static void UpdateMinimumHeight(this AView platformView, IView view) var value = (int)platformView.Context!.ToPixels(min); platformView.SetMinimumHeight(value); - PlatformInterop.RequestLayoutIfNeeded(platformView); + PlatformInterop2.RequestLayoutIfNeeded(platformView); } public static void UpdateMinimumWidth(this AView platformView, IView view) @@ -369,19 +402,19 @@ public static void UpdateMinimumWidth(this AView platformView, IView view) var value = (int)platformView.Context!.ToPixels(min); platformView.SetMinimumWidth(value); - PlatformInterop.RequestLayoutIfNeeded(platformView); + PlatformInterop2.RequestLayoutIfNeeded(platformView); } public static void UpdateMaximumHeight(this AView platformView, IView view) { // GetDesiredSize will take the specified Height into account during the layout - PlatformInterop.RequestLayoutIfNeeded(platformView); + PlatformInterop2.RequestLayoutIfNeeded(platformView); } public static void UpdateMaximumWidth(this AView platformView, IView view) { // GetDesiredSize will take the specified Height into account during the layout - PlatformInterop.RequestLayoutIfNeeded(platformView); + PlatformInterop2.RequestLayoutIfNeeded(platformView); } public static async Task UpdateBackgroundImageSourceAsync(this AView platformView, IImageSource? imageSource, IImageSourceServiceProvider? provider)