Xamarin.Forms CollectionView 布局

Download Sample下载示例

CollectionView 定义以下对布局进行控制的属性:

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

默认情况下,CollectionView 将在垂直列表中显示其项。 但是,可以使用以下任一布局:

  • 垂直列表 – 随着添加新项而在垂直方向上增加的单列列表。
  • 水平列表 – 随着添加新项而在水平方向增加的单行列表。
  • 垂直网格 – 随着添加新项而在垂直方向上增加的多列网格。
  • 水平网格 – 随着添加新项而在水平方向增加的多行网格。

可以通过将 ItemsLayout 属性设置为派生自 ItemsLayout 类的类来指定这些布局。 此类定义了以下属性:

这些属性由 BindableProperty 对象提供支持,这意味着这些属性可以作为数据绑定的目标。 有关对齐点的详细信息,请参阅 Xamarin.Forms CollectionView 滚动指南中的对齐点

ItemsLayoutOrientation 枚举定义以下成员:

LinearItemsLayout 类继承自 ItemsLayout 类,并定义类型为 doubleItemSpacing 属性,该属性表示每个项周围的空白空间。 此属性的默认值为 0,其值必须始终大于或等于 0。 LinearItemsLayout 类还定义静态 VerticalHorizontal 成员。 这些成员可用于分别创建垂直列表或水平列表。 或者,可以创建 LinearItemsLayout 对象,将 ItemsLayoutOrientation 枚举成员指定为参数。

GridItemsLayout 类继承自 ItemsLayout 类,并定义以下属性:

  • VerticalItemSpacing,类型为 double,表示每个项周围的垂直空白空间。 此属性的默认值为 0,并且其值必须始终大于或等于 0。
  • HorizontalItemSpacing,类型为 double,表示每个项周围的水平空白空间。 此属性的默认值为 0,并且其值必须始终大于或等于 0。
  • Span,类型为 int,表示要在网格中显示的列数或行数。 此属性的默认值为 1,并且其值必须始终大于或等于 1。

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

注意

CollectionView 使用本机布局引擎执行布局。

垂直列表

默认情况下,CollectionView 将在垂直列表布局中显示其项。 因此,无需设置 ItemsLayout 属性即可使用此布局:

<CollectionView ItemsSource="{Binding Monkeys}">
    <CollectionView.ItemTemplate>
        <DataTemplate>
            <Grid Padding="10">
                <Grid.RowDefinitions>
                    <RowDefinition Height="Auto" />
                    <RowDefinition Height="Auto" />
                </Grid.RowDefinitions>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="Auto" />
                    <ColumnDefinition Width="Auto" />
                </Grid.ColumnDefinitions>
                <Image Grid.RowSpan="2"
                       Source="{Binding ImageUrl}"
                       Aspect="AspectFill"
                       HeightRequest="60"
                       WidthRequest="60" />
                <Label Grid.Column="1"
                       Text="{Binding Name}"
                       FontAttributes="Bold" />
                <Label Grid.Row="1"
                       Grid.Column="1"
                       Text="{Binding Location}"
                       FontAttributes="Italic"
                       VerticalOptions="End" />
            </Grid>
        </DataTemplate>
    </CollectionView.ItemTemplate>
</CollectionView>

但是,为了完整起见,在 XAML 中,可以通过将 CollectionViewItemsLayout 属性设置为 VerticalList,将其设置为在垂直列表中显示其项:

<CollectionView ItemsSource="{Binding Monkeys}"
                ItemsLayout="VerticalList">
    ...
</CollectionView>

或者,也可以通过将 ItemsLayout 属性设置为 LinearItemsLayout 对象,并将 VerticalItemsLayoutOrientation 枚举成员指定为 Orientation 属性值来完成此操作:

<CollectionView ItemsSource="{Binding Monkeys}">
    <CollectionView.ItemsLayout>
        <LinearItemsLayout Orientation="Vertical" />
    </CollectionView.ItemsLayout>
    ...
</CollectionView>

等效 C# 代码如下:

CollectionView collectionView = new CollectionView
{
    ...
    ItemsLayout = LinearItemsLayout.Vertical
};

