Xamarin.Forms CollectionView 分组

Download Sample下载示例

在不断滚动的列表中显示时,大型数据集通常会变得笨拙。 在此方案中,将数据组织成组可以简化数据导航,从而改善用户体验。

CollectionView 支持显示分组的数据,并定义以下属性来控制其呈现方式:

  • IsGrouped,类型为 bool,指示是否应在组中显示基础数据。 此属性的默认值为 false
  • GroupHeaderTemplate,类型为 DataTemplate,每个组的标头要使用的模板。
  • GroupFooterTemplate,类型为 DataTemplate,每个组的页脚要使用的模板。

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

下面的屏幕截图显示了一个 CollectionView,其中显示了分组的数据:

Screenshot of a grouped data in a CollectionView, on iOS and Android

有关数据模板的详细信息,请参阅 Xamarin.Forms 数据模板

为数据分组

必须先对数据进行分组,然后才能显示数据。 这可以通过创建组列表来达成此目的,其中每个组都是项的列表。 组列表应为 IEnumerable<T> 集合,其中 T 定义两条数据:

  • 组名称。
  • 定义组内项的 IEnumerable 集合。

因此,分组数据的过程为:

  • 创建对单个项建模的类型。
  • 创建对单个项组建模的类型。
  • 创建 IEnumerable<T> 集合,其中 T 为对单个项组建模的类型。 因此,此集合是一个组集合,这些组用于存储分组的数据。
  • 将数据添加到 IEnumerable<T> 集合。

示例

对数据分组时,第一步是创建对单个项进行建模的类型。 以下示例显示示例应用程序中的 Animal 类:

public class Animal
{
    public string Name { get; set; }
    public string Location { get; set; }
    public string Details { get; set; }
    public string ImageUrl { get; set; }
}

Animal 类对单个项建模。 然后,可以创建对项组建模的类型。 以下示例显示示例应用程序中的 AnimalGroup 类:

public class AnimalGroup : List<Animal>
{
    public string Name { get; private set; }

    public AnimalGroup(string name, List<Animal> animals) : base(animals)
    {
        Name = name;
    }
}

AnimalGroup 类继承自 List<T> 类,并添加表示组名的 Name 属性。

然后,可以创建 IEnumerable<T> 组集合:

public List<AnimalGroup> Animals { get; private set; } = new List<AnimalGroup>();

此代码定义名为 Animals 的集合,其中集合中的每个项都是一个 AnimalGroup 对象。 每个 AnimalGroup 对象都包含名称和定义组中 Animal 对象的 List<Animal> 集合。

然后,可将分组数据添加到 Animals 集合中:

Animals.Add(new AnimalGroup("Bears", new List<Animal>
{
    new Animal
    {
        Name = "American Black Bear",
        Location = "North America",
        Details = "Details about the bear go here.",
        ImageUrl = "https://upload.wikimedia.org/wikipedia/commons/0/08/01_Schwarzbär.jpg"
    },
    new Animal
    {
        Name = "Asian Black Bear",
        Location = "Asia",
        Details = "Details about the bear go here.",
        ImageUrl = "https://upload.wikimedia.org/wikipedia/commons/thumb/b/b7/Ursus_thibetanus_3_%28Wroclaw_zoo%29.JPG/180px-Ursus_thibetanus_3_%28Wroclaw_zoo%29.JPG"
    },
    // ...
}));

