Skip to content

Commit de663a9

Browse files
PureWeenrachelkang
andauthored
Fixup Flyout Template Parts (#8022)
* Fixup Flyout Template Parts * - fix close enough comparisons * - fix comparisons * Update ShellFlyoutTemplatedContentRenderer.cs * Update src/Controls/tests/DeviceTests/Elements/Shell/ShellTests.Android.cs Co-authored-by: Rachel Kang <rachelkang@microsoft.com> Co-authored-by: Rachel Kang <rachelkang@microsoft.com>
1 parent 7c0c940 commit de663a9

17 files changed

+861
-143
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<Shell
2+
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
3+
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
4+
x:Class="Maui.Controls.Sample.SandboxShell"
5+
xmlns:local="clr-namespace:Maui.Controls.Sample"
6+
x:Name="shell">
7+
<FlyoutItem Title="Flyout Item Title">
8+
<ShellContent ContentTemplate="{DataTemplate local:MainPage}"></ShellContent>
9+
</FlyoutItem>
10+
</Shell>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
using System;
2+
using System.Collections.ObjectModel;
3+
using System.Diagnostics;
4+
using Microsoft.Extensions.DependencyInjection;
5+
using Microsoft.Maui;
6+
using Microsoft.Maui.Controls;
7+
8+
namespace Maui.Controls.Sample
9+
{
10+
public partial class SandboxShell : Shell
11+
{
12+
public SandboxShell()
13+
{
14+
InitializeComponent();
15+
}
16+
}
17+
}

src/Controls/src/Core/Compatibility/Handlers/Shell/Android/ContainerView.cs

+77-3
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,20 @@
33
using Android.Runtime;
44
using Android.Util;
55
using Android.Views;
6+
using Microsoft.Maui.Platform;
67
using AView = Android.Views.View;
78

89
namespace Microsoft.Maui.Controls.Platform.Compatibility
910
{
1011
public class ContainerView : ViewGroup
1112
{
13+
int _parentTopPadding;
1214
View _view;
1315
ShellViewRenderer _shellContentView;
1416
readonly IMauiContext _mauiContext;
15-
AView PlatformView => _view?.Handler?.PlatformView as AView;
17+
internal AView PlatformView => _view?.Handler?.PlatformView as AView;
18+
internal Graphics.Size DesiredSize { get; private set; }
19+
internal Graphics.Size MinimumSize { get; private set; }
1620

1721
public ContainerView(Context context, View view, IMauiContext mauiContext) : base(context)
1822
{
@@ -60,6 +64,28 @@ protected override void OnLayout(bool changed, int l, int t, int r, int b)
6064
((IPlatformViewHandler)View.Handler).LayoutVirtualView(l, t, r, b);
6165
}
6266

67+
// This indicates the amount of top padding the parent wants this control to have. This
68+
// allows us to adjust the height measured so that the parent container maintains a constant height.
69+
//
70+
// This is mainly used with ShellHeaderFlyoutBehavior.CollapseOnScroll
71+
// The container added to the AppBarLayout needs to maintain a constant height otherwise the
72+
// OnOffsetChanged value passed in to IOnOffsetChangedListener will just infinitely change as the
73+
// header size keeps changing. So in order to make the collapse work we just have to resize the xplat view
74+
// and then shift it down in the container.
75+
//
76+
// In theory we could call `IView.Arrange` with the desired position in the parent but then that sets the xplat `Frame`
77+
// to values that don't really make any sense from an xplat perspective. This just causes implementation details
78+
// to leak up into the abstraction. It'll just be confusing that the headers position is "0,300" even though it looks
79+
// like it's "0,0"
80+
internal void SetParentTopPadding(int v)
81+
{
82+
if (_parentTopPadding == v)
83+
return;
84+
85+
_parentTopPadding = v;
86+
this.MaybeRequestLayout();
87+
}
88+
6389
protected override void OnMeasure(int widthMeasureSpec, int heightMeasureSpec)
6490
{
6591
if (View == null)
@@ -77,8 +103,8 @@ protected override void OnMeasure(int widthMeasureSpec, int heightMeasureSpec)
77103
var width = widthMeasureSpec.GetSize();
78104
var height = heightMeasureSpec.GetSize();
79105

80-
var measureWidth = width > 0 ? width : MeasureSpecMode.Unspecified.MakeMeasureSpec(0);
81-
var measureHeight = height > 0 ? height : MeasureSpecMode.Unspecified.MakeMeasureSpec(0);
106+
var measureWidth = width > 0 ? MeasureSpecMode.Exactly.MakeMeasureSpec(width) : MeasureSpecMode.Unspecified.MakeMeasureSpec(0);
107+
var measureHeight = height > 0 ? MeasureSpecMode.Exactly.MakeMeasureSpec(height) : MeasureSpecMode.Unspecified.MakeMeasureSpec(0);
82108

83109
double? maxHeight = null;
84110

@@ -97,6 +123,54 @@ protected override void OnMeasure(int widthMeasureSpec, int heightMeasureSpec)
97123
}
98124

99125
var size = _shellContentView.Measure(measureWidth, measureHeight, null, (int?)maxHeight);
126+
127+
if (size.Height < this.MinimumHeight)
128+
size = new Graphics.Size(size.Width, this.MinimumHeight);
129+
130+
DesiredSize = size;
131+
var newHeight = size.Height;
132+
133+
if (_parentTopPadding > 0)
134+
{
135+
newHeight -= _parentTopPadding;
136+
}
137+
138+
if (newHeight != size.Height)
139+
{
140+
// Android treats zero as unspecified so we need to set the height to at least 1.
141+
// If there's a view that's able to squish/stretch then passing in zero will give it's stretched size
142+
// but what we want here is the squished size.
143+
var minHeightMeasureSpec = 1;
144+
145+
if (MinimumHeight > 0)
146+
minHeightMeasureSpec = MinimumHeight;
147+
148+
if (newHeight <= 0)
149+
{
150+
newHeight = minHeightMeasureSpec;
151+
}
152+
153+
// For collapse on scroll we need to know the minimum size the view can reach so we can set the minimum size
154+
// on the headers container. This is the only way to get the AppBarLayout collapse behavior to stop producing
155+
// offset changes once the header has reached the minimum size it can get to
156+
MinimumSize = _shellContentView.Measure(measureWidth, MeasureSpecMode.Exactly.MakeMeasureSpec(minHeightMeasureSpec), null, (int?)maxHeight);
157+
158+
// If the offset of the appbar layout hasn't reached the minimum size then
159+
// we use the new collapsed size to remeasure so everything is the correct size
160+
if (newHeight > minHeightMeasureSpec)
161+
{
162+
size = _shellContentView.Measure(measureWidth, MeasureSpecMode.Exactly.MakeMeasureSpec((int)newHeight), null, (int?)maxHeight);
163+
}
164+
else
165+
{
166+
size = MinimumSize;
167+
}
168+
}
169+
else
170+
{
171+
this.MinimumSize = new Graphics.Size((int)size.Width, this.MinimumHeight);
172+
}
173+
100174
SetMeasuredDimension((int)size.Width, (int)size.Height);
101175
}
102176

src/Controls/src/Core/Compatibility/Handlers/Shell/Android/ShellContentView.cs

+11-5
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
using Google.Android.Material.AppBar;
77
using Microsoft.Maui.Controls.Platform.Compatibility;
88
using Microsoft.Maui.Graphics;
9+
using Microsoft.Maui.Platform;
910
using AView = Android.Views.View;
1011
using LP = Android.Views.ViewGroup.LayoutParams;
1112

@@ -15,7 +16,7 @@ namespace Microsoft.Maui.Controls.Platform.Compatibility
1516
internal class ShellViewRenderer
1617
{
1718
public IViewHandler Handler { get; private set; }
18-
View _view;
19+
IView _view;
1920
WeakReference<Context> _context;
2021
readonly IMauiContext _mauiContext;
2122
IView MauiView => View;
@@ -38,7 +39,7 @@ public ShellViewRenderer(Context context, View view, IMauiContext mauiContext)
3839
View = view;
3940
}
4041

41-
public View View
42+
public IView View
4243
{
4344
get { return _view; }
4445
set
@@ -49,6 +50,7 @@ public View View
4950

5051
public void TearDown()
5152
{
53+
MauiView?.Handler?.DisconnectHandler();
5254
View = null;
5355
Handler = null;
5456
_view = null;
@@ -89,15 +91,19 @@ public Graphics.Size Measure(int widthMeasureSpec, int heightMeasureSpec, int? m
8991
heightMeasureSpec = MeasureSpecMode.Unspecified.MakeMeasureSpec(0);
9092

9193
PlatformView.LayoutParameters = layoutParams;
94+
9295
return ((IPlatformViewHandler)_view.Handler).MeasureVirtualView(widthMeasureSpec, heightMeasureSpec);
9396
}
9497

95-
public virtual void OnViewSet(View view)
98+
public virtual void OnViewSet(IView view)
9699
{
97-
if (View != null)
100+
if (view == View)
101+
return;
102+
103+
if (View != null && PlatformView.IsAlive())
98104
{
99105
PlatformView.RemoveFromParent();
100-
View.Handler = null;
106+
View.Handler?.DisconnectHandler();
101107
}
102108

103109
_view = view;

src/Controls/src/Core/Compatibility/Handlers/Shell/Android/ShellFlyoutRenderer.cs

+4-4
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ void IFlyoutBehaviorObserver.OnFlyoutBehaviorChanged(FlyoutBehavior behavior)
124124
int _flyoutWidthDefault;
125125
double _flyoutWidth = -1;
126126
double _flyoutHeight;
127-
bool _flyoutFirstDrawPassFinished;
127+
internal bool FlyoutFirstDrawPassFinished { get; private set; }
128128
int _currentLockMode;
129129
bool _disposed;
130130
Brush _scrimBrush;
@@ -182,10 +182,10 @@ protected override bool DrawChild(Canvas canvas, AView child, long drawingTime)
182182
canvas.DrawRect(0, 0, Width, Height, _scrimPaint);
183183
}
184184

185-
if (!_flyoutFirstDrawPassFinished && _flyoutContent != null)
185+
if (!FlyoutFirstDrawPassFinished && _flyoutContent != null)
186186
{
187187
if (child == _flyoutContent?.AndroidView)
188-
_flyoutFirstDrawPassFinished = true;
188+
FlyoutFirstDrawPassFinished = true;
189189

190190
if (this.IsDrawerOpen(_flyoutContent.AndroidView) != _shellContext.Shell.FlyoutIsPresented)
191191
{
@@ -261,7 +261,7 @@ protected virtual void OnShellPropertyChanged(object sender, PropertyChangedEven
261261

262262
if (e.PropertyName == Shell.FlyoutIsPresentedProperty.PropertyName)
263263
{
264-
if (!_flyoutFirstDrawPassFinished)
264+
if (!FlyoutFirstDrawPassFinished)
265265
{
266266
// if the first draw pass hasn't happened yet
267267
// then calling close/open drawer really confuses

0 commit comments

Comments
 (0)