.NET 多平台應用程式 UI(.NET MAUI)的布局类允许您在应用程序中排列和分组 UI 控件。选择布局类需要了解布局如何放置其子元素以及如何调整它们的大小。有时,可能需要嵌套布局来创建所需的界面。
StackLayout
StackLayout 在水平或垂直的一维堆叠中组织元素。其 Orientation 属性指定方向,默认为垂直。StackLayout 通常用于排列页面上的 UI 子部分。
以下 XAML 示例展示了如何创建一个包含三个 Label 对象的垂直 StackLayout:
<StackLayout Margin="20,35,20,25">
<Label Text="The StackLayout has its Margin property set, to control the rendering position of the StackLayout." />
<Label Text="The Padding property can be set to specify the distance between the StackLayout and its children." />
<Label Text="The Spacing property can be set to specify the distance between views in the StackLayout." />
</StackLayout>
在 StackLayout 中,如果未明确设置元素的大小,它们会扩展以填充可用宽度(当 Orientation 设置为水平时)或高度。
StackLayout 常作为父布局包含其他子布局。但是,不应使用多个 StackLayout 的组合来重现 Grid 布局,因为这会浪费资源。以下代码展示了这种不良做法:
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Details.HomePage"
Padding="0,20,0,0">
<StackLayout>
<StackLayout Orientation="Horizontal">
<Label Text="Name:" />
<Entry Placeholder="Enter your name" />
</StackLayout>
<StackLayout Orientation="Horizontal">
<Label Text="Age:" />
<Entry Placeholder="Enter your age" />
</StackLayout>
<StackLayout Orientation="Horizontal">
<Label Text="Occupation:" />
<Entry Placeholder="Enter your occupation" />
</StackLayout>
<StackLayout Orientation="Horizontal">
<Label Text="Address:" />
<Entry Placeholder="Enter your address" />
</StackLayout>
</StackLayout>
</ContentPage>
相反,应使用 Grid 来实现所需布局。
HorizontalStackLayout
HorizontalStackLayout 在水平堆叠中组织子视图,是 StackLayout 的性能更高替代方案。它通常用于排列页面上的 UI 子部分。
以下 XAML 示例展示了如何创建包含不同子视图的 HorizontalStackLayout:
<HorizontalStackLayout Margin="20">
<Rectangle Fill="Red"
HeightRequest="30"
WidthRequest="30" />
<Label Text="Red"
FontSize="18" />
</HorizontalStackLayout>
在 HorizontalStackLayout 中,如果未明确设置元素的大小,它们会扩展以填充可用高度。
VerticalStackLayout
VerticalStackLayout 在垂直堆叠中组织子视图,是 StackLayout 的性能更高替代方案。它通常用于排列页面上的 UI 子部分。
以下 XAML 示例展示了如何创建包含三个 Label 对象的 VerticalStackLayout:
<VerticalStackLayout Margin="20,35,20,25">
<Label Text="The VericalStackLayout has its Margin property set, to control the rendering position of the VerticalStackLayout." />
<Label Text="The Padding property can be set to specify the distance between the VerticalStackLayout and its children." />
<Label Text="The Spacing property can be set to specify the distance between views in the VerticalStackLayout." />
</VerticalStackLayout>
在 VerticalStackLayout 中,如果未明确设置元素的大小,它们会扩展以填充可用宽度。
Grid
Grid 用于在行和列中显示元素,大小可以是比例或绝对大小。行和列通过 RowDefinitions 和 ColumnDefinitions 属性指定。
要将项目放置在特定 Grid 单元格中,使用 Grid.Column 和 Grid.Row 附加属性。要使元素跨越多行和多列,使用 Grid.RowSpan 和 Grid.ColumnSpan 附加属性。
注意:Grid 布局不应与表格混淆,不适合呈现表格数据。
以下 XAML 示例展示了如何创建具有两行和两列的 Grid:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="50" />
<RowDefinition Height="50" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Label Text="Column 0, Row 0"
WidthRequest="200" />
<Label Grid.Column="1"
Text="Column 1, Row 0" />
<Label Grid.Row="1"
Text="Column 0, Row 1" />
<Label Grid.Column="1"
Grid.Row="1"
Text="Column 1, Row 1" />
</Grid>
在此示例中:
- 每行有明确的 50 个设备独立单位高度。
- 第一列宽度设置为
Auto,因此其子元素所需宽度相同,这里是 200 个单位以适应第一个 Label。
空间可以通过自动调整大小在列或行内分配,使列和行大小适应其内容。为此,将 RowDefinition 的高度或 ColumnDefinition 的宽度设置为 Auto。比例调整大小也可用于按加权比例在行和列之间分配可用空间,使用 * 运算符。
警告:尽量少将行或列设置为 Auto 大小,因为每个自动调整大小的行或列都会导致布局引擎执行额外计算。尽可能使用固定大小或比例空间。
FlexLayout
FlexLayout 类似于 StackLayout,在堆叠中水平或垂直显示子项目。但 FlexLayout 还可以在单行或单列容纳不下时换行,并能更精细地控制子元素的大小、方向和对齐。
以下 XAML 示例展示了如何创建 FlexLayout 以在单行中显示其视图:
<FlexLayout Direction="Column"
AlignItems="Center"
JustifyContent="SpaceEvenly">
<Label Text="FlexLayout in Action" />
<Button Text="Button" />
<Label Text="Another Label" />
</FlexLayout>
在此示例中:
Direction属性设置为Column,使子元素排列在单列中。AlignItems属性设置为Center,使每个项目水平居中。JustifyContent属性设置为SpaceEvenly,将所有剩余垂直空间平均分配在项目之间以及第一个项目上方和最后一个项目下方。
AbsoluteLayout
AbsoluteLayout 使用明确值或相对于布局大小的值来定位和调整项目大小。位置相对于 AbsoluteLayout 的左上角指定。
AbsoluteLayout 应视为特殊用途布局,仅当可以对子元素施加大小或元素大小不影响其他子元素位置时使用。典型用法是创建覆盖页面的叠加层,可能保护用户与页面控件交互。
在 AbsoluteLayout 中,AbsoluteLayout.LayoutBounds 附加属性用于指定项目的水平位置、垂直位置、宽度和高度。AbsoluteLayout.LayoutFlags 附加属性指定如何解释布局边界。
以下 XAML 示例展示了如何在 AbsoluteLayout 中排列项目:
<AbsoluteLayout Margin="40">
<BoxView Color="Red"
AbsoluteLayout.LayoutFlags="PositionProportional"
AbsoluteLayout.LayoutBounds="0.5, 0, 100, 100"
Rotation="30" />
<BoxView Color="Green"
AbsoluteLayout.LayoutFlags="PositionProportional"
AbsoluteLayout.LayoutBounds="0.5, 0, 100, 100"
Rotation="60" />
<BoxView Color="Blue"
AbsoluteLayout.LayoutFlags="PositionProportional"
AbsoluteLayout.LayoutBounds="0.5, 0, 100, 100" />
</AbsoluteLayout>
在此示例中:
- 每个 BoxView 指定 100x100 的明确大小,并以水平居中方式显示在相同位置。
- 红色 BoxView 旋转 30 度,绿色旋转 60 度。
- 在每个 BoxView 上,
AbsoluteLayout.LayoutFlags设置为PositionProportional,表示位置与剩余空间成比例。
警告:尽可能避免使用 AbsoluteLayout.AutoSize 属性,因为它会导致额外布局计算。
BindableLayout
BindableLayout 允许任何派生自 Layout 类的布局类通过绑定到项目集合来生成其内容,并可选使用 DataTemplate 设置每个项目外观。
可通过将 ItemsSource 属性设置为任何实现 IEnumerable 的集合来填充可绑定布局,并将其附加到 Layout 派生类。通过将 BindableLayout.ItemTemplate 附加属性设置为 DataTemplate 来定义每个项目外观。
以下 XAML 示例展示了如何将 StackLayout 绑定到项目集合并使用 DataTemplate 定义其外观:
<StackLayout BindableLayout.ItemsSource="{Binding User.TopFollowers}"
Orientation="Horizontal">
<BindableLayout.ItemTemplate>
<DataTemplate x:DataType="sys:String">
<Image Source="{Binding}"
Aspect="AspectFill"
WidthRequest="44"
HeightRequest="44" />
</DataTemplate>
</BindableLayout.ItemTemplate>
</StackLayout>
仅当要显示的项目集合较小且不需要滚动和选择时,才应使用可绑定布局。
自定义布局
在 .NET MAUI 中,布局类派生自抽象 Layout 类。此类将跨平台布局和度量委托给布局管理器类。每个布局管理器类实现 ILayoutManager 接口,指定必须提供 Measure 和 ArrangeChildren 实现:
- Measure 实现调用布局中每个视图的 IView.Measure,并返回指定约束的布局总大小。
- ArrangeChildren 实现决定每个视图在布局边界内的位置,并调用每个视图的 Arrange 及其适当边界。返回值是布局的实际大小。
.NET MAUI 的布局有预定义布局管理器处理其布局。但有时需要使用 .NET MAUI 未提供的布局来组织页面内容。可以通过编写自定义布局实现。
输入透明度
每个视觉元素都有一个 InputTransparent 可绑定属性,用于定义元素是否可以接收输入。默认值为 false,确保元素可以接收输入。当此属性在元素上为 true 时,元素将不会接收任何输入;输入将传递给视觉上位于元素后方的任何元素。
Layout 类(所有布局派生自此类)具有 CascadeInputTransparent 可绑定属性,控制子元素是否继承布局的输入透明度。默认值为 true,确保将布局类上的 InputTransparent 属性设置为 true 会导致布局中的所有元素不接收任何输入。