Xamarin.Forms StackLayout

Xamarin.Forms StackLayout

StackLayout 在水平或垂直方向在一维堆栈中组织子视图。 默认情况下,StackLayout 是垂直方向。 此外,StackLayout 还可用作包含其他子布局的父布局。

StackLayout 类定义以下属性:

  • StackOrientation 类型 的 Orientation 表示子视图的排列方向。 此属性的默认值为 Vertical
  • double 类型的 Spacing 指示每个子视图之间的空间量。 此属性的默认值为六个与设备无关的单位。

这些属性由 BindableProperty 对象提供支持,这意味着它们可以作为数据绑定的目标,也可以进行设置样式。

StackLayout 类派生自 Layout<T> 类,后者定义了类型 IList<T>Children 属性。 由于 Children 属性是 Layout<T> 类的 ContentProperty,因此不需要通过 XAML 显式设置。

提示

若要获取最佳可能布局性能,请遵循优化布局性能中的准则。

垂直方向

以下 XAML 展示如何创建包含不同子视图且方向垂直的 StackLayout

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="StackLayoutDemos.Views.VerticalStackLayoutPage"
             Title="Vertical StackLayout demo">
    <StackLayout Margin="20">
        <Label Text="Primary colors" />
        <BoxView Color="Red" />
        <BoxView Color="Yellow" />
        <BoxView Color="Blue" />
        <Label Text="Secondary colors" />
        <BoxView Color="Green" />
        <BoxView Color="Orange" />
        <BoxView Color="Purple" />
    </StackLayout>
</ContentPage>

此示例创建包含 LabelBoxView 对象的垂直 StackLayout。 默认情况下,子视图之间有六个与设备无关的空间单位:

竖屏 StackLayout 的屏幕截图

等效 C# 代码如下:

public class VerticalStackLayoutPageCS : ContentPage
{
    public VerticalStackLayoutPageCS()
    {
        Title = "Vertical StackLayout demo";
        Content = new StackLayout
        {
            Margin = new Thickness(20),
            Children =
            {
                new Label { Text = "Primary colors" },
                new BoxView { Color = Color.Red },
                new BoxView { Color = Color.Yellow },
                new BoxView { Color = Color.Blue },
                new Label { Text = "Secondary colors" },
                new BoxView { Color = Color.Green },
                new BoxView { Color = Color.Orange },
                new BoxView { Color = Color.Purple }
            }
        };
    }
}

注意

Margin 属性的值表示某元素与其相邻元素之间的距离。 有关详细信息,请参阅边距和填充

水平方向

以下 XAML 展示如何通过将 Orientation 属性设置为 Horizontal 创建水平方向的 StackLayout:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="StackLayoutDemos.Views.HorizontalStackLayoutPage"
             Title="Horizontal StackLayout demo">
    <StackLayout Margin="20"
                 Orientation="Horizontal"
                 HorizontalOptions="Center">
        <BoxView Color="Red" />
        <BoxView Color="Yellow" />
        <BoxView Color="Blue" />
        <BoxView Color="Green" />
        <BoxView Color="Orange" />
        <BoxView Color="Purple" />
    </StackLayout>
</ContentPage>

此示例创建一个包含 BoxView 对象的水平 StackLayout,且子视图之间有六个与设备无关的空间单位:

横屏 StackLayout 的屏幕截图

等效 C# 代码如下:

public HorizontalStackLayoutPageCS()
{
    Title = "Horizontal StackLayout demo";
    Content = new StackLayout
    {
        Margin = new Thickness(20),
        Orientation = StackOrientation.Horizontal,
        HorizontalOptions = LayoutOptions.Center,
        Children =
        {
            new BoxView { Color = Color.Red },
            new BoxView { Color = Color.Yellow },
            new BoxView { Color = Color.Blue },
            new BoxView { Color = Color.Green },
            new BoxView { Color = Color.Orange },
            new BoxView { Color = Color.Purple }
        }
    };
}

