3
3
using Android . Runtime ;
4
4
using Android . Util ;
5
5
using Android . Views ;
6
+ using Microsoft . Maui . Platform ;
6
7
using AView = Android . Views . View ;
7
8
8
9
namespace Microsoft . Maui . Controls . Platform . Compatibility
9
10
{
10
11
public class ContainerView : ViewGroup
11
12
{
13
+ int _parentTopPadding ;
12
14
View _view ;
13
15
ShellViewRenderer _shellContentView ;
14
16
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 ; }
16
20
17
21
public ContainerView ( Context context , View view , IMauiContext mauiContext ) : base ( context )
18
22
{
@@ -60,6 +64,28 @@ protected override void OnLayout(bool changed, int l, int t, int r, int b)
60
64
( ( IPlatformViewHandler ) View . Handler ) . LayoutVirtualView ( l , t , r , b ) ;
61
65
}
62
66
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
+
63
89
protected override void OnMeasure ( int widthMeasureSpec , int heightMeasureSpec )
64
90
{
65
91
if ( View == null )
@@ -77,8 +103,8 @@ protected override void OnMeasure(int widthMeasureSpec, int heightMeasureSpec)
77
103
var width = widthMeasureSpec . GetSize ( ) ;
78
104
var height = heightMeasureSpec . GetSize ( ) ;
79
105
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 ) ;
82
108
83
109
double ? maxHeight = null ;
84
110
@@ -97,6 +123,54 @@ protected override void OnMeasure(int widthMeasureSpec, int heightMeasureSpec)
97
123
}
98
124
99
125
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
+
100
174
SetMeasuredDimension ( ( int ) size . Width , ( int ) size . Height ) ;
101
175
}
102
176
0 commit comments