-
Notifications
You must be signed in to change notification settings - Fork 1.9k
[Android] Span line height fix #20352
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Hey there @kubaflo! Thank you so much for your PR! Someone from the team will get assigned to your PR shortly and we'll get it reviewed. |
/azp run |
Azure Pipelines successfully started running 3 pipeline(s). |
// The line height should be the same for each line | ||
// of the paragraph, 1.5 and 2.5 respectively, | ||
// as opposed to progressively growing | ||
App.Screenshot(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could we use here App.VerifyScreenshot
? This will generate a snapshot to compare it with a reference one.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The test NativeFormattedStringContainsSpan
seems to be failing with the changes
Java.Lang.NullPointerException : Attempt to read from field 'float android.util.DisplayMetrics.scaledDensity' on a null object reference
@jsuarezruiz I have fixed the falling test |
/azp run |
Azure Pipelines successfully started running 3 pipeline(s). |
@@ -64,6 +64,12 @@ public static SpannableString ToSpannableString(this Label label) | |||
|
|||
var builder = new StringBuilder(); | |||
|
|||
var fontMetrics = context?.Resources?.DisplayMetrics != null ? new Paint() | |||
{ | |||
TextSize = TypedValue.ApplyDimension(ComplexUnitType.Sp, (float)defaultFontSize, context.Resources.DisplayMetrics) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This line looks like it would throw if context.Resources
returned null
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
#I'm not sure why it would throw🤔
The only place that fontMetrics
is here
maui/src/Controls/src/Core/Platform/Android/Extensions/FormattedStringExtensions.cs
Line 110 in 24d4049
if (span.LineHeight >= 0 && fontMetrics is not null) |
var fontMetrics = context?.Resources?.DisplayMetrics != null ? new Paint() | ||
{ | ||
TextSize = TypedValue.ApplyDimension(ComplexUnitType.Sp, (float)defaultFontSize, context.Resources.DisplayMetrics) | ||
}.GetFontMetrics() | ||
: null; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could we write this code in Java, it looks like it interops between C# and Java a lot. I've seen some past issue complaining about Android FormattedString performance like:
So we'd make a version of this method that returns "Font metrics" instead of "Window metrics":
maui/src/Core/AndroidNative/maui/src/main/java/com/microsoft/maui/PlatformInterop.java
Lines 581 to 592 in ad28b65
/** | |
* Computes the current WindowMetrics' bounds | |
* @param activity | |
* @return Rect value of the bounds | |
*/ | |
@NonNull | |
public static Rect getCurrentWindowMetrics(Activity activity) { | |
return WindowMetricsCalculator.Companion | |
.getOrCreate() | |
.computeCurrentWindowMetrics(activity) | |
.getBounds(); | |
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, that would be beneficial, but I'm not sure how to write this code in JAVA and how to use it in MAUI
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you add a method in this file, it will magically appear on the PlatformInterop
static class, you can call from C# like:
//signature
public static Rect GetCurrentWindowMetrics(Activity activity);
//usage
var rect = PlatformInterop.GetCurrentWindowMetrics(myActivity);
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you! I pushed a new commit with this functionality
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I made one small change, but it seems like this should be ready if we fix up the screenshot.
778d523
to
12d9c14
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks good to me if the new test & screenshot work. 👍
/rebase |
* call `context.getResources().getDisplayMetrics()` once * build `maui.aar`
f32a663
to
ce1b722
Compare
/azp run MAUI-UITests-public |
Azure Pipelines successfully started running 1 pipeline(s). |
Conflicts: src/Core/src/maui.aar
18fdf8f
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed the merge conflict.
Not sure why UI Tests aren't triggering |
Context: https://github.com/Redth/MauiCollectionViewGallery Fixes: dotnet#14222 This will conflict with: * dotnet#20352 But I will rework this after it is merged. Profiling the above sample while scrolling on a Pixel 5, a lot of time is spent in `FormattedStringExtensions.RecalculateSpanPositions()`: (20%) Microsoft.Maui.Controls!Microsoft.Maui.Controls.Platform.FormattedStringExtensions.RecalculateSpanPositions(Android.Widget.TextView,Microsoft.Maui.Controls.Label,Android.Text.SpannableString,Microsoft.Maui.SizeRequest) (11%) Microsoft.Maui.Controls!Microsoft.Maui.Controls.Platform.FormattedStringExtensions.FontSpan.UpdateDrawState(Android.Text.TextPaint) (11%) Microsoft.Maui.Controls!Microsoft.Maui.Controls.Platform.FormattedStringExtensions.FontSpan.Apply(Android.Text.TextPaint) (6.3%) MauiCollectionViewGallery!PoolMathApp.Helpers.FormattedTextExtensions.ToFormattedString(PoolMathApp.Models.FormattedTex The key contributors are `FontSpan` and `LineHeightSpan` which: * Implement `MetricAffectingSpan`, an abstract class that allows to change the metrics of the text. * Implement `UpdateDrawState()` and `Apply()` methods that are called during draw. Causing frequent Java -> C# interop. To fix this, let's move the following types from C# to Java: * `FontSpan` -> `PlatformFontSpan` * `LetterSpacingSpan` -> `PlatformFontSpan` (use different ctor) * `LineHeightSpan` -> `PlatformLineHeightSpan` `PlatformLineHeightSpan` is similar, as it is an implementation of the `LineHeightSpan` interface. With these changes, I see a nice improvement while scrolling: (5.5%) Microsoft.Maui.Controls!Microsoft.Maui.Controls.Platform.FormattedStringExtensions.RecalculateSpanPositions(Android.Widget.TextView,Microsoft.Maui.Controls.Label,Android.Text.SpannableString,Microsoft.Maui.SizeRequest) (4.0%) MauiCollectionViewGallery!PoolMathApp.Helpers.FormattedTextExtensions.ToFormattedString(PoolMathApp.Models.FormattedText `RecalculateSpanPositions` overall, went from 20% to 5.5%! Comparing the new types, the times spent calling the constructors are also improved: Before: (1.1%) Microsoft.Maui.Controls!Microsoft.Maui.Controls.Platform.FormattedStringExtensions.FontSpan..ctor(Microsoft.Maui.Font,M (1.5%) Microsoft.Maui.Controls!Microsoft.Maui.Controls.Platform.FormattedStringExtensions.LetterSpacingSpan..ctor(double) After: (1.0%) Microsoft.Maui!Microsoft.Maui.PlatformFontSpan..ctor(Android.Content.Context,Android.Graphics.Typeface,bool,single) (0.82%) Microsoft.Maui!Microsoft.Maui.PlatformFontSpan..ctor(single) This should be reasonable for .NET 8servicing, as there should be no behavior changes and no API changes. In a future PR, it looks like `FormattedStringExtensions` could be improved further, but this is a decent starting point that makes it a lot better.
/azp run |
Azure Pipelines successfully started running 3 pipeline(s). |
Context: https://github.com/Redth/MauiCollectionViewGallery Fixes: dotnet#14222 This will conflict with: * dotnet#20352 But I will rework this after it is merged. Profiling the above sample while scrolling on a Pixel 5, a lot of time is spent in `FormattedStringExtensions.RecalculateSpanPositions()`: (20%) Microsoft.Maui.Controls!Microsoft.Maui.Controls.Platform.FormattedStringExtensions.RecalculateSpanPositions(Android.Widget.TextView,Microsoft.Maui.Controls.Label,Android.Text.SpannableString,Microsoft.Maui.SizeRequest) (11%) Microsoft.Maui.Controls!Microsoft.Maui.Controls.Platform.FormattedStringExtensions.FontSpan.UpdateDrawState(Android.Text.TextPaint) (11%) Microsoft.Maui.Controls!Microsoft.Maui.Controls.Platform.FormattedStringExtensions.FontSpan.Apply(Android.Text.TextPaint) (6.3%) MauiCollectionViewGallery!PoolMathApp.Helpers.FormattedTextExtensions.ToFormattedString(PoolMathApp.Models.FormattedTex The key contributors are `FontSpan` and `LineHeightSpan` which: * Implement `MetricAffectingSpan`, an abstract class that allows to change the metrics of the text. * Implement `UpdateDrawState()` and `Apply()` methods that are called during draw. Causing frequent Java -> C# interop. To fix this, let's move the following types from C# to Java: * `FontSpan` -> `PlatformFontSpan` * `LetterSpacingSpan` -> `PlatformFontSpan` (use different ctor) * `LineHeightSpan` -> `PlatformLineHeightSpan` `PlatformLineHeightSpan` is similar, as it is an implementation of the `LineHeightSpan` interface. With these changes, I see a nice improvement while scrolling: (5.5%) Microsoft.Maui.Controls!Microsoft.Maui.Controls.Platform.FormattedStringExtensions.RecalculateSpanPositions(Android.Widget.TextView,Microsoft.Maui.Controls.Label,Android.Text.SpannableString,Microsoft.Maui.SizeRequest) (4.0%) MauiCollectionViewGallery!PoolMathApp.Helpers.FormattedTextExtensions.ToFormattedString(PoolMathApp.Models.FormattedText `RecalculateSpanPositions` overall, went from 20% to 5.5%! Comparing the new types, the times spent calling the constructors are also improved: Before: (1.1%) Microsoft.Maui.Controls!Microsoft.Maui.Controls.Platform.FormattedStringExtensions.FontSpan..ctor(Microsoft.Maui.Font,M (1.5%) Microsoft.Maui.Controls!Microsoft.Maui.Controls.Platform.FormattedStringExtensions.LetterSpacingSpan..ctor(double) After: (1.0%) Microsoft.Maui!Microsoft.Maui.PlatformFontSpan..ctor(Android.Content.Context,Android.Graphics.Typeface,bool,single) (0.82%) Microsoft.Maui!Microsoft.Maui.PlatformFontSpan..ctor(single) This should be reasonable for .NET 8servicing, as there should be no behavior changes and no API changes. In a future PR, it looks like `FormattedStringExtensions` could be improved further, but this is a decent starting point that makes it a lot better.
Context: https://github.com/Redth/MauiCollectionViewGallery Fixes: dotnet#14222 This will conflict with: * dotnet#20352 But I will rework this after it is merged. Profiling the above sample while scrolling on a Pixel 5, a lot of time is spent in `FormattedStringExtensions.RecalculateSpanPositions()`: (20%) Microsoft.Maui.Controls!Microsoft.Maui.Controls.Platform.FormattedStringExtensions.RecalculateSpanPositions(Android.Widget.TextView,Microsoft.Maui.Controls.Label,Android.Text.SpannableString,Microsoft.Maui.SizeRequest) (11%) Microsoft.Maui.Controls!Microsoft.Maui.Controls.Platform.FormattedStringExtensions.FontSpan.UpdateDrawState(Android.Text.TextPaint) (11%) Microsoft.Maui.Controls!Microsoft.Maui.Controls.Platform.FormattedStringExtensions.FontSpan.Apply(Android.Text.TextPaint) (6.3%) MauiCollectionViewGallery!PoolMathApp.Helpers.FormattedTextExtensions.ToFormattedString(PoolMathApp.Models.FormattedTex The key contributors are `FontSpan` and `LineHeightSpan` which: * Implement `MetricAffectingSpan`, an abstract class that allows to change the metrics of the text. * Implement `UpdateDrawState()` and `Apply()` methods that are called during draw. Causing frequent Java -> C# interop. To fix this, let's move the following types from C# to Java: * `FontSpan` -> `PlatformFontSpan` * `LetterSpacingSpan` -> `PlatformFontSpan` (use different ctor) * `LineHeightSpan` -> `PlatformLineHeightSpan` `PlatformLineHeightSpan` is similar, as it is an implementation of the `LineHeightSpan` interface. With these changes, I see a nice improvement while scrolling: (5.5%) Microsoft.Maui.Controls!Microsoft.Maui.Controls.Platform.FormattedStringExtensions.RecalculateSpanPositions(Android.Widget.TextView,Microsoft.Maui.Controls.Label,Android.Text.SpannableString,Microsoft.Maui.SizeRequest) (4.0%) MauiCollectionViewGallery!PoolMathApp.Helpers.FormattedTextExtensions.ToFormattedString(PoolMathApp.Models.FormattedText `RecalculateSpanPositions` overall, went from 20% to 5.5%! Comparing the new types, the times spent calling the constructors are also improved: Before: (1.1%) Microsoft.Maui.Controls!Microsoft.Maui.Controls.Platform.FormattedStringExtensions.FontSpan..ctor(Microsoft.Maui.Font,M (1.5%) Microsoft.Maui.Controls!Microsoft.Maui.Controls.Platform.FormattedStringExtensions.LetterSpacingSpan..ctor(double) After: (1.0%) Microsoft.Maui!Microsoft.Maui.PlatformFontSpan..ctor(Android.Content.Context,Android.Graphics.Typeface,bool,single) (0.82%) Microsoft.Maui!Microsoft.Maui.PlatformFontSpan..ctor(single) This should be reasonable for .NET 8servicing, as there should be no behavior changes and no API changes. In a future PR, it looks like `FormattedStringExtensions` could be improved further, but this is a decent starting point that makes it a lot better.
Context: https://github.com/Redth/MauiCollectionViewGallery Fixes: #14222 This will conflict with: * #20352 But I will rework this after it is merged. Profiling the above sample while scrolling on a Pixel 5, a lot of time is spent in `FormattedStringExtensions.RecalculateSpanPositions()`: (20%) Microsoft.Maui.Controls!Microsoft.Maui.Controls.Platform.FormattedStringExtensions.RecalculateSpanPositions(Android.Widget.TextView,Microsoft.Maui.Controls.Label,Android.Text.SpannableString,Microsoft.Maui.SizeRequest) (11%) Microsoft.Maui.Controls!Microsoft.Maui.Controls.Platform.FormattedStringExtensions.FontSpan.UpdateDrawState(Android.Text.TextPaint) (11%) Microsoft.Maui.Controls!Microsoft.Maui.Controls.Platform.FormattedStringExtensions.FontSpan.Apply(Android.Text.TextPaint) (6.3%) MauiCollectionViewGallery!PoolMathApp.Helpers.FormattedTextExtensions.ToFormattedString(PoolMathApp.Models.FormattedTex The key contributors are `FontSpan` and `LineHeightSpan` which: * Implement `MetricAffectingSpan`, an abstract class that allows to change the metrics of the text. * Implement `UpdateDrawState()` and `Apply()` methods that are called during draw. Causing frequent Java -> C# interop. To fix this, let's move the following types from C# to Java: * `FontSpan` -> `PlatformFontSpan` * `LetterSpacingSpan` -> `PlatformFontSpan` (use different ctor) * `LineHeightSpan` -> `PlatformLineHeightSpan` `PlatformLineHeightSpan` is similar, as it is an implementation of the `LineHeightSpan` interface. With these changes, I see a nice improvement while scrolling: (5.5%) Microsoft.Maui.Controls!Microsoft.Maui.Controls.Platform.FormattedStringExtensions.RecalculateSpanPositions(Android.Widget.TextView,Microsoft.Maui.Controls.Label,Android.Text.SpannableString,Microsoft.Maui.SizeRequest) (4.0%) MauiCollectionViewGallery!PoolMathApp.Helpers.FormattedTextExtensions.ToFormattedString(PoolMathApp.Models.FormattedText `RecalculateSpanPositions` overall, went from 20% to 5.5%! Comparing the new types, the times spent calling the constructors are also improved: Before: (1.1%) Microsoft.Maui.Controls!Microsoft.Maui.Controls.Platform.FormattedStringExtensions.FontSpan..ctor(Microsoft.Maui.Font,M (1.5%) Microsoft.Maui.Controls!Microsoft.Maui.Controls.Platform.FormattedStringExtensions.LetterSpacingSpan..ctor(double) After: (1.0%) Microsoft.Maui!Microsoft.Maui.PlatformFontSpan..ctor(Android.Content.Context,Android.Graphics.Typeface,bool,single) (0.82%) Microsoft.Maui!Microsoft.Maui.PlatformFontSpan..ctor(single) This should be reasonable for .NET 8servicing, as there should be no behavior changes and no API changes. In a future PR, it looks like `FormattedStringExtensions` could be improved further, but this is a decent starting point that makes it a lot better.
Description of Change
The
fm.Top
value was constantly increasing so that the height of each line grew progressively. Instead, the line height should be adjusted based on the initial top value of font metricsIssues Fixed
Fixes #19592