Skip to content

Commit 8359359

Browse files
kubaflojsuarezruizjonathanpeppers
authored
[Android] Span line height fix (#20352)
* [Android] Fixed Wrong Span LineHeight (#19592) * Added a UI Test (#19592) * Added a null checking (#19592) * Updated the UiTest (#19592) * Added snapshot * Added Java native code * Improve Java code * call `context.getResources().getDisplayMetrics()` once * build `maui.aar` --------- Co-authored-by: Javier Suárez <[email protected]> Co-authored-by: Jonathan Peppers <[email protected]>
1 parent 8a3a6fe commit 8359359

File tree

7 files changed

+106
-4
lines changed

7 files changed

+106
-4
lines changed
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<?xml version="1.0" encoding="utf-8" ?>
2+
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
3+
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
4+
x:Class="Maui.Controls.Sample.Issues.Issue19592"
5+
xmlns:ns="clr-namespace:Maui.Controls.Sample.Issues">
6+
<StackLayout>
7+
<Label FontAttributes="Bold"
8+
Text="Line height: 1.25"/>
9+
<Label AutomationId="label"
10+
LineBreakMode="WordWrap">
11+
<Label.FormattedText>
12+
<FormattedString>
13+
<Span LineHeight="1.5">Outside, the church's garden was a haven of tranquility, where nature and spirituality intertwined harmoniously. Flowers bloomed in vibrant hues, each petal seemingly capturing a prayer or hope of the visitors who walked among them. The garden, lovingly tended by members of the congregation, reflected the church's commitment to nurturing not just the soul, but also the beauty of the world around it. It was a place where people of all walks of life could find solace, inspiration, and a sense of belonging within the embrace of the church's community.</Span>
14+
</FormattedString>
15+
</Label.FormattedText>
16+
</Label>
17+
18+
<Label FontAttributes="Bold"
19+
Margin="0,20,0,0"
20+
Text="Line height: 2.5"/>
21+
<Label LineBreakMode="WordWrap">
22+
<Label.FormattedText>
23+
<FormattedString>
24+
<Span LineHeight="2.5">Outside, the church's garden was a haven of tranquility, where nature and spirituality intertwined harmoniously. Flowers bloomed in vibrant hues, each petal seemingly capturing a prayer or hope of the visitors who walked among them. The garden, lovingly tended by members of the congregation, reflected the church's commitment to nurturing not just the soul, but also the beauty of the world around it. It was a place where people of all walks of life could find solace, inspiration, and a sense of belonging within the embrace of the church's community.</Span>
25+
</FormattedString>
26+
</Label.FormattedText>
27+
</Label>
28+
</StackLayout>
29+
</ContentPage>
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
using Microsoft.Maui.Controls;
2+
using Microsoft.Maui.Controls.Xaml;
3+
4+
namespace Maui.Controls.Sample.Issues
5+
{
6+
[XamlCompilation(XamlCompilationOptions.Compile)]
7+
[Issue(IssueTracker.Github, 19592, "Span LineHeight Wrong on Android", PlatformAffected.Android)]
8+
public partial class Issue19592 : ContentPage
9+
{
10+
public Issue19592()
11+
{
12+
InitializeComponent();
13+
}
14+
}
15+
}

src/Controls/src/Core/Platform/Android/Extensions/FormattedStringExtensions.cs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,8 @@ internal static SpannableString ToSpannableStringNewWay(
6464

6565
var builder = new StringBuilder();
6666

67+
var fontMetrics = PlatformInterop.GetFontMetrics(context, defaultFontSize);
68+
6769
for (int i = 0; i < formattedString.Spans.Count; i++)
6870
{
6971
Span span = formattedString.Spans[i];
@@ -101,8 +103,8 @@ internal static SpannableString ToSpannableStringNewWay(
101103
spannable.SetSpan(new BackgroundColorSpan(span.BackgroundColor.ToPlatform()), start, end, SpanTypes.InclusiveExclusive);
102104

103105
// LineHeight
104-
if (span.LineHeight >= 0)
105-
spannable.SetSpan(new LineHeightSpan(span.LineHeight), start, end, SpanTypes.InclusiveExclusive);
106+
if (span.LineHeight >= 0 && fontMetrics is not null)
107+
spannable.SetSpan(new LineHeightSpan(span.LineHeight, fontMetrics.Top), start, end, SpanTypes.InclusiveExclusive);
106108

107109
// CharacterSpacing
108110
var characterSpacing = span.CharacterSpacing >= 0
@@ -289,18 +291,20 @@ void Apply(TextPaint paint)
289291
class LineHeightSpan : Java.Lang.Object, ILineHeightSpan
290292
{
291293
readonly double _relativeLineHeight;
294+
readonly double _originalTop;
292295

293-
public LineHeightSpan(double relativeLineHeight)
296+
public LineHeightSpan(double relativeLineHeight, double originalTop)
294297
{
295298
_relativeLineHeight = relativeLineHeight;
299+
_originalTop = originalTop;
296300
}
297301

298302
public void ChooseHeight(Java.Lang.ICharSequence? text, int start, int end, int spanstartv, int lineHeight, Paint.FontMetricsInt? fm)
299303
{
300304
if (fm is null)
301305
return;
302306

303-
fm.Ascent = (int)(fm.Top * _relativeLineHeight);
307+
fm.Ascent = (int)(_originalTop * _relativeLineHeight);
304308
}
305309
}
306310
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
using NUnit.Framework;
2+
using UITest.Appium;
3+
using UITest.Core;
4+
5+
namespace Microsoft.Maui.AppiumTests.Issues
6+
{
7+
public class Issue19592 : _IssuesUITest
8+
{
9+
public override string Issue => "Span LineHeight Wrong on Android";
10+
11+
public Issue19592(TestDevice device) : base(device)
12+
{
13+
}
14+
15+
[Test]
16+
public void SpanLineHeightShouldNotGrowProgressively()
17+
{
18+
this.IgnoreIfPlatforms(new TestDevice[] { TestDevice.iOS, TestDevice.Mac, TestDevice.Windows });
19+
20+
_ = App.WaitForElement("label");
21+
22+
// The line height should be the same for each line
23+
// of the paragraph, 1.5 and 2.5 respectively,
24+
// as opposed to progressively growing
25+
VerifyScreenshot();
26+
}
27+
}
28+
}
80.3 KB
Loading

src/Core/AndroidNative/maui/src/main/java/com/microsoft/maui/PlatformInterop.java

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import android.os.Build;
2020
import android.text.Editable;
2121
import android.text.InputFilter;
22+
import android.util.DisplayMetrics;
2223
import android.util.TypedValue;
2324
import android.view.Gravity;
2425
import android.view.View;
@@ -591,6 +592,31 @@ public static Rect getCurrentWindowMetrics(Activity activity) {
591592
.getBounds();
592593
}
593594

595+
/**
596+
* Gets font metrics based on the given context and default font size
597+
* @param context
598+
* @param defaultFontSize
599+
* @return FontMetrics object or null if context or display metrics is null
600+
*/
601+
public static Paint.FontMetrics getFontMetrics(Context context, double defaultFontSize) {
602+
if (context == null)
603+
return null;
604+
605+
DisplayMetrics metrics = context.getResources().getDisplayMetrics();
606+
if (metrics != null) {
607+
return new Paint() {{
608+
setTextSize(
609+
TypedValue.applyDimension(
610+
TypedValue.COMPLEX_UNIT_SP,
611+
(float) defaultFontSize,
612+
metrics
613+
));
614+
}}.getFontMetrics();
615+
} else {
616+
return null;
617+
}
618+
}
619+
594620
private static class ColorStates
595621
{
596622
static final int[] EMPTY = new int[] { };

src/Core/src/maui.aar

817 Bytes
Binary file not shown.

0 commit comments

Comments
 (0)