这会生成单列列表,该列表会随着添加新项在垂直方向上增加:

Screenshot of a CollectionView vertical list layout, on iOS and Android

水平列表

在 XAML 中,CollectionView 可以通过将其 ItemsLayout 属性设置为 HorizontalList,在水平列表中显示其项:

<CollectionView ItemsSource="{Binding Monkeys}"
                ItemsLayout="HorizontalList">
    <CollectionView.ItemTemplate>
        <DataTemplate>
            <Grid Padding="10">
                <Grid.RowDefinitions>
                    <RowDefinition Height="35" />
                    <RowDefinition Height="35" />
                </Grid.RowDefinitions>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="70" />
                    <ColumnDefinition Width="140" />
                </Grid.ColumnDefinitions>
                <Image Grid.RowSpan="2"
                       Source="{Binding ImageUrl}"
                       Aspect="AspectFill"
                       HeightRequest="60"
                       WidthRequest="60" />
                <Label Grid.Column="1"
                       Text="{Binding Name}"
                       FontAttributes="Bold"
                       LineBreakMode="TailTruncation" />
                <Label Grid.Row="1"
                       Grid.Column="1"
                       Text="{Binding Location}"
                       LineBreakMode="TailTruncation"
                       FontAttributes="Italic"
                       VerticalOptions="End" />
            </Grid>
        </DataTemplate>
    </CollectionView.ItemTemplate>
</CollectionView>

或者,也可以通过将 ItemsLayout 属性设置为 LinearItemsLayout 对象,并将 HorizontalItemsLayoutOrientation 枚举成员指定为 Orientation 属性值来完成此布局:

<CollectionView ItemsSource="{Binding Monkeys}">
    <CollectionView.ItemsLayout>
        <LinearItemsLayout Orientation="Horizontal" />
    </CollectionView.ItemsLayout>
    ...
</CollectionView>

等效 C# 代码如下:

CollectionView collectionView = new CollectionView
{
    ...
    ItemsLayout = LinearItemsLayout.Horizontal
};

这会生成单行列表,该列表会随着添加新项而在水平方向增加:

Screenshot of a CollectionView horizontal list layout, on iOS and Android

垂直网格

在 XAML 中,CollectionView 可以通过将其 ItemsLayout 属性设置为 VerticalGrid,在垂直网格中显示其项:

<CollectionView ItemsSource="{Binding Monkeys}"
                ItemsLayout="VerticalGrid, 2">
    <CollectionView.ItemTemplate>
        <DataTemplate>
            <Grid Padding="10">
                <Grid.RowDefinitions>
                    <RowDefinition Height="35" />
                    <RowDefinition Height="35" />
                </Grid.RowDefinitions>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="70" />
                    <ColumnDefinition Width="80" />
                </Grid.ColumnDefinitions>
                <Image Grid.RowSpan="2"
                       Source="{Binding ImageUrl}"
                       Aspect="AspectFill"
                       HeightRequest="60"
                       WidthRequest="60" />
                <Label Grid.Column="1"
                       Text="{Binding Name}"
                       FontAttributes="Bold"
                       LineBreakMode="TailTruncation" />
                <Label Grid.Row="1"
                       Grid.Column="1"
                       Text="{Binding Location}"
                       LineBreakMode="TailTruncation"
                       FontAttributes="Italic"
                       VerticalOptions="End" />
            </Grid>
        </DataTemplate>
    </CollectionView.ItemTemplate>
</CollectionView>

或者,也可以通过将 ItemsLayout 属性设置为 GridItemsLayout 对象,将该对象的 Orientation 属性设置为 Vertical 来完成此布局:

<CollectionView ItemsSource="{Binding Monkeys}">
    <CollectionView.ItemsLayout>
       <GridItemsLayout Orientation="Vertical"
                        Span="2" />
    </CollectionView.ItemsLayout>
    ...
</CollectionView>

等效 C# 代码如下:

CollectionView collectionView = new CollectionView
{
    ...
    ItemsLayout = new GridItemsLayout(2, ItemsLayoutOrientation.Vertical)
};

默认情况下,垂直 GridItemsLayout 将显示单列中的项。 但是,此示例将 GridItemsLayout.Span 属性设置为 2。 这将生成一个两列网格,该网格会随着添加新项而在垂直方向上增加:

Screenshot of a CollectionView vertical grid layout, on iOS and Android

水平网格

在 XAML 中,CollectionView 可以通过将其 ItemsLayout 属性设置为 HorizontalGrid,在水平网格中显示其项:

<CollectionView ItemsSource="{Binding Monkeys}"
                ItemsLayout="HorizontalGrid, 4">
    <CollectionView.ItemTemplate>
        <DataTemplate>
            <Grid Padding="10">
                <Grid.RowDefinitions>
                    <RowDefinition Height="35" />
                    <RowDefinition Height="35" />
                </Grid.RowDefinitions>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="70" />
                    <ColumnDefinition Width="140" />
                </Grid.ColumnDefinitions>
                <Image Grid.RowSpan="2"
                       Source="{Binding ImageUrl}"
                       Aspect="AspectFill"
                       HeightRequest="60"
                       WidthRequest="60" />
                <Label Grid.Column="1"
                       Text="{Binding Name}"
                       FontAttributes="Bold"
                       LineBreakMode="TailTruncation" />
                <Label Grid.Row="1"
                       Grid.Column="1"
                       Text="{Binding Location}"
                       LineBreakMode="TailTruncation"
                       FontAttributes="Italic"
                       VerticalOptions="End" />
            </Grid>
        </DataTemplate>
    </CollectionView.ItemTemplate>
</CollectionView>

或者,也可以通过将 ItemsLayout 属性设置为 GridItemsLayout 对象,将该对象的 Orientation 属性设置为 Horizontal 来完成此布局:

<CollectionView ItemsSource="{Binding Monkeys}">
    <CollectionView.ItemsLayout>
       <GridItemsLayout Orientation="Horizontal"
                        Span="4" />
    </CollectionView.ItemsLayout>
    ...
</CollectionView>

等效 C# 代码如下:

CollectionView collectionView = new CollectionView
{
    ...
    ItemsLayout = new GridItemsLayout(4, ItemsLayoutOrientation.Horizontal)
};

默认情况下,水平 GridItemsLayout 将在单行中显示项。 但是,此示例将 GridItemsLayout.Span 属性设置为 4。 这将生成一个四行网格,该网格会随着新项的添加而水平扩展:

Screenshot of a CollectionView horizontal grid layout, on iOS and Android

页眉和页脚

CollectionView 可以显示随列表中的项一起滚动的页眉和页脚。 页眉和页脚可以是字符串、视图或 DataTemplate 对象。

CollectionView 定义用于指定页眉和页脚的以下属性:

  • object 类型的 Header 指定将在列表开头显示的字符串、绑定或视图。
  • DataTemplate 类型的 HeaderTemplate 指定用于设置 Header 格式的 DataTemplate
  • object 类型的 Footer 指定将在列表末尾显示的字符串、绑定或视图。
  • DataTemplate 类型的 FooterTemplate 指定用于设置 Footer 格式的 DataTemplate

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

将页眉添加到从左到右水平扩展的布局中时,页眉会显示在列表的左侧。 同样,将页脚添加到从左到右水平扩展的布局时,页脚会显示在列表右侧。

可以将 HeaderFooter 属性设置为 string 值,如以下示例所示:

<CollectionView ItemsSource="{Binding Monkeys}"
                Header="Monkeys"
                Footer="2019">
    ...
</CollectionView>

等效 C# 代码如下:

CollectionView collectionView = new CollectionView
{
    Header = "Monkeys",
    Footer = "2019"
};
collectionView.SetBinding(ItemsView.ItemsSourceProperty, "Monkeys");

此代码生成以下屏幕截图,其中页眉显示在 iOS 屏幕截图中,页脚显示在 Android 屏幕截图中:

Screenshot of a CollectionView string header and footer, on iOS and Android

可以将 FooterHeader 属性都设置为视图。 这可以是单个视图,也可以是包含多个子视图的视图。 以下示例显示 HeaderFooter 属性,每个属性都设置为包含 Label 对象的 StackLayout 对象:

