天天看點

第十七章:掌握網格(一)基本網格

Grid是一種強大的布局機制,可将其子項組織為單元格的行和列。起初,Grid似乎與HTML表類似,但有一個非常重要的差別:HTML表是為示範目的而設計的,而Grid僅用于布局。例如,網格中沒有标題的概念,并且沒有内置功能來在單元格周圍繪制框或使用分隔線分隔行和列。 Grid的優勢在于使用三個高度和寬度設定選項指定單元格尺寸。

正如您所見,StackLayout非常适合一維兒童收藏。盡管可以在StackLayout中嵌套StackLayout以容納第二維并模仿表,但結果通常會出現對齊問題。然而,Grid專為二維兒童陣列而設計。正如您将在本章末尾看到的那樣,Grid對于管理适應縱向和橫向模式的布局也非常有用。

基本網格

可以使用代碼或XAML中的子節點定義和填充網格,但XAML方法更容易和更清晰,是以到目前為止更常見。

XAML中的網格

在XAML中定義時,Grid幾乎總是具有固定數量的行和列。 Grid定義通常以兩個重要屬性開頭,名為RowDefinitions(RowDefinition對象的集合)和ColumnDefinitions(ColumnDefinition對象的集合)。 這些集合包含Grid中每行的一個RowDefinition和每列的一個ColumnDefinition,它們定義Grid的行和列特征。

網格可以由單行或單列組成(在這種情況下,它不需要兩個定義集合中的一個),甚至隻需要一個單元格。

RowDefinition具有GridLength類型的Height屬性,ColumnDefinition具有Width屬性,也是GridLength類型。 GridLength結構根據GridUnitType枚舉指定行高或列寬,該枚舉有三個成員:

  • 絕對 - 寬度或高度是與裝置無關的機關中的值(XAML中的數字)
  • 自動 - 寬度或高度根據單元格内容自動調整(XAML中的“自動”)
  • 按比例配置設定星級剩餘寬度或高度(XAML中帶有“*”的數字)

這是SimpleGridDemo項目中XAML檔案的前半部分:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="SimpleGridDemo.SimpleGridDemoPage">
 
    <ContentPage.Padding>
        <OnPlatform x:TypeArguments="Thickness"
                    iOS="0, 20, 0, 0" />
    </ContentPage.Padding>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="100" />
            <RowDefinition Height="2*" />
            <RowDefinition Height="1*" />
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="*" />
        </Grid.ColumnDefinitions>
    __
    </Grid>
</ContentPage>           

該網格有四行兩列。第一行的高度是“自動” - 意味着高度是根據占據第一行的所有元素的最大高度計算的。第二行是100個與裝置無關的機關高度。

使用“*”(發音為“star”)的兩個高度設定需要一些額外的解釋:此特定網格的總高度是頁面的高度減去iOS上的填充設定。在内部,Grid根據該行的内容确定第一行的高度,并且它知道第二行的高度為100.它從它自己的高度中減去這兩個高度,并在第三行中按比例配置設定剩餘高度和第四行基于星形設定中的數字。第三行是第四行高度的兩倍。

兩個ColumnDefinition對象都将Width設定為“”,這與“1 ”相同,這意味着螢幕的寬度在兩列之間平均配置設定。

您将從第14章“絕對布局”中回憶一下,AbsoluteLayout類定義了兩個附加的可綁定屬性和四個靜态Set和Get方法,這些方法允許程式在代碼或XAML中指定AbsoluteLayout子項的位置和大小。

網格非常相似。 Grid類定義了四個附加的可綁定屬性,用于指定Grid的子節點占用的一個或多個單元格:

  • Grid.RowProperty-從零開始的行;預設值為0
  • Grid.ColumnProperty-從零開始的列; 預設值為0
  • Grid.RowSpanProperty - 子跨越的行數; 預設值為1
  • Grid.ColumnSpanProperty - 子跨越的列數; 預設值為1

所有四個屬性都定義為int類型。

例如,要在代碼中指定名為view的Grid子項駐留在特定的行和列中,可以調用:

view.SetValue(Grid.RowProperty, 2);
view.SetValue(Grid.ColumnProperty, 1);           

這些是從零開始的行号和列号,是以将子項配置設定給第三行和第二列。

Grid類還定義了八種靜态方法,用于在代碼中簡化設定和擷取這些屬性:

  • Grid.SetRow 和 Grid.GetRow
  • Grid.SetColumn 和 Grid.GetColumn
  • Grid.SetRowSpan 和 Grid.GetRowSpan
  • Grid.SetColumnSpan 和 Grid.GetColumnSpan

這相當于您剛剛看到的兩個SetValue調用:

Grid.SetRow(view, 2);
Grid.SetColumn(view, 1);           

正如您在學習AbsoluteLayout時所了解的那樣,這些靜态Set和Get方法是使用Grid的子節點上的SetValue和GetValue調用實作的。 例如,以下是如何在Grid類中定義SetRow:

public static void SetRow(BindableObject bindable, int value)
{
     bindable.SetValue(Grid.RowProperty, value);
}           

您無法在XAML中調用這些方法,是以您可以使用以下屬性在Grid的子級上設定附加的可綁定屬性:

  • Grid.Row
  • Grid.Column
  • Grid.RowSpan
  • Grid.ColumnSpan

