Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,11 @@ protected override void OnMeasure(int widthMeasureSpec, int heightMeasureSpec)

if (Content.VirtualView.Handler is IPlatformViewHandler pvh)
{
var widthSpec = Context.CreateMeasureSpec(targetWidth,
var (widthSpec, _, _) = Context.CreateMeasureSpec(targetWidth,
double.IsInfinity(targetWidth) ? double.NaN : targetWidth
, minimumSize: double.NaN, maximumSize: targetWidth);

var heightSpec = Context.CreateMeasureSpec(targetHeight, double.IsInfinity(targetHeight) ? double.NaN : targetHeight
var (heightSpec, _, _) = Context.CreateMeasureSpec(targetHeight, double.IsInfinity(targetHeight) ? double.NaN : targetHeight
, minimumSize: double.NaN, maximumSize: targetHeight);

var size = pvh.MeasureVirtualView(widthSpec, heightSpec);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import android.util.AttributeSet;
import android.view.ViewGroup;

public abstract class PlatformContentViewGroup extends ViewGroup {
public abstract class PlatformContentViewGroup extends PlatformViewGroup {

public PlatformContentViewGroup(Context context) {
super(context);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package com.microsoft.maui;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Path;
import android.util.AttributeSet;
import android.view.ViewGroup;

public abstract class PlatformViewGroup extends ViewGroup {

private int lastWidthMeasureSpec;
private int lastHeightMeasureSpec;
private int measuredWidth;
private int measuredHeight;
private boolean hasValidMeasure;

public PlatformViewGroup(Context context) {
super(context);
}

public PlatformViewGroup(Context context, AttributeSet attrs) {
super(context, attrs);
}

public PlatformViewGroup(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}

public PlatformViewGroup(Context context, AttributeSet attrs, int defStyle, int defStyleRes) {
super(context, attrs, defStyle, defStyleRes);
}

@Override
public void requestLayout() {
super.requestLayout();
hasValidMeasure = false;
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if (hasValidMeasure && lastWidthMeasureSpec == widthMeasureSpec && lastHeightMeasureSpec == heightMeasureSpec) {
setMeasuredDimension(measuredWidth, measuredHeight);
return;
}

doMeasure(widthMeasureSpec, heightMeasureSpec);
lastWidthMeasureSpec = widthMeasureSpec;
lastHeightMeasureSpec = heightMeasureSpec;
measuredWidth = getMeasuredWidth();
measuredHeight = getMeasuredHeight();
hasValidMeasure = true;
}

protected abstract void doMeasure(int widthMeasureSpec, int heightMeasureSpec);

public void quickMeasure(int widthMeasureSpec, int heightMeasureSpec, int measuredWidth, int measuredHeight) {
this.measuredWidth = measuredWidth;
this.measuredHeight = measuredHeight;
lastWidthMeasureSpec = widthMeasureSpec;
lastHeightMeasureSpec = heightMeasureSpec;
hasValidMeasure = true;
measure(widthMeasureSpec, heightMeasureSpec);
}

public boolean needsMeasure(int widthMeasureSpec, int heightMeasureSpec) {
return !hasValidMeasure || lastWidthMeasureSpec != widthMeasureSpec || lastHeightMeasureSpec != heightMeasureSpec;
}
}
4 changes: 2 additions & 2 deletions src/Core/src/Handlers/ScrollView/ScrollViewHandler.Android.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,8 @@ public override Size GetDesiredSize(double widthConstraint, double heightConstra
}

// Create a spec to handle the native measure
var widthSpec = Context.CreateMeasureSpec(widthConstraint, virtualView.Width, virtualView.MinimumWidth, virtualView.MaximumWidth);
var heightSpec = Context.CreateMeasureSpec(heightConstraint, virtualView.Height, virtualView.MinimumHeight, virtualView.MaximumHeight);
var (widthSpec, _, _) = Context.CreateMeasureSpec(widthConstraint, virtualView.Width, virtualView.MinimumWidth, virtualView.MaximumWidth);
var (heightSpec, _, _) = Context.CreateMeasureSpec(heightConstraint, virtualView.Height, virtualView.MinimumHeight, virtualView.MaximumHeight);

if (platformView.FillViewport)
{
Expand Down
32 changes: 27 additions & 5 deletions src/Core/src/Handlers/ViewHandlerExtensions.Android.cs
Original file line number Diff line number Diff line change
Expand Up @@ -88,12 +88,34 @@ internal static Size GetDesiredSizeFromHandler(this IViewHandler viewHandler, do
}

// Create a spec to handle the native measure
var widthSpec = context.CreateMeasureSpec(widthConstraint, virtualView.Width, virtualView.MinimumWidth, virtualView.MaximumWidth);
var heightSpec = context.CreateMeasureSpec(heightConstraint, virtualView.Height, virtualView.MinimumHeight, virtualView.MaximumHeight);
var (widthSpec, widthDp, widthExact) = context.CreateMeasureSpec(widthConstraint, virtualView.Width, virtualView.MinimumWidth, virtualView.MaximumWidth);
var (heightSpec, heightDp, heightExact) = context.CreateMeasureSpec(heightConstraint, virtualView.Height, virtualView.MinimumHeight, virtualView.MaximumHeight);
int measuredWidth, measuredHeight;

var packed = PlatformInterop.MeasureAndGetWidthAndHeight(platformView, widthSpec, heightSpec);
var measuredWidth = (int)(packed >> 32);
var measuredHeight = (int)(packed & 0xffffffffL);
if (platformView is ICrossPlatformLayoutBacking { CrossPlatformLayout: { } crossPlatformLayout } and IPlatformQuickLayout quickLayout &&
quickLayout.NeedsMeasure(widthSpec, heightSpec))
{
var measure = crossPlatformLayout.CrossPlatformMeasure(widthDp, heightDp);

// If the measure spec was exact, we should return the explicit size value, even if the content
// measure came out to a different size
var measuredWidthDp = widthExact ? widthDp : measure.Width;
var measuredHeightDp = heightExact ? heightDp : measure.Height;

measuredWidth = (int)context.ToPixels(measuredWidthDp);
measuredHeight = (int)context.ToPixels(measuredHeightDp);

// Minimum values win over everything
measuredWidth = Math.Max(platformView.MinimumWidth, measuredWidth);
measuredHeight = Math.Max(platformView.MinimumHeight, measuredHeight);
quickLayout.QuickMeasure(widthSpec, heightSpec, measuredWidth, measuredHeight);
}
else
{
var packed = PlatformInterop.MeasureAndGetWidthAndHeight(platformView, widthSpec, heightSpec);
measuredWidth = (int)(packed >> 32);
measuredHeight = (int)(packed & 0xffffffffL);
}

// Convert back to xplat sizes for the return value
return context.FromPixels(measuredWidth, measuredHeight);
Expand Down
97 changes: 66 additions & 31 deletions src/Core/src/Platform/Android/ContentViewGroup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

namespace Microsoft.Maui.Platform
{
public class ContentViewGroup : PlatformContentViewGroup, ICrossPlatformLayoutBacking, IVisualTreeElementProvidable
public class ContentViewGroup : PlatformContentViewGroup, ICrossPlatformLayoutBacking, IVisualTreeElementProvidable, IPlatformQuickLayout
{
IBorderStroke? _clip;
readonly Context _context;
Expand Down Expand Up @@ -56,36 +56,36 @@ Graphics.Size CrossPlatformArrange(Graphics.Rect bounds)
return CrossPlatformLayout?.CrossPlatformArrange(bounds) ?? Graphics.Size.Zero;
}

protected override void OnMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
if (CrossPlatformLayout is null)
{
base.OnMeasure(widthMeasureSpec, heightMeasureSpec);
return;
}

var deviceIndependentWidth = widthMeasureSpec.ToDouble(_context);
var deviceIndependentHeight = heightMeasureSpec.ToDouble(_context);

var widthMode = MeasureSpec.GetMode(widthMeasureSpec);
var heightMode = MeasureSpec.GetMode(heightMeasureSpec);

var measure = CrossPlatformMeasure(deviceIndependentWidth, deviceIndependentHeight);

// If the measure spec was exact, we should return the explicit size value, even if the content
// measure came out to a different size
var width = widthMode == MeasureSpecMode.Exactly ? deviceIndependentWidth : measure.Width;
var height = heightMode == MeasureSpecMode.Exactly ? deviceIndependentHeight : measure.Height;

var platformWidth = _context.ToPixels(width);
var platformHeight = _context.ToPixels(height);

// Minimum values win over everything
platformWidth = Math.Max(MinimumWidth, platformWidth);
platformHeight = Math.Max(MinimumHeight, platformHeight);

SetMeasuredDimension((int)platformWidth, (int)platformHeight);
}
// protected override void OnMeasure(int widthMeasureSpec, int heightMeasureSpec)
// {
// if (CrossPlatformLayout is null)
// {
// base.OnMeasure(widthMeasureSpec, heightMeasureSpec);
// return;
// }
//
// var deviceIndependentWidth = widthMeasureSpec.ToDouble(_context);
// var deviceIndependentHeight = heightMeasureSpec.ToDouble(_context);
//
// var widthMode = MeasureSpec.GetMode(widthMeasureSpec);
// var heightMode = MeasureSpec.GetMode(heightMeasureSpec);
//
// var measure = CrossPlatformMeasure(deviceIndependentWidth, deviceIndependentHeight);
//
// // If the measure spec was exact, we should return the explicit size value, even if the content
// // measure came out to a different size
// var width = widthMode == MeasureSpecMode.Exactly ? deviceIndependentWidth : measure.Width;
// var height = heightMode == MeasureSpecMode.Exactly ? deviceIndependentHeight : measure.Height;
//
// var platformWidth = _context.ToPixels(width);
// var platformHeight = _context.ToPixels(height);
//
// // Minimum values win over everything
// platformWidth = Math.Max(MinimumWidth, platformWidth);
// platformHeight = Math.Max(MinimumHeight, platformHeight);
//
// SetMeasuredDimension((int)platformWidth, (int)platformHeight);
// }

protected override void OnLayout(bool changed, int left, int top, int right, int bottom)
{
Expand Down Expand Up @@ -143,5 +143,40 @@ internal IBorderStroke? Clip

return null;
}

internal override void DoMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
var deviceIndependentWidth = widthMeasureSpec.ToDouble(_context);
var deviceIndependentHeight = heightMeasureSpec.ToDouble(_context);

var widthMode = MeasureSpec.GetMode(widthMeasureSpec);
var heightMode = MeasureSpec.GetMode(heightMeasureSpec);

var measure = CrossPlatformMeasure(deviceIndependentWidth, deviceIndependentHeight);

// If the measure spec was exact, we should return the explicit size value, even if the content
// measure came out to a different size
var width = widthMode == MeasureSpecMode.Exactly ? deviceIndependentWidth : measure.Width;
var height = heightMode == MeasureSpecMode.Exactly ? deviceIndependentHeight : measure.Height;

var platformWidth = _context.ToPixels(width);
var platformHeight = _context.ToPixels(height);

// Minimum values win over everything
platformWidth = Math.Max(MinimumWidth, platformWidth);
platformHeight = Math.Max(MinimumHeight, platformHeight);

SetMeasuredDimension((int)platformWidth, (int)platformHeight);
}

bool IPlatformQuickLayout.NeedsMeasure(int p0, int p1)
{
return base.NeedsMeasure(p0, p1);
}

void IPlatformQuickLayout.QuickMeasure(int p0, int p1, int p2, int p3)
{
base.QuickMeasure(p0, p1, p2, p3);
}
}
}
10 changes: 8 additions & 2 deletions src/Core/src/Platform/Android/ContextExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -404,14 +404,17 @@ internal static int GetNavigationBarHeight(this Context context)
return _navigationBarHeight ?? 0;
}

internal static int CreateMeasureSpec(this Context context, double constraint, double explicitSize, double minimumSize, double maximumSize)
internal static (int MeasureSpec, double CrossPlatformConstraint, bool Exact) CreateMeasureSpec(this Context context, double constraint, double explicitSize, double minimumSize, double maximumSize)
{
var mode = MeasureSpecMode.AtMost;
var exact = false;
var crossPlatformConstraint = constraint;

if (IsExplicitSet(explicitSize))
{
// We have a set value (i.e., a Width or Height)
mode = MeasureSpecMode.Exactly;
exact = true;

// Since the mode is "Exactly", we have to return the exact final value clamped to the minimum/maximum.
constraint = Math.Max(explicitSize, ResolveMinimum(minimumSize));
Expand All @@ -420,11 +423,14 @@ internal static int CreateMeasureSpec(this Context context, double constraint, d
{
constraint = Math.Min(constraint, maximumSize);
}

crossPlatformConstraint = constraint;
}
else if (IsMaximumSet(maximumSize) && maximumSize < constraint)
{
mode = MeasureSpecMode.AtMost;
constraint = maximumSize;
crossPlatformConstraint = constraint;
}
else if (double.IsInfinity(constraint))
{
Expand All @@ -436,7 +442,7 @@ internal static int CreateMeasureSpec(this Context context, double constraint, d
// Convert to a platform size to create the spec for measuring
var deviceConstraint = (int)context.ToPixels(constraint);

return mode.MakeMeasureSpec(deviceConstraint);
return (mode.MakeMeasureSpec(deviceConstraint), crossPlatformConstraint, exact);
}

public static float GetDisplayDensity(this Context? context)
Expand Down
Loading
Loading