-
Notifications
You must be signed in to change notification settings - Fork 1.9k
[Android] Fixed the Label Width related issues #29620
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
Changes from all commits
99ddaad
9c16661
5910a19
b08d565
68ab77a
c988924
4d89377
3c44f2e
ba78473
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,124 @@ | ||
| namespace Maui.Controls.Sample.Issues; | ||
|
|
||
| [Issue(IssueTracker.Github, 29542, "I1_Vertical_list_for_Multiple_Rows - Rotating the emulator would cause clipping on the description text.", PlatformAffected.Android)] | ||
| public class Issue29542 : ContentPage | ||
| { | ||
| public Issue29542() | ||
| { | ||
| var grid = new Grid | ||
| { | ||
| Margin = 20, | ||
| RowDefinitions = | ||
| { | ||
| new RowDefinition { Height = GridLength.Auto }, | ||
| new RowDefinition { Height = GridLength.Auto }, | ||
| new RowDefinition { Height = GridLength.Star }, | ||
|
|
||
| } | ||
| }; | ||
|
|
||
| // Top text description | ||
| var headerLabel = new Label | ||
| { | ||
| Text = "1. The test passes if resizing or rotating without clipping or elements disappearing in multiple rows.", | ||
| }; | ||
|
|
||
| var button = new Button | ||
| { | ||
| Text = "Scroll Down", | ||
| AutomationId = "ScrollToDownButton", | ||
| BackgroundColor = Colors.Red, | ||
| HorizontalOptions = LayoutOptions.Center, | ||
| VerticalOptions = LayoutOptions.Center, | ||
| }; | ||
|
|
||
| var verticalHeaderLayout = new VerticalStackLayout() | ||
| { | ||
| Children = | ||
| { | ||
| headerLabel | ||
| }, | ||
| }; | ||
| Grid.SetRow(verticalHeaderLayout, 0); | ||
| Grid.SetRow(button, 1); | ||
| var items = new List<string>(); | ||
| for (int i = 1; i <= 21; i++) | ||
| { | ||
| items.Add(i.ToString()); | ||
| } | ||
| var myCollection = new CollectionView | ||
| { | ||
| AutomationId = "TestCollectionView", | ||
| ItemsSource = items, | ||
| ItemsLayout = new GridItemsLayout(1, ItemsLayoutOrientation.Vertical) | ||
| { | ||
| VerticalItemSpacing = 5 | ||
| }, | ||
| Header = new Label | ||
| { | ||
| Text = "Header", | ||
| FontSize = 32, | ||
| TextColor = Color.FromArgb("#3B3A39") | ||
| }, | ||
| ItemTemplate = new DataTemplate(CreateItemTemplate) | ||
| }; | ||
|
|
||
| Grid.SetRow(myCollection, 2); | ||
| button.Clicked += (s, e) => | ||
| { | ||
|
|
||
| myCollection.ScrollTo(15, position: ScrollToPosition.End, animate: true); | ||
| }; | ||
|
|
||
| // Add to grid | ||
| grid.Children.Add(verticalHeaderLayout); | ||
| grid.Children.Add(button); | ||
|
|
||
| grid.Children.Add(myCollection); | ||
| Content = grid; | ||
| } | ||
|
|
||
| private View CreateItemTemplate() | ||
| { | ||
| var label = new Label | ||
| { | ||
| MaxLines = 3, | ||
| LineBreakMode = LineBreakMode.TailTruncation, | ||
| Text = @"DescriptionDescriptionDescriptionDescriptionDescriptionDescriptionDescriptionDescriptionDescriptionDescriptionDescriptionDescription DescriptionDescriptionDescriptionDescriptionDescriptionDescriptionDescriptionDescription | ||
| DescriptionDescriptionDescriptionDescriptionDescriptionDescription DescriptionDescriptionDescriptionDescriptionDescriptionDescriptionDescriptionDescriptionDescriptionDescriptionDescriptionDescriptionDescriptionDescriptionDescription | ||
| DescriptionDescriptionDescription | ||
| DescriptionDescriptionDescription", | ||
| BackgroundColor = Colors.Green, | ||
| HorizontalOptions = LayoutOptions.Start | ||
| }; | ||
|
|
||
| var image = new Image | ||
| { | ||
| Aspect = Aspect.AspectFit, | ||
| BackgroundColor = Colors.Transparent, | ||
| VerticalOptions = LayoutOptions.Center, | ||
| HorizontalOptions = LayoutOptions.Center, | ||
| HeightRequest = 24, | ||
| Source = "dotnet_bot.png", | ||
| Margin = new Thickness(0, 0, 20, 0) | ||
| }; | ||
|
|
||
| var layout = new Grid | ||
| { | ||
| ColumnDefinitions = | ||
| { | ||
| new ColumnDefinition { Width = GridLength.Star }, | ||
| new ColumnDefinition { Width = GridLength.Auto } | ||
| }, | ||
| BackgroundColor = Colors.AliceBlue, | ||
| Padding = 5 | ||
| }; | ||
|
|
||
| layout.Children.Add(label); | ||
| layout.Children.Add(image); | ||
| Grid.SetColumn(label, 0); | ||
| Grid.SetColumn(image, 1); | ||
|
|
||
| return layout; | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,144 @@ | ||
| using Microsoft.Maui.Controls.Shapes; | ||
|
|
||
| namespace Maui.Controls.Sample.Issues; | ||
|
|
||
| [Issue(IssueTracker.Github, 29727, "I1 - Vertical list for Item Height- After rotating the Android emulator, some text boxes have extra blank space", PlatformAffected.Android)] | ||
| public class Issue29727 : ContentPage | ||
| { | ||
| public List<string> Items { get; set; } | ||
| public Issue29727() | ||
| { | ||
| Title = "Consistent Height Test"; | ||
|
|
||
| Items = new List<string> | ||
| { | ||
| "If you're visiting this page, you're likely here because you're searching for a random sentence.", | ||
| "Sometimes a random word just isn't enough, and that is where the random sentence generator comes into play. By inputting the desired number, you can make a list of as many random sentences as you want or need. Producing random sentences can be helpful in a number of different ways.", | ||
| "For writers, a random sentence can help them get their creative juices flowing. Since the topic of the sentence is completely unknown, it forces the writer to be creative when the sentence appears. There are a number of different ways a writer can use the random sentence for creativity. The most common way to use the sentence is to begin a story. Another option is to include it somewhere in the story. A much more difficult challenge is to use it to end a story. In any of these cases, it forces the writer to think creatively since they have no idea what sentence will appear from the tool.", | ||
| "For those writers who have writers' block, this can be an excellent way to take a step to crumbling those walls.", | ||
| "It can also be successfully used as a daily exercise to get writers to begin writing. Being shown a random sentence and using it to complete a paragraph each day can be an excellent way to begin any writing session.", | ||
| "By taking the writer away from the subject matter that is causing the block, a random sentence may allow them to see the project they're working on in a different light and perspective. Sometimes all it takes is to get that first sentence down to help break the block.", | ||
| "It can also be a fun way to surprise others. You might choose to share a random sentence on social media just to see what type of reaction it garners from others. It's an unexpected move that might create more conversation than a typical post or tweet.", | ||
| "Have several random sentences generated and you'll soon be able to see if they can help with your project." | ||
| }; | ||
| BindingContext = this; | ||
|
|
||
| var mainGrid = new Grid | ||
| { | ||
| Margin = new Thickness(20), | ||
| RowDefinitions = | ||
| { | ||
| new RowDefinition { Height = GridLength.Auto }, | ||
| new RowDefinition { Height = GridLength.Auto }, | ||
| new RowDefinition { Height = GridLength.Star } | ||
| } | ||
| }; | ||
|
|
||
| var instructionLabel = new Label | ||
| { | ||
| Text = "1. The test pass if the item heights are consistent when scrolling.", | ||
| }; | ||
|
|
||
| var headerLayout = new StackLayout(); | ||
| headerLayout.Children.Add(instructionLabel); | ||
| mainGrid.Children.Add(headerLayout); | ||
| Grid.SetRow(headerLayout, 0); | ||
|
|
||
| var collectionView = new CollectionView | ||
| { | ||
| ItemsSource = Items, | ||
| ItemsLayout = new GridItemsLayout(ItemsLayoutOrientation.Vertical), | ||
| AutomationId = "TestCollectionView", | ||
| ItemTemplate = new DataTemplate(() => | ||
| { | ||
| var outerStack = new VerticalStackLayout { Padding = 20 }; | ||
|
|
||
| var border = new Border | ||
| { | ||
| StrokeShape = new RoundRectangle { CornerRadius = 15 }, | ||
| BackgroundColor = Colors.Red, | ||
| Padding = 10 | ||
| }; | ||
|
|
||
| var grid = new Grid | ||
| { | ||
| ColumnDefinitions = | ||
| { | ||
| new ColumnDefinition { Width = 40 }, | ||
| new ColumnDefinition { Width = GridLength.Star } | ||
| }, | ||
| ColumnSpacing = 10 | ||
| }; | ||
|
|
||
| var imageStack = new VerticalStackLayout | ||
| { | ||
| VerticalOptions = LayoutOptions.Start, | ||
| Spacing = 5 | ||
| }; | ||
|
|
||
| var image = new Image | ||
| { | ||
| Source = "dotnet_bot.png", | ||
| WidthRequest = 40, | ||
| HeightRequest = 40, | ||
| VerticalOptions = LayoutOptions.Start, | ||
| HorizontalOptions = LayoutOptions.Center | ||
| }; | ||
|
|
||
| imageStack.Children.Add(image); | ||
| grid.Children.Add(imageStack); | ||
| Grid.SetColumn(imageStack, 0); | ||
|
|
||
| var textStack = new VerticalStackLayout(); | ||
|
|
||
| var labelGrid = new Grid { Margin = new Thickness(0, 0, 0, 10) }; | ||
| labelGrid.Children.Add(new Label | ||
| { | ||
| Text = "Username", | ||
| HorizontalOptions = LayoutOptions.Start | ||
| }); | ||
| labelGrid.Children.Add(new Label | ||
| { | ||
| Text = "Today", | ||
| VerticalOptions = LayoutOptions.Center, | ||
| HorizontalOptions = LayoutOptions.End | ||
| }); | ||
|
|
||
| var contentLabel = new Label | ||
| { | ||
| Margin = new Thickness(0, 0, 0, 10) | ||
| }; | ||
| contentLabel.SetBinding(Label.TextProperty, "."); | ||
|
|
||
| textStack.Children.Add(labelGrid); | ||
| textStack.Children.Add(contentLabel); | ||
|
|
||
| grid.Children.Add(textStack); | ||
| grid.SetColumn(textStack, 1); | ||
| border.Content = grid; | ||
| outerStack.Children.Add(border); | ||
|
|
||
| return outerStack; | ||
| }) | ||
| }; | ||
|
|
||
| var button = new Button | ||
| { | ||
| AutomationId = "ScrollToDownButton", | ||
| Text = "Scroll to Item 4", | ||
| HorizontalOptions = LayoutOptions.Center, | ||
| Margin = new Thickness(0, 10, 0, 10) | ||
| }; | ||
|
|
||
| button.Clicked += (s, e) => | ||
| { | ||
| collectionView.ScrollTo(5, position: ScrollToPosition.End, animate: true); | ||
| }; | ||
| mainGrid.Children.Add(button); | ||
| Grid.SetRow(button, 1); | ||
| mainGrid.Children.Add(collectionView); | ||
| Grid.SetRow(collectionView, 2); | ||
|
|
||
| Content = mainGrid; | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| #if TEST_FAILS_ON_CATALYST && TEST_FAILS_ON_WINDOWS //The test fails on Windows and MacCatalyst because the SetOrientation method, which is intended to change the device orientation, is only supported on mobile platforms iOS and Android. | ||
| using NUnit.Framework; | ||
| using UITest.Appium; | ||
| using UITest.Core; | ||
|
|
||
| namespace Microsoft.Maui.TestCases.Tests.Issues; | ||
|
|
||
| public class Issue29542 : _IssuesUITest | ||
| { | ||
| public Issue29542(TestDevice device) : base(device) { } | ||
|
|
||
| public override string Issue => "I1_Vertical_list_for_Multiple_Rows - Rotating the emulator would cause clipping on the description text."; | ||
|
|
||
| [Test] | ||
| [Category(UITestCategories.Label)] | ||
| public void LabelShouldSizeProperlyOnCollectionView() | ||
| { | ||
| App.WaitForElement("TestCollectionView"); | ||
| App.Tap("ScrollToDownButton"); | ||
| App.SetOrientationLandscape(); | ||
| App.WaitForElement("TestCollectionView"); | ||
| VerifyScreenshot(); | ||
| } | ||
|
|
||
| [TearDown] | ||
| public void TearDown() | ||
| { | ||
| App.SetOrientationPortrait(); | ||
| } | ||
| } | ||
| #endif | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| #if TEST_FAILS_ON_CATALYST && TEST_FAILS_ON_WINDOWS //The test fails on Windows and MacCatalyst because the SetOrientation method, which is intended to change the device orientation, is only supported on mobile platforms iOS and Android. | ||
| using NUnit.Framework; | ||
| using UITest.Appium; | ||
| using UITest.Core; | ||
|
|
||
| namespace Microsoft.Maui.TestCases.Tests.Issues; | ||
|
|
||
| public class Issue29727 : _IssuesUITest | ||
| { | ||
| public Issue29727(TestDevice device) : base(device) { } | ||
|
|
||
| public override string Issue => "I1 - Vertical list for Item Height- After rotating the Android emulator, some text boxes have extra blank space"; | ||
|
|
||
| [Test] | ||
| [Category(UITestCategories.Label)] | ||
| public void LabelShouldSizeProperlyOnCollectionViewWithoutBlankSpace() | ||
| { | ||
| App.WaitForElement("TestCollectionView"); | ||
| App.Tap("ScrollToDownButton"); | ||
| App.SetOrientationLandscape(); | ||
| App.WaitForElement("TestCollectionView"); | ||
| VerifyScreenshot(); | ||
| } | ||
|
|
||
| [TearDown] | ||
| public void TearDown() | ||
| { | ||
| App.SetOrientationPortrait(); | ||
| } | ||
| } | ||
| #endif |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,11 +3,17 @@ | |
| using Android.Text; | ||
| using Android.Views; | ||
| using AndroidX.AppCompat.Widget; | ||
| using Microsoft.Maui.Graphics.Platform; | ||
|
|
||
| namespace Microsoft.Maui.Platform | ||
| { | ||
| public class MauiTextView : PlatformAppCompatTextView | ||
| { | ||
| private Layout? _cachedLayout; | ||
| private string? _lastText; | ||
| private int _lastAvailableWidth; | ||
| private int _lastTotalPadding; | ||
|
|
||
| public MauiTextView(Context context) : base(context) | ||
| { | ||
| } | ||
|
|
@@ -20,20 +26,30 @@ internal override void OnLayoutFormatted(bool changed, int l, int t, int r, int | |
| } | ||
| protected override void OnMeasure(int widthMeasureSpec, int heightMeasureSpec) | ||
| { | ||
| if (MeasureSpec.GetMode(widthMeasureSpec) == MeasureSpecMode.AtMost && Layout is not null) | ||
| if (MeasureSpec.GetMode(widthMeasureSpec) == MeasureSpecMode.AtMost && !string.IsNullOrEmpty(Text)) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You check
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I have verified this by dynamically setting the text to an empty string (string.Empty); it resizes correctly, just as it did before the fix. |
||
| { | ||
| // Ensure the Layout is valid and measured before reading LineCount or GetLineWidth(i) to avoid unnecessary calculations. | ||
| if (Layout.Width > 0) | ||
| int availableWidth = MeasureSpec.GetSize(widthMeasureSpec); | ||
| int totalPadding = CompoundPaddingLeft + CompoundPaddingRight; | ||
|
|
||
| if (availableWidth > totalPadding) | ||
| { | ||
| // Calculate the total width needed based on text content plus padding | ||
| int contentWidth = (int)Math.Ceiling(GetMaxLineWidth(Layout)); | ||
| int totalPadding = CompoundPaddingLeft + CompoundPaddingRight; | ||
| int requiredWidth = contentWidth + totalPadding; | ||
| // Only create new layout if text, width or padding changed | ||
| if (_lastText != Text || _lastAvailableWidth != availableWidth || _lastTotalPadding != totalPadding || _cachedLayout is null) | ||
| { | ||
| _cachedLayout = TextLayoutUtils.CreateLayout(Text, Paint, availableWidth - totalPadding, Android.Text.Layout.Alignment.AlignNormal); | ||
| _lastText = Text; | ||
| _lastAvailableWidth = availableWidth; | ||
| _lastTotalPadding = totalPadding; | ||
| } | ||
| // since the original issue 27614 occurs when the text is multiline, we only apply custom width measurement for multiline text | ||
| if (_cachedLayout.LineCount > 1) | ||
| { | ||
| int contentWidth = (int)Math.Ceiling(GetMaxLineWidth(_cachedLayout)); | ||
| int requiredWidth = contentWidth + totalPadding; | ||
| int desiredWidth = Math.Min(requiredWidth, availableWidth); | ||
| widthMeasureSpec = MeasureSpec.MakeMeasureSpec(desiredWidth, MeasureSpecMode.AtMost); | ||
| } | ||
|
|
||
| // Constrain to maximum available width from original spec | ||
| int availableWidth = MeasureSpec.GetSize(widthMeasureSpec); | ||
| int desiredWidth = Math.Min(requiredWidth, availableWidth); | ||
| widthMeasureSpec = MeasureSpec.MakeMeasureSpec(desiredWidth, MeasureSpecMode.AtMost); | ||
| } | ||
| } | ||
| base.OnMeasure(widthMeasureSpec, heightMeasureSpec); | ||
|
|
||
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 preprocessor directive uses
&&which means the test will only compile when both macros are defined; likely you intended||so it’s excluded on either platform where it fails.