這些XAML屬性實際上并不是由Grid類定義的,但XAML解析器知道它必須引用Grid定義的關聯附加可綁定屬性。

您不需要在Grid的每個子項上設定所有這些屬性。 如果子節點隻占用一個單元格,則不要設定Grid.RowSpan或Grid.ColumnSpan,因為預設值為1. Grid.Row和Grid.Column屬性的預設值為0,是以您不需要 如果子項占據第一行或第一列,則設定值。 但是,為了清楚起見,本書中的代碼通常會顯示這兩個屬性的設定。 為了節省空間,這些屬性通常會出現在XAML清單的同一行中。

這是SimpleGridDemo的完整XAML檔案:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="SimpleGridDemo.SimpleGridDemoPage">
 
    <ContentPage.Padding>
        <OnPlatform x:TypeArguments="Thickness"
                    iOS="0, 20, 0, 0" />
    </ContentPage.Padding>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="100" />
            <RowDefinition Height="2*" />
            <RowDefinition Height="1*" />
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="*" />
        </Grid.ColumnDefinitions>
        <Label Text="Grid Demo"
               Grid.Row="0" Grid.Column="0"
               FontSize="Large"
               HorizontalOptions="End" />
        <Label Text="Demo the Grid"
               Grid.Row="0" Grid.Column="1"
               FontSize="Small"
               HorizontalOptions="End"
               VerticalOptions="End" />
        <Image BackgroundColor="Gray"
               Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2">
            <Image.Source>
                <OnPlatform x:TypeArguments="ImageSource"
                            iOS="Icon-60.png"
                            Android="icon.png"
                            WinPhone="Assets/StoreLogo.png" />
            </Image.Source>
        </Image>
        <BoxView Color="Green"
                 Grid.Row="2" Grid.Column="0" />
        <BoxView Color="Red"
                 Grid.Row="2" Grid.Column="1" Grid.RowSpan="2" />
        <BoxView Color="Blue"
                 Opacity="0.5"
                 Grid.Row="3" Grid.Column="0" Grid.ColumnSpan="2" />
    </Grid>
</ContentPage>           

具有不同FontSize設定的兩個Label元素占據第一行的兩列。 該行的高度由最高元素控制。 HorizontalOptions和VerticalOptions的設定可以将子項定位在單元格中。

第二行的高度為100個與裝置無關的單元。 該行由顯示具有灰色背景的應用程式圖示的Image元素占用。 Image元素跨越該行的兩列。

底部的兩行由三個BoxView元素占用,一個跨越兩行,另一個跨越兩列,這些在右下角的單元格中重疊:

第十七章:掌握網格(一)基本網格

螢幕截圖确認第一行的大小與大型Label的高度相同;第二行是100個與裝置無關的機關高;并且第三和第四行占據所有剩餘空間。第三排是第四排的兩倍。兩列寬度相等,将整個網格分成兩半。紅色和藍色的BoxView元素在右下角的單元格中重疊,但藍色的BoxView顯然位于紅色的頂部,因為它的不透明度設定為0.5,結果為紫色。

由于白色背景,藍色半透明BoxView的左半部分在iPhone和Windows 10移動裝置上比在Android手機上輕。

如您所見,Grid的子節點可以共享單元格。子項在XAML檔案中出現的順序是将子項放入Grid中的順序,後來的子項看起來就像是在早期子項之上(并且模糊了)。

您會注意到一點間隙似乎将背景透過的行和列分開。這由兩個Grid屬性控制:

RowSpacing-預設值為6

TolumnSpacing-預設值為6

如果要關閉該空間,可以将這些屬性設定為0,如果希望窺視顔色不同,則可以設定Grid的BackgroundColor屬性。您還可以使用網格上的“填充”設定在網格内部圍繞其周邊添加空間。

您現在已經了解了Grid定義的所有公共屬性和方法。

在繼續之前,讓我們用SimpleGridDemo進行幾個實驗。首先,注釋掉或删除網格頂部附近的整個RowDefinitions和ColumnDefinitions部分,然後重新部署該程式。這是你會看到的:

第十七章:掌握網格(一)基本網格

如果未定義自己的RowDefinition和ColumnDefinition對象,則Grid會在将視圖添加到Children集合時自動生成它們。 但是,預設的RowDefinition和ColumnDefinition是“*”(星号),這意味着現在四行平均分為四分之一螢幕,每個單元格占總網格的八分之一。

這是另一個實驗。 恢複RowDefinitions和ColumnDefinitions部分,并将Grid自身的HorizontalOptions和VerticalOptions屬性設定為Center。 預設情況下,這兩個屬性是Fill,這意味着Grid填充其容器。 以下是Center發生的情況:

第十七章:掌握網格(一)基本網格

第三行仍然是底行高度的兩倍,但現在底行的高度基于BoxView的預設HeightRequest,即40。

将Grid放入StackLayout時,您會看到類似的效果。 您還可以将StackLayout放在網格單元格中,或者放入網格單元格中的另一個網格中,但不要使用此技術:嵌套網格和其他布局越深,嵌套布局對性能的影響就越大。

繼續閱讀