第十七章:掌握网格(一)

简介:

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元素占用,一个跨越两行,另一个跨越两列,这些在右下角的单元格中重叠:
2018_09_29_171622
屏幕截图确认第一行的大小与大型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部分,然后重新部署该程序。这是你会看到的:
2018_09_29_171738
如果未定义自己的RowDefinition和ColumnDefinition对象,则Grid会在将视图添加到Children集合时自动生成它们。 但是,默认的RowDefinition和ColumnDefinition是“*”(星号),这意味着现在四行平均分为四分之一屏幕,每个单元格占总网格的八分之一。
这是另一个实验。 恢复RowDefinitions和ColumnDefinitions部分,并将Grid自身的HorizontalOptions和VerticalOptions属性设置为Center。 默认情况下,这两个属性是Fill,这意味着Grid填充其容器。 以下是Center发生的情况:
2018_09_29_171916
第三行仍然是底行高度的两倍,但现在底行的高度基于BoxView的默认HeightRequest,即40。
将Grid放入StackLayout时,您会看到类似的效果。 您还可以将StackLayout放在网格单元格中,或者放入网格单元格中的另一个网格中,但不要使用此技术:嵌套网格和其他布局越深,嵌套布局对性能的影响就越大。

目录
相关文章
|
9月前
技术分享 | ANSYS高级几何处理与网格应用技巧
Space Claim、排油烟机几何处理、流体域抽取;ANSYS Meshing、实际案例网格划分
技术分享 | ANSYS高级几何处理与网格应用技巧
|
人工智能 Kubernetes 监控
谈谈我对服务网格的理解
服务网格作为一种用来管理应用服务通信的基础核心技术,为应用服务间的调用带来了安全、可靠、快速、应用无感知的流量路由、安全、可观测能力。
谈谈我对服务网格的理解
|
Android开发
第二十一章:变换(十四)
3D-ish旋转 即使计算机屏幕是平面和二维的,也可以在这些屏幕上绘制视觉对象,使其具有第三维的外观。 在本章的前面,您看到了一些文本效果,它们提供了第三个维度的提示,而Xamarin.Forms支持两个额外的旋转,名为RotationX和RotationY,它们似乎也突破了屏幕固有的二维平面度。
1344 0
|
JavaScript Android开发 索引
第二十一章:变换(十二)
这两个问题都在非最小的BoxViewClock中得到解决。 XAML文件与MinimalBoxViewClock非常相似,但代码隐藏文件更为广泛。 它以名为HandParams的小结构开始,该结构定义每只手相对于半径的大小,但也包括偏移值。
1062 0
|
Android开发 索引
第二十一章:变换(十)
样式通过将AnchorX值设置为0来结束,该值将旋转中心设置为每个Label的左边缘的垂直中心。 然后每个Label都会获得一个独特的旋转设置:显然,选择“ROTATE”字符串之前的空格,以便R的垂直条组合形成一个看起来几乎像圆的16边多边形。
1019 0
|
JavaScript Android开发 索引
第二十一章:变换(九)
旋转的文字效果轮换很有趣。 旋转动画时更有趣(正如您将在下一章中看到的那样),但即使使用静态图像也很有趣。本章和下一章中的几个旋转示例涉及将视觉元素排列在一个圆圈中,所以让我们首先尝试显示一个简单的圆圈。
841 0
|
Android开发
第二十一章:变换(八)
旋转变换 “旋转”属性旋转屏幕表面上的可视元素。 将“旋转”属性设置为以度为单位的角度(不是弧度)。 正角度顺时针旋转元素。 您可以将“旋转”设置为小于0或大于360的角度。实际旋转角度是旋转属性模数360的值。
818 0
|
Android开发 iOS开发
第二十一章:变换(七)
锚定规模当你尝试使用Scale属性时,你可能已经注意到视觉元素的任何扩展都是从元素的中心向外发生的,如果你将视觉元素缩小到任何东西,它也会向中心收缩。这是另一种思考方式:无论Scale属性的设置如何,视觉元素正中心的点都保持在同一位置。
935 0
|
Android开发 iOS开发 Windows
第二十一章:变换(六)
两个按钮的Clicked处理程序每个都启动一个独立的动画。 第一个Button的Clicked处理程序将其Scale属性从1设置为5并再次返回,而第二个Button的Clicked处理程序将其FontSize属性设置为1到5的比例因子,然后再返回。
940 0
|
JavaScript Android开发
第二十一章:变换(五)
规模变换 VisualElement类定义名为Scale的属性,您可以使用该属性更改元素的呈现大小。 Scale属性不会影响布局(将在ButtonScaler程序中演示)。它不会影响元素的get-only Width和Height属性,也不会影响包含Width和Height值的get-only Bounds属性。
906 0