Animals.Add(new AnimalGroup("Monkeys", new List<Animal>
{
    new Animal
    {
        Name = "Baboon",
        Location = "Africa & Asia",
        Details = "Details about the monkey go here.",
        ImageUrl = "https://upload.wikimedia.org/wikipedia/commons/thumb/f/fc/Papio_anubis_%28Serengeti%2C_2009%29.jpg/200px-Papio_anubis_%28Serengeti%2C_2009%29.jpg"
    },
    new Animal
    {
        Name = "Capuchin Monkey",
        Location = "Central & South America",
        Details = "Details about the monkey go here.",
        ImageUrl = "https://upload.wikimedia.org/wikipedia/commons/thumb/4/40/Capuchin_Costa_Rica.jpg/200px-Capuchin_Costa_Rica.jpg"
    },
    new Animal
    {
        Name = "Blue Monkey",
        Location = "Central and East Africa",
        Details = "Details about the monkey go here.",
        ImageUrl = "https://upload.wikimedia.org/wikipedia/commons/thumb/8/83/BlueMonkey.jpg/220px-BlueMonkey.jpg"
    },
    // ...
}));

此代码在 Animals 集合中创建两个组。 第一个 AnimalGroup 名为 Bears,包含熊详细信息的 List<Animal> 集合。 第二个 AnimalGroup 名为 Monkeys,包含猴子详细信息的 List<Animal> 集合。

显示分组数据

如果数据已正确分组,CollectionView 将通过将 IsGrouped 属性设置为 true 来显示分组数据:

<CollectionView ItemsSource="{Binding Animals}"
                IsGrouped="true">
    <CollectionView.ItemTemplate>
        <DataTemplate>
            <Grid Padding="10">
                ...
                <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>

等效 C# 代码如下:

CollectionView collectionView = new CollectionView
{
    IsGrouped = true
};
collectionView.SetBinding(ItemsView.ItemsSourceProperty, "Animals");
// ...

通过将 CollectionView.ItemTemplate 属性设置为 DataTemplate 可定义 CollectionView 中每个项的外观。 有关详细信息,请参阅定义项外观

注意

默认情况下,CollectionView 将在组标头和页脚中显示组名称。 可以通过自定义组标头和组页脚来更改此行为。

自定义组标头

通过将 CollectionView.GroupHeaderTemplate 属性设置为 DataTemplate,即可自定义每个组标头的外观:

<CollectionView ItemsSource="{Binding Animals}"
                IsGrouped="true">
    ...
    <CollectionView.GroupHeaderTemplate>
        <DataTemplate>
            <Label Text="{Binding Name}"
                   BackgroundColor="LightGray"
                   FontSize="Large"
                   FontAttributes="Bold" />
        </DataTemplate>
    </CollectionView.GroupHeaderTemplate>
</CollectionView>

在此示例中,每个组标头都设置为显示组名的 Label,并且设置了其他外观属性。 下面的屏幕截图显示了自定义的组标题:

Screenshot of a customized group header in a CollectionView, on iOS and Android

通过将 CollectionView.GroupFooterTemplate 属性设置为 DataTemplate,可以自定义每个组页脚的外观:

<CollectionView ItemsSource="{Binding Animals}"
                IsGrouped="true">
    ...
    <CollectionView.GroupFooterTemplate>
        <DataTemplate>
            <Label Text="{Binding Count, StringFormat='Total animals: {0:D}'}"
                   Margin="0,0,0,10" />
        </DataTemplate>
    </CollectionView.GroupFooterTemplate>
</CollectionView>

在此示例中,每个组页脚都设置为 Label,显示组中的项目数。 下面的屏幕截图显示了自定义的组脚注:

Screenshot of a customized group footer in a CollectionView, on iOS and Android

空组

CollectionView 显示分组数据时,它将显示任何空分组。 此类组将显示一个组标头和页脚,指示该组为空。 下面的屏幕截图显示了一个空组:

Screenshot of an empty group in a CollectionView, on iOS and Android

注意

在 iOS 10 及更低版本上,空组的组标题和脚注可能全部显示在 CollectionView 的顶部。

没有模板的组

CollectionView 可以正确显示分组数据,而无需将 CollectionView.ItemTemplate 属性设置为 DataTemplate

<CollectionView ItemsSource="{Binding Animals}"
                IsGrouped="true" />

在此方案中,可以通过替代单个项建模类型和单个项组建模类型中的 ToString 方法来显示有意义的数据。