Skip to content
Merged
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
88 changes: 88 additions & 0 deletions src/Controls/tests/TestCases.HostApp/Issues/Issue34211.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
namespace Maui.Controls.Sample.Issues;

[Issue(IssueTracker.Github, 34211, "Android display-size change causes parent and drawable children mismatch in .NET MAUI", PlatformAffected.Android)]
public class Issue34211 : ContentPage
{
readonly Issue34211_Drawable _drawable = new();
GraphicsView _graphicsView;
Label _statusLabel;

public Issue34211()
{
_graphicsView = new GraphicsView
{
AutomationId = "Issue34211_GraphicsView",
BackgroundColor = Color.FromArgb("#F0F0F0"),
Drawable = _drawable,
};

_statusLabel = new Label
{
AutomationId = "Issue34211_StatusLabel",
Text = "Waiting for first draw...",
HorizontalOptions = LayoutOptions.Center,
Margin = new Thickness(0, 0, 0, 10),
};

var checkButton = new Button
{
AutomationId = "Issue34211_CheckButton",
Text = "Check Size Match",
};
checkButton.Clicked += (_, _) => _graphicsView.Invalidate();

Content = new Grid
{
Padding = 20,
RowDefinitions =
{
new RowDefinition { Height = GridLength.Auto },
new RowDefinition { Height = GridLength.Auto },
new RowDefinition { Height = GridLength.Star },
},
Children =
{
_statusLabel,
checkButton,
_graphicsView,
}
};

Grid.SetRow(_statusLabel, 0);
Grid.SetRow(checkButton, 1);
Grid.SetRow(_graphicsView, 2);

_graphicsView.SizeChanged += (_, _) => _graphicsView.Invalidate();

_drawable.OnDrawn = rect =>
{
MainThread.BeginInvokeOnMainThread(() =>
{
double viewW = _graphicsView.Width;
double viewH = _graphicsView.Height;
bool widthMatch = Math.Abs(viewW - rect.Width) <= 1.0;
bool heightMatch = Math.Abs(viewH - rect.Height) <= 1.0;
_statusLabel.Text = widthMatch && heightMatch
? "PASS: sizes match"
: $"FAIL: GraphicsView={viewW:F1}x{viewH:F1} Drawable={rect.Width:F1}x{rect.Height:F1}";
});
};
}
}

public class Issue34211_Drawable : IDrawable
{
public Action<RectF> OnDrawn { get; set; }

public void Draw(ICanvas canvas, RectF dirtyRect)
{
canvas.FillColor = Colors.CornflowerBlue;
canvas.FillRectangle(dirtyRect);

canvas.StrokeColor = Colors.DarkBlue;
canvas.StrokeSize = 3;
canvas.DrawRectangle(dirtyRect);

OnDrawn?.Invoke(dirtyRect);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
#if ANDROID // Android-only: display-density change can only be triggered via adb shell wm density.
using NUnit.Framework;
using UITest.Appium;
using UITest.Core;

namespace Microsoft.Maui.TestCases.Tests.Issues;

public class Issue34211 : _IssuesUITest
{
public Issue34211(TestDevice device) : base(device) { }

public override string Issue => "Android display-size change causes parent and drawable children mismatch in .NET MAUI";

[Test]
[Category(UITestCategories.GraphicsView)]
public void DrawableDirtyRectMatchesGraphicsViewSizeAfterDisplayDensityChange()
{
App.WaitForElement("Issue34211_GraphicsView");

string originalDensity = ShellHelper.ExecuteShellCommandWithOutput("adb shell wm density").Trim();
try
{
App.BackgroundApp();
ShellHelper.ExecuteShellCommand($"adb shell wm density {(originalDensity.Contains("320", StringComparison.Ordinal) ? "280" : "320")}");
App.ForegroundApp();

// adb shell wm density triggers an Activity recreation because density is not listed
// in MAUI's android:configChanges. The app restarts at the list page, so re-navigate.
App.WaitForElement("SearchBar");
App.ClearText("SearchBar");
App.EnterText("SearchBar", Issue);
App.WaitForElement("GoToTestButton");
App.Tap("GoToTestButton");

Comment thread
praveenkumarkarunanithi marked this conversation as resolved.
var el = App.WaitForElement(() =>
{
var e = App.FindElement("Issue34211_StatusLabel");
var text = e?.GetText() ?? string.Empty;
return text.StartsWith("PASS", StringComparison.Ordinal) || text.StartsWith("FAIL", StringComparison.Ordinal) ? e : null;
}, "Timed out waiting for draw after density change");
Assert.That(el.GetText(), Does.StartWith("PASS"), "GraphicsView and drawable sizes diverged after display-density change");
}
finally
{
ShellHelper.ExecuteShellCommand("adb shell wm density reset");
}
Comment thread
praveenkumarkarunanithi marked this conversation as resolved.
}
}
#endif
7 changes: 6 additions & 1 deletion src/Core/src/Platform/Android/PlatformTouchGraphicsView.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,19 @@ public class PlatformTouchGraphicsView : PlatformGraphicsView

public PlatformTouchGraphicsView(Context context) : base(context)
{
_scale = (context ?? global::Android.App.Application.Context)?.Resources?.DisplayMetrics?.Density ?? 1;
}

// Override to use MAUI's cached density (Context.GetDisplayDensity) so that
// dirtyRect, touch coords and _bounds stay consistent with GraphicsView.Width/Height.
internal override float GetDisplayDensity() => Context!.GetDisplayDensity();

protected override void OnLayout(bool changed, int left, int top, int right, int bottom)
{
base.OnLayout(changed, left, top, right, bottom);
if (changed)
{
// Cache density once per layout; reused by touch/hover events until next layout.
_scale = GetDisplayDensity();
var width = right - left;
var height = bottom - top;
_bounds = new RectF(0, 0, width / _scale, height / _scale);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ public class PlatformGraphicsView : View
private readonly PlatformCanvas _canvas;
private readonly ScalingCanvas _scalingCanvas;
private IDrawable _drawable;
private readonly float _scale = 1;
private float _scale;
private Color _backgroundColor;

public PlatformGraphicsView(Context context, IAttributeSet attrs, IDrawable drawable = null) : base(context, attrs)
Expand All @@ -31,6 +31,9 @@ public PlatformGraphicsView(Context context, IDrawable drawable = null) : base(c
Drawable = drawable;
}

// Overridden by friend assemblies to supply the density source used for draw scaling.
internal virtual float GetDisplayDensity() => Resources.DisplayMetrics.Density;
Comment thread
praveenkumarkarunanithi marked this conversation as resolved.

public Color BackgroundColor
{
get => _backgroundColor;
Expand Down Expand Up @@ -78,6 +81,7 @@ public override void Draw(Canvas androidCanvas)
protected override void OnSizeChanged(int width, int height, int oldWidth, int oldHeight)
{
base.OnSizeChanged(width, height, oldWidth, oldHeight);
_scale = GetDisplayDensity();
_width = width;
_height = height;
}
Expand Down
Loading