dotnet / maui

.NET MAUI is the .NET Multi-platform App UI, a framework for building native device applications spanning mobile, tablet, and desktop.
https://dot.net/maui
MIT License
22.21k stars 1.75k forks source link

Label.Measure() in Android returns very different results from native Android.Text.StaticLayout() measurement method, causing abnormal Label clipping and wrapping behavior #18328

Open jonmdev opened 1 year ago

jonmdev commented 1 year ago

Description

Labels in Android do not layout correctly currently causing abnormal wrapping and clipping of letters/words: https://github.com/dotnet/maui/issues/17884

This is likely because using Label.Measure() to get the size of a given Label in Maui returns very different results from the native Android.Text.StaticLayout() method. If Maui and Android are discordant in what size Labels should be, this will cause the problematic behavior.

ANDROID GIVES WRONG RESULTS

A Label's expected size may be found in Android by using a StaticLayout object, as Maui does in PlatformStringSizeService. There are two methods to do this allowed by Android and both return the same results, which differ considerably from Label.Measure() with the same data.

My test project lays out a Label and then gets its size based on the label.Measure() (ie. real world size) and also based on the attempted prediction of its size.

The relevant output code is:

                Size labelMeasureSize = label.Measure(1000, 1000, MeasureFlags.None); //constraints are set in Label.MaxWidth already
                Size nativeMeasuredSize = NativeLabelMeasurer.Instance.getMeasurement(labelParams);
                Debug.WriteLine("label.Measure(): " + labelMeasureSize + " || NativeMeasurement: " + nativeMeasuredSize);

Expected behavior is that Label.Measure() should return the same as the NativeLabelMeasurer output.

In Windows this returns perfectly:

label.Measure(): { Width = 30 Height = 581} || NativeMeasurement: { Width = 30 Height = 581} //measuring perfectly`

In iOS this returns close but not exactly:

label.Measure(): { Width = 29.333333333333332 Height = 580.3333333333334} || NativeMeasurement: { Width = 29.232 Height = 580.2439999999997} //close enough?

In Android this returns completely wrong:

label.Measure(): { Width=30.181818181818183 Height=583.2727272727273} || NativeMeasurement: { Width=30 Height=612} // very wrong

IMPLICATIONS

I wonder if this is why we are seeing these wrapping/clipping glitches in Android: https://github.com/dotnet/maui/issues/17884

If Maui is somehow not allocating the correct amount of space for the Android objects or there is some miscommunication on their sizes, things may clip or wrap abnormally as shown in that other bug report.

SOLUTION FOR WINDOWS:

I also see this thread here where someone was requesting a method for Windows in PlatformStringSizeService: https://github.com/dotnet/maui/issues/12476

My method can be used for this purpose as it returns exactly correct results every time.

MINOR IOS ISSUES

Here is the iOS version in Maui:

https://github.com/dotnet/maui/blob/783db2459c6e3ec6437f9939a0ea2681fd634a3d/src/Graphics/src/Graphics/Platforms/iOS/PlatformStringSizeService.cs

There are two problems with it FYI:

I just mention these iOS issues in passing if it is helpful.

Steps to Reproduce

1) Open bug project and play in Windows/iOS/Android. 2) See the measured sizes in Windows are perfect, they are close to perfect in iOS, and they are very wrong in Android. 3) Try to comment in my new suggested Android method of getting the StaticLayout object and see the enum error. 4) See this perhaps related bug report: https://github.com/dotnet/maui/issues/17884

All code can be seen here:

https://github.com/jonmdev/Label-Measurement-Bug/blob/main/Label%20Measurement%20Bug/App.xaml.cs

Link to public reproduction project repository

https://github.com/jonmdev/Label-Measurement-Bug

Version with bug

7.0.96

Is this a regression from previous behavior?

No, this is something new

Affected platforms

iOS, Android

Did you find any workaround?

None. Needs fixing likely to get reliable Layout/Label behaviors. Not sure why it is happening.

ghost commented 1 year ago

We've added this issue to our backlog, and we will work to address it as time and resources allow. If you have any additional information or questions about this issue, please leave a comment. For additional info about issue management, please read our Triage Process.

jonmdev commented 7 months ago

I have done some more work on this bug. I was now able to get both the old (API 21+ I believe) and new (API 33+) Android native label measurement methods working in the bug project, but again, both return completely wrong results compared to Label.Measure().

I updated my bug project code accordingly and some notes as well. All code is contained here:

https://github.com/jonmdev/Label-Measurement-Bug/blob/main/Label%20Measurement%20Bug/App.xaml.cs