<CollectionView ItemsSource="{Binding Monkeys}">
    <CollectionView.Header>
        <StackLayout BackgroundColor="LightGray">
            <Label Margin="10,0,0,0"
                   Text="Monkeys"
                   FontSize="Small"
                   FontAttributes="Bold" />
        </StackLayout>
    </CollectionView.Header>
    <CollectionView.Footer>
        <StackLayout BackgroundColor="LightGray">
            <Label Margin="10,0,0,0"
                   Text="Friends of Xamarin Monkey"
                   FontSize="Small"
                   FontAttributes="Bold" />
        </StackLayout>
    </CollectionView.Footer>
    ...
</CollectionView>

等效 C# 代码如下:

CollectionView collectionView = new CollectionView
{
    Header = new StackLayout
    {
        Children =
        {
            new Label { Text = "Monkeys", ... }
        }
    },
    Footer = new StackLayout
    {
        Children =
        {
            new Label { Text = "Friends of Xamarin Monkey", ... }
        }
    }
};
collectionView.SetBinding(ItemsView.ItemsSourceProperty, "Monkeys");

此代码生成以下屏幕截图,其中页眉显示在 iOS 屏幕截图中,页脚显示在 Android 屏幕截图中:

Screenshot of a CollectionView header and footer using views, on iOS and Android

可以将 HeaderTemplateFooterTemplate 属性设置为用于设置页眉和页脚格式的 DataTemplate 对象。 在此方案中,HeaderFooter 属性必须绑定到要应用模板的当前源,如以下示例所示:

<CollectionView ItemsSource="{Binding Monkeys}"
                Header="{Binding .}"
                Footer="{Binding .}">
    <CollectionView.HeaderTemplate>
        <DataTemplate>
            <StackLayout BackgroundColor="LightGray">
                <Label Margin="10,0,0,0"
                       Text="Monkeys"
                       FontSize="Small"
                       FontAttributes="Bold" />
            </StackLayout>
        </DataTemplate>
    </CollectionView.HeaderTemplate>
    <CollectionView.FooterTemplate>
        <DataTemplate>
            <StackLayout BackgroundColor="LightGray">
                <Label Margin="10,0,0,0"
                       Text="Friends of Xamarin Monkey"
                       FontSize="Small"
                       FontAttributes="Bold" />
            </StackLayout>
        </DataTemplate>
    </CollectionView.FooterTemplate>
    ...
</CollectionView>

等效 C# 代码如下:

CollectionView collectionView = new CollectionView
{
    HeaderTemplate = new DataTemplate(() =>
    {
        return new StackLayout { };
    }),
    FooterTemplate = new DataTemplate(() =>
    {
        return new StackLayout { };
    })
};
collectionView.SetBinding(ItemsView.HeaderProperty, ".");
collectionView.SetBinding(ItemsView.FooterProperty, ".");
collectionView.SetBinding(ItemsView.ItemsSourceProperty, "Monkeys");

此代码生成以下屏幕截图,其中页眉显示在 iOS 屏幕截图中,页脚显示在 Android 屏幕截图中:

Screenshot of a CollectionView header and footer using templates, on iOS and Android

项间距

默认情况下,CollectionView 中的每个项之间没有空格。 可以通过在 CollectionView 使用的项布局上设置属性更改此行为。

CollectionView 将其 ItemsLayout 属性设置为 LinearItemsLayout 对象时,可以将 LinearItemsLayout.ItemSpacing 属性设置为表示项之间空格的 double 值:

<CollectionView ItemsSource="{Binding Monkeys}">
    <CollectionView.ItemsLayout>
        <LinearItemsLayout Orientation="Vertical"
                           ItemSpacing="20" />
    </CollectionView.ItemsLayout>
    ...
</CollectionView>

注意

LinearItemsLayout.ItemSpacing 属性设置验证回调集,可确保属性值始终大于或等于 0。

等效 C# 代码如下:

CollectionView collectionView = new CollectionView
{
    ...
    ItemsLayout = new LinearItemsLayout(ItemsLayoutOrientation.Vertical)
    {
        ItemSpacing = 20
    }
};

此代码生成一个垂直单列列表,各个项之间的间距为 20:

Screenshot of a CollectionView with item spacing, on iOS and Android

CollectionView 将其 ItemsLayout 属性设置为 GridItemsLayout 对象时,可以将 GridItemsLayout.VerticalItemSpacingGridItemsLayout.HorizontalItemSpacing 属性设置为 double 值,这些值表示项之间的垂直和水平空白区域:

<CollectionView ItemsSource="{Binding Monkeys}">
    <CollectionView.ItemsLayout>
       <GridItemsLayout Orientation="Vertical"
                        Span="2"
                        VerticalItemSpacing="20"
                        HorizontalItemSpacing="30" />
    </CollectionView.ItemsLayout>
    ...
</CollectionView>

注意

GridItemsLayout.VerticalItemSpacingGridItemsLayout.HorizontalItemSpacing 属性设置了验证回调,这可确保属性的值始终大于或等于 0。

等效 C# 代码如下:

CollectionView collectionView = new CollectionView
{
    ...
    ItemsLayout = new GridItemsLayout(2, ItemsLayoutOrientation.Vertical)
    {
        VerticalItemSpacing = 20,
        HorizontalItemSpacing = 30
    }
};

此代码生成一个垂直两列网格,各个项之间的垂直间距为 20,各个项之间水平间距为 30:

Screenshot of a CollectionView with item spacing, on Android

调整项大小

默认情况下,如果 DataTemplate 中的 UI 元素未指定固定大小,则单独测量和调整 CollectionView 中每个项的大小。 此行为(可以更改)由 CollectionView.ItemSizingStrategy 属性值指定。 可将此属性值设置为 ItemSizingStrategy 枚举成员中的一个:

  • MeasureAllItems – 每个项都是单独测量的。 这是默认值。
  • MeasureFirstItem – 仅测量第一项,所有后续项的大小都与第一项相同。

重要

在所有项的大小均一致的情况下使用 MeasureFirstItem 大小调整策略将可以提高性能。

以下代码示例展示如何设置 ItemSizingStrategy 属性:

<CollectionView ...
                ItemSizingStrategy="MeasureFirstItem">
    ...
</CollectionView>

等效 C# 代码如下:

CollectionView collectionView = new CollectionView
{
    ...
    ItemSizingStrategy = ItemSizingStrategy.MeasureFirstItem
};

动态调整项大小

在运行时可以通过更改 DataTemplate 中元素的布局相关属性,动态调整 CollectionView 中项的大小。 例如,以下代码示例更改 Image 对象的 HeightRequestWidthRequest 属性:

void OnImageTapped(object sender, EventArgs e)
{
    Image image = sender as Image;
    image.HeightRequest = image.WidthRequest = image.HeightRequest.Equals(60) ? 100 : 60;
}

执行 OnImageTapped 事件处理程序以响应正在点击的 Image 对象,并更改图像尺寸,以便其更易于查看:

Screenshot of a CollectionView with dynamic item sizing, on iOS and Android

从右到左布局

CollectionView 可按从右到左的流方向布局其内容,方法是将其 FlowDirection 属性设置为 RightToLeft。 但是,最好在页面或根布局上设置 FlowDirection 属性,以便页面或根布局中的所有元素都响应流方向:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="CollectionViewDemos.Views.VerticalListFlowDirectionPage"
             Title="Vertical list (RTL FlowDirection)"
             FlowDirection="RightToLeft">
    <StackLayout Margin="20">
        <CollectionView ItemsSource="{Binding Monkeys}">
            ...
        </CollectionView>
    </StackLayout>
</ContentPage>

包含父元素的元素其默认 FlowDirectionMatchParent。 因此,CollectionViewStackLayout 继承 FlowDirection 属性值,而前者又从 ContentPage 继承 FlowDirection 属性值。 这会生成如以下屏幕截图所示的布局:

Screenshot of a CollectionView right-to-left vertical list layout, on iOS and Android

有关流方向的详细信息,请参阅从右到左本地化