子视图之间的间距

可以通过将 Spacing 属性设置为 double 值更改 StackLayout 中子视图之间的间距:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="StackLayoutDemos.Views.StackLayoutSpacingPage"
             Title="StackLayout Spacing demo">
    <StackLayout Margin="20"
                 Spacing="0">
        <Label Text="Primary colors" />
        <BoxView Color="Red" />
        <BoxView Color="Yellow" />
        <BoxView Color="Blue" />
        <Label Text="Secondary colors" />
        <BoxView Color="Green" />
        <BoxView Color="Orange" />
        <BoxView Color="Purple" />
    </StackLayout>
</ContentPage>

此示例创建一个包含 LabelBoxView 对象的垂直 StackLayout,且这些对象之间没有间距:

不带任何间距的 StackLayout 的屏幕截图

提示

可将 Spacing 属性设置为负值,以使子视图重叠。

等效 C# 代码如下:

public class StackLayoutSpacingPageCS : ContentPage
{
    public StackLayoutSpacingPageCS()
    {
        Title = "StackLayout Spacing demo";
        Content = new StackLayout
        {
            Margin = new Thickness(20),
            Spacing = 0,
            Children =
            {
                new Label { Text = "Primary colors" },
                new BoxView { Color = Color.Red },
                new BoxView { Color = Color.Yellow },
                new BoxView { Color = Color.Blue },
                new Label { Text = "Secondary colors" },
                new BoxView { Color = Color.Green },
                new BoxView { Color = Color.Orange },
                new BoxView { Color = Color.Purple }
            }
        };
    }
}

子视图的位置和大小

StackLayout 中子视图的大小和位置取决于子视图的 HeightRequestWidthRequest 属性的值,以及其 HorizontalOptionsVerticalOptions 属性的值。 在垂直 StackLayout 中,如果未显式设置子视图的大小,则可以将其展开以填充可用宽度。 同样,在水平 StackLayout 中,如果未显式设置子视图的大小,则可将其展开以填充可用高度。

可以将 StackLayout 及其子视图的 HorizontalOptionsVerticalOptions 属性设置为来自 LayoutOptions 结构的字段,该结构封装了两个布局首选项:

  • 对齐方式决定了子视图在其父布局中的位置和大小。
  • 扩展指示子视图是否应使用额外空间(如果可用)。

提示

除非需要,否则不要设置 StackLayoutHorizontalOptionsVerticalOptions 属性。 LayoutOptions.FillLayoutOptions.FillAndExpand 的默认值可以实现最佳布局优化。 更改这些属性会产生成本并消耗内存,即使是将它们设置回默认值。

对齐

以下 XAML 示例在 StackLayout 中对每个子视图设置对齐方式首选项:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="StackLayoutDemos.Views.AlignmentPage"
             Title="Alignment demo">
    <StackLayout Margin="20">
        <Label Text="Start"
               BackgroundColor="Gray"
               HorizontalOptions="Start" />
        <Label Text="Center"
               BackgroundColor="Gray"
               HorizontalOptions="Center" />
        <Label Text="End"
               BackgroundColor="Gray"
               HorizontalOptions="End" />
        <Label Text="Fill"
               BackgroundColor="Gray"
               HorizontalOptions="Fill" />
    </StackLayout>
</ContentPage>

在此示例中,对 Label 对象设置对齐方式首选项,以控制其在 StackLayout 中的位置。 StartCenterEndFill 字段用于定义 Label 对象在父 StackLayout 中的对齐方式:

设置了对齐选项的 StackLayout 的屏幕截图

StackLayout 仅遵循与 StackLayout 方向相反的子视图的对齐方式首选项。 因此,垂直方向的 StackLayout 中的 Label 子视图将其 HorizontalOptions 属性设置为对齐方式字段中的其中一种:

等效 C# 代码如下:

public class AlignmentPageCS : ContentPage
{
    public AlignmentPageCS()
    {
        Title = "Alignment demo";
        Content = new StackLayout
        {
            Margin = new Thickness(20),
            Children =
            {
                new Label { Text = "Start", BackgroundColor = Color.Gray, HorizontalOptions = LayoutOptions.Start },
                new Label { Text = "Center", BackgroundColor = Color.Gray, HorizontalOptions = LayoutOptions.Center },
                new Label { Text = "End", BackgroundColor = Color.Gray, HorizontalOptions = LayoutOptions.End },
                new Label { Text = "Fill", BackgroundColor = Color.Gray, HorizontalOptions = LayoutOptions.Fill }
            }
        };
    }
}

扩展

以下 XAML 示例在 StackLayout 中的每个 Label 上设置扩展首选项:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="StackLayoutDemos.Views.ExpansionPage"
             Title="Expansion demo">
    <StackLayout Margin="20">
        <BoxView BackgroundColor="Red"
                 HeightRequest="1" />
        <Label Text="Start"
               BackgroundColor="Gray"
               VerticalOptions="StartAndExpand" />
        <BoxView BackgroundColor="Red"
                 HeightRequest="1" />
        <Label Text="Center"
               BackgroundColor="Gray"
               VerticalOptions="CenterAndExpand" />
        <BoxView BackgroundColor="Red"
                 HeightRequest="1" />
        <Label Text="End"
               BackgroundColor="Gray"
               VerticalOptions="EndAndExpand" />
        <BoxView BackgroundColor="Red"
                 HeightRequest="1" />
        <Label Text="Fill"
               BackgroundColor="Gray"
               VerticalOptions="FillAndExpand" />
        <BoxView BackgroundColor="Red"
                 HeightRequest="1" />
    </StackLayout>
</ContentPage>

在此示例中,在 Label 对象上设置了扩展首选项以控制它们在 StackLayout 中的大小。 StartAndExpandCenterAndExpandEndAndExpandFillAndExpand 字段用于定义对齐方式首选项,以及如果父级 StackLayout 内有更多空间可用,Label 是否将占用更多空间:

设置了扩展选项的 StackLayout 的屏幕截图

StackLayout 只能按照其方向展开子视图。 因此,垂直方向的 StackLayout 可以扩展 Label 子视图,该视图将 VerticalOptions 属性设置为其中一个扩展字段。 这意味着,对于垂直对齐方式,每个 LabelStackLayout 内占据相同的空间量。 但是,只有最后一个 Label(可将 VerticalOptions 属性设置为 FillAndExpand)具有不同的大小。

提示

使用 StackLayout 时,请确保只有一个子视图设置为 LayoutOptions.Expands。 此属性可确保指定子级会占用 StackLayout 可以向它提供的最大空间,而多次执行这些计算比较浪费。

等效 C# 代码如下:

public ExpansionPageCS()
{
    Title = "Expansion demo";
    Content = new StackLayout
    {
        Margin = new Thickness(20),
        Children =
        {
            new BoxView { BackgroundColor = Color.Red, HeightRequest = 1 },
            new Label { Text = "StartAndExpand", BackgroundColor = Color.Gray, VerticalOptions = LayoutOptions.StartAndExpand },
            new BoxView { BackgroundColor = Color.Red, HeightRequest = 1 },
            new Label { Text = "CenterAndExpand", BackgroundColor = Color.Gray, VerticalOptions = LayoutOptions.CenterAndExpand },
            new BoxView { BackgroundColor = Color.Red, HeightRequest = 1 },
            new Label { Text = "EndAndExpand", BackgroundColor = Color.Gray, VerticalOptions = LayoutOptions.EndAndExpand },
            new BoxView { BackgroundColor = Color.Red, HeightRequest = 1 },
            new Label { Text = "FillAndExpand", BackgroundColor = Color.Gray, VerticalOptions = LayoutOptions.FillAndExpand },
            new BoxView { BackgroundColor = Color.Red, HeightRequest = 1 }
        }
    };
}

重要

使用 StackLayout 中的所有空间时,扩展首选项不起作用。

有关对齐方式和扩展的详细信息,请参阅 Xamarin.Forms 中的布局选项

嵌套的 StackLayout 对象

StackLayout 可用作包含嵌套子 StackLayout 对象或其他子布局的父布局。

以下 XAML 展示了一个嵌套 StackLayout 对象的示例:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="StackLayoutDemos.Views.CombinedStackLayoutPage"
             Title="Combined StackLayouts demo">
    <StackLayout Margin="20">
        ...
        <Frame BorderColor="Black"
               Padding="5">
            <StackLayout Orientation="Horizontal"
                         Spacing="15">
                <BoxView Color="Red" />
                <Label Text="Red"
                       FontSize="Large"
                       VerticalOptions="Center" />
            </StackLayout>
        </Frame>
        <Frame BorderColor="Black"
               Padding="5">
            <StackLayout Orientation="Horizontal"
                         Spacing="15">
                <BoxView Color="Yellow" />
                <Label Text="Yellow"
                       FontSize="Large"
                       VerticalOptions="Center" />
            </StackLayout>
        </Frame>
        <Frame BorderColor="Black"
               Padding="5">
            <StackLayout Orientation="Horizontal"
                         Spacing="15">
                <BoxView Color="Blue" />
                <Label Text="Blue"
                       FontSize="Large"
                       VerticalOptions="Center" />
            </StackLayout>
        </Frame>
        ...
    </StackLayout>
</ContentPage>

在此示例中,父级 StackLayout 包含 Frame 对象中嵌套的 StackLayout 对象。 父级 StackLayout 是垂直朝向,而子级 StackLayout 对象是水平朝向:

嵌套 StackLayout 对象的屏幕截图

重要

嵌套 StackLayout 对象和其他布局越深,嵌套布局对性能的影响就越大。 有关详细信息,请参阅选择正确的布局

等效 C# 代码如下:

public class CombinedStackLayoutPageCS : ContentPage
{
    public CombinedStackLayoutPageCS()
    {
        Title = "Combined StackLayouts demo";
        Content = new StackLayout
        {
            Margin = new Thickness(20),
            Children =
            {
                new Label { Text = "Primary colors" },
                new Frame
                {
                    BorderColor = Color.Black,
                    Padding = new Thickness(5),
                    Content = new StackLayout
                    {
                        Orientation = StackOrientation.Horizontal,
                        Spacing = 15,
                        Children =
                        {
                            new BoxView { Color = Color.Red },
                            new Label { Text = "Red", FontSize = Device.GetNamedSize(NamedSize.Large, typeof(Label)), VerticalOptions = LayoutOptions.Center }
                        }
                    }
                },
                new Frame
                {
                    BorderColor = Color.Black,
                    Padding = new Thickness(5),
                    Content = new StackLayout
                    {
                        Orientation = StackOrientation.Horizontal,
                        Spacing = 15,
                        Children =
                        {
                            new BoxView { Color = Color.Yellow },
                            new Label { Text = "Yellow", FontSize = Device.GetNamedSize(NamedSize.Large, typeof(Label)), VerticalOptions = LayoutOptions.Center }
                        }
                    }
                },
                new Frame
                {
                    BorderColor = Color.Black,
                    Padding = new Thickness(5),
                    Content = new StackLayout
                    {
                        Orientation = StackOrientation.Horizontal,
                        Spacing = 15,
                        Children =
                        {
                            new BoxView { Color = Color.Blue },
                            new Label { Text = "Blue", FontSize = Device.GetNamedSize(NamedSize.Large, typeof(Label)), VerticalOptions = LayoutOptions.Center }
                        }
                    }
                },
                // ...
            }
        };
    }
}