搞懂Xamarin.Forms布局,看這篇應該就夠了吧


  Xamarin.Forms 布局介紹

  什么是布局?可以簡單的理解為,我們通過將布局元素有效的組織起來,讓屏幕變成我們想要的樣子!

  我們通過畫圖的方式來描述一下Xamarin.Forms的布局。

    小節錨點:

      布局控件之StackLayout

      Xamarin.Forms 中可以C#代碼進行布局

      Xamarin.Forms 的布局方向

      邊距和填充

      八種布局選項

      布局控件之Grid

      布局控件之AbsoluteLayout

      布局控件之ScrollView

      布局控件之RelativeLayout

      布局控件之FlexLayout

      布局壓縮LayoutCompression

  布局控件之StackLayout

  Xamarin.Forms 常用的有 6 種布局控件,我們先選一個最基礎最常用的布局控件 StackLayout 來講解布局。后面的內容再依次介紹其他布局控件。StackLayout 中文翻譯又叫“堆棧布局”,意指在Xamarin.Forms中使用XAML代碼或者是C#代碼,通過一維排列的方式來完成布局。

  我們可以從圖上看到 StackLayout 是從上到下依次排列的,StackLayout 並不復雜,它只是簡單的線性方式排列。如果想創建更復雜的布局,我們可以使用 StackLayout 嵌套的方式來完成布局。

  我們先來演練一個簡單的 StackLayout :

 

XAML 代碼:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:Layout"
             x:Class="Layout.MainPage">


    <StackLayout>
        <StackLayout BackgroundColor="#A8A8A8">
            <Label Text="布局1"/>
        </StackLayout>
        <StackLayout BackgroundColor="#A8A8A8">
            <Label Text="布局2"/>
        </StackLayout>
        <StackLayout BackgroundColor="#A8A8A8">
            <Label Text="布局3"/>
        </StackLayout>
        <StackLayout BackgroundColor="#A8A8A8">
            <Label Text="布局4"/>
        </StackLayout>
        <StackLayout BackgroundColor="#A8A8A8">
            <Label Text="布局5"/>
        </StackLayout>
    </StackLayout>

</ContentPage>
 

  可以看到我們使用 XAML代碼實現了一個簡單的布局,我們將五個StackLayout 嵌套到一個 StackLayout,對每個內嵌的StackLayout設置背景顏色為 #A8A8A8,StackLayout 中再放入一個文本。代碼寫完直接運行就變成了圖上的樣子。因為本人沒有使用Mac電腦,無法打包IOS應用,所以暫時只放 Android 的圖。

 

  Xamarin.Forms 中可以C#代碼進行布局

  實際上在Xamarin.Forms中不止可以通過XAML代碼的方式來布局,我們還可以使用C#代碼來完成布局。

  剛剛演示了XAML方式寫一個簡單的StackLayout布局,本輪演示一個C#代碼布局。

 

 

 C# 代碼:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using Xamarin.Forms;
using Xamarin.Forms.Xaml;

namespace Layout
{
    [XamlCompilation(XamlCompilationOptions.Compile)]
    public partial class CSharpLayout : ContentPage
    {
        public CSharpLayout ()
        {
            InitializeComponent ();

            //最外面的StackLayout
            var stackLayout = new StackLayout();

            //嵌套StackLayout_1
            var stackLayout_1 = new StackLayout() { BackgroundColor = Color.FromHex("#A8A8A8") };
            stackLayout_1.Children.Add(new Label() { Text = "C#編寫布局1" });
            stackLayout.Children.Add(stackLayout_1);

            //嵌套StackLayout_2
            var stackLayout_2 = new StackLayout() { BackgroundColor = Color.FromHex("#A8A8A8") };
            stackLayout_2.Children.Add(new Label() { Text = "C#編寫布局2" });
            stackLayout.Children.Add(stackLayout_2);

            //嵌套StackLayout_3
            var stackLayout_3 = new StackLayout() { BackgroundColor = Color.FromHex("#A8A8A8") };
            stackLayout_3.Children.Add(new Label() { Text = "C#編寫布局3" });
            stackLayout.Children.Add(stackLayout_3);

            //嵌套StackLayout_4
            var stackLayout_4 = new StackLayout() { BackgroundColor = Color.FromHex("#A8A8A8") };
            stackLayout_4.Children.Add(new Label() { Text = "C#編寫布局4" });
            stackLayout.Children.Add(stackLayout_4);

            //嵌套StackLayout_5
            var stackLayout_5 = new StackLayout() { BackgroundColor = Color.FromHex("#A8A8A8") };
            stackLayout_5.Children.Add(new Label() { Text = "C#編寫布局5" });
            stackLayout.Children.Add(stackLayout_5);

            //將StackLayout放入Content
            Content = stackLayout;
        }
    }
}

  我們可以看到,前面用XAML實現的布局,我們用C#代碼同樣可以實現。只是使用C#代碼我們可能會寫更多的代碼而已。從單純布局來說,微軟官方推薦我們使用 XAML 方式布局。那 C# 方式在什么場景下使用呢?我認為我們在實現動態數據綁定效果,或者是頁面控件動態生成時,我們可以使用C#代碼完成。如果你搞過Web開發,就可以理解為寫XAML等於直接寫HTML,寫C#代碼等於用Javascript來生成HTML。

  總的來說,XAML方式布局更簡潔,易讀寫,C#代碼布局更靈活更動態。

 

  Xamarin.Forms 的布局方向

   在Xamarin.Forms中目前支持兩種布局方向:

    Horizontal(水平布局):從左到右布局

    Vertical(垂直布局): 從上到下布局

 

XAML 代碼:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="Layout.LayoutOrientation">
    <ContentPage.Content>
        <StackLayout>

            <StackLayout Orientation="Horizontal" BackgroundColor="#A8A8A8">
                <Label Text="水平"/>
                <Label Text="水平"/>
                <Label Text="水平"/>
            </StackLayout>

            <StackLayout Orientation="Vertical" BackgroundColor="#A8A8A8">
                <Label Text="垂直"/>
                <Label Text="垂直"/>
                <Label Text="垂直"/>
            </StackLayout>

            <StackLayout BackgroundColor="#A8A8A8">
                <Label Text="默認"/>
                <Label Text="默認"/>
                <Label Text="默認"/>
            </StackLayout>

        </StackLayout>
    </ContentPage.Content>
</ContentPage>

 

C# 代碼:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using Xamarin.Forms;
using Xamarin.Forms.Xaml;

namespace Layout
{
    [XamlCompilation(XamlCompilationOptions.Compile)]
    public partial class CSharpLayoutOrientation : ContentPage
    {
        public CSharpLayoutOrientation ()
        {
            InitializeComponent ();

            var stackLayout = new StackLayout();

            //設置以水平方式布局
            var stackLayout_1 = new StackLayout()
            {
                Orientation = StackOrientation.Horizontal,
                BackgroundColor = Color.FromHex("#A8A8A8")
            };
            stackLayout_1.Children.Add(new Label() { Text = "水平" });
            stackLayout_1.Children.Add(new Label() { Text = "水平" });
            stackLayout_1.Children.Add(new Label() { Text = "水平" });

            //設置以垂直方式布局
            var stackLayout_2 = new StackLayout()
            {
                Orientation = StackOrientation.Vertical,
                BackgroundColor = Color.FromHex("#A8A8A8")
            };
            stackLayout_2.Children.Add(new Label() { Text = "垂直" });
            stackLayout_2.Children.Add(new Label() { Text = "垂直" });
            stackLayout_2.Children.Add(new Label() { Text = "垂直" });

            //默認的方式布局
            var stackLayout_3 = new StackLayout()
            {
                BackgroundColor = Color.FromHex("#A8A8A8")
            };
            stackLayout_3.Children.Add(new Label() { Text = "默認" });
            stackLayout_3.Children.Add(new Label() { Text = "默認" });
            stackLayout_3.Children.Add(new Label() { Text = "默認" });


            stackLayout.Children.Add(stackLayout_1);
            stackLayout.Children.Add(stackLayout_2);
            stackLayout.Children.Add(stackLayout_3);

            Content = stackLayout;
        }
    }
}

 

  我們可以看到,我用水平,垂直,默認三種方式來展現了布局方向。

  設置 StackLayout 的 Orientation 屬性為 Horizontal 表示,系統將會以水平方式來布局。

  設置 StackLayout 的 Orientation 屬性為 Vertical 表示,系統將會以垂直方式來布局。

  默認 StackLayout 的 Orientation 屬性為 Horizontal。

 

  邊距和填充

  我們在使用布局的時候,可以通過邊距、填充屬性來控制布局行為。

    Margin:表示一個元素與它相鄰的元素之間的距離。

    Padding:表示一個元素與它的子元素之間的距離。

 

 

 

 

  Margin 和 Padding 都有四個方位的值,分別是 上下左右。

  特別要注意的是,Margin 的值是有累加性的。比如:我們把兩個相鄰元素的Margin值分別設置為 Margin 。

  下面我們用代碼來預覽一下:

  

XAML 代碼:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="Layout.MarginPadding">
    <ContentPage.Content>

        <StackLayout BackgroundColor="#A8A8A8">

            <StackLayout BackgroundColor="#f9d33c" Margin="60,60,60,60" Padding="30,30,30,30">
                <StackLayout BackgroundColor="#91e2f4" WidthRequest="200" HeightRequest="100">
                    <Label Text="Child"/>
                </StackLayout>
            </StackLayout>

            <StackLayout BackgroundColor="#f9d33c" Margin="60,60,60,60" Padding="30,30,30,30">
                <StackLayout BackgroundColor="#91e2f4" WidthRequest="200" HeightRequest="100">
                    <Label Text="Child"/>
                </StackLayout>
            </StackLayout>

        </StackLayout>

    </ContentPage.Content>
</ContentPage>

C# 代碼:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using Xamarin.Forms;
using Xamarin.Forms.Xaml;

namespace Layout
{
    [XamlCompilation(XamlCompilationOptions.Compile)]
    public partial class CSharpMarginPadding : ContentPage
    {
        public CSharpMarginPadding()
        {
            InitializeComponent();

            var stackLayout = new StackLayout() {
                BackgroundColor = Color.FromHex("#A8A8A8")
            };

            var stackLayout_1 = new StackLayout()
            {
                BackgroundColor = Color.FromHex("#f9d33c"),
                Margin = new Thickness(60, 60, 60, 60),
                Padding = new Thickness(30, 30, 30, 30)
            };
            var stackLayout_1_1 = new StackLayout()
            {
                BackgroundColor = Color.FromHex("#91e2f4"),
                WidthRequest = 200,
                HeightRequest = 100
            };
            stackLayout_1_1.Children.Add(new Label() { Text = "Child" });
            stackLayout_1.Children.Add(stackLayout_1_1);
            stackLayout.Children.Add(stackLayout_1);


            var stackLayout_2 = new StackLayout()
            {
                BackgroundColor = Color.FromHex("#f9d33c"),
                Margin = new Thickness(60, 60, 60, 60),
                Padding = new Thickness(30, 30, 30, 30)
            };
            var stackLayout_2_1 = new StackLayout()
            {
                BackgroundColor = Color.FromHex("#91e2f4"),
                WidthRequest = 200,
                HeightRequest = 100
            };
            stackLayout_2_1.Children.Add(new Label() { Text = "Child" });
            stackLayout_2.Children.Add(stackLayout_2_1);
            stackLayout.Children.Add(stackLayout_2);


            Content = stackLayout;

        }
    }
}

我們可以看到 Margin 和 Padding 分別展現的效果。而兩個相鄰元素分別設置 Margin Bottom = 60 ,Margin Top = 60 ,讓兩個元素中間的間距為 120。

我們再進一步,我們來看看 Margin 和 Padding 是什么類型。

 

 

 

從圖片上,我們可以看到,Margin 是屬於 Xamarin.Forms.View 的一個屬性,類型為 Xamarin.Forms.Thickness

 


 

 

從圖片上,我們可以看到,Padding 是屬於 Xamarin.Forms.Layout 的一個屬性,類型為 Xamarin.Forms.Thickness

小結:Xamarin.Forms.Thickness 是一個結構體,我們可以通過該結構體設置元素的上下左右的間距。

 

  八種布局選項

  Xamarin.Forms 中所有的 View 都具有類型為 Xamarin.Forms.LayoutOptions 的 HorizontalOptions 和 VerticalOptions 兩個屬性。

  那么哪些控件是屬於 Xamarin.Forms.View 類型的呢?諸如常用的布局控件 StackLayout,AbsoluteLayout,RelativeLayout ,Grid,FlexLayout,ScrollView 等控件都是直接或間接繼承自 Xamarin.Forms.View 的。

  總之, Xamarin.Forms.LayoutOptions 在 Xamarin.Forms 布局工作中是非常重要的!

 

  Xamarin.Forms.LayoutOptions 說明:

    HorizontalOptions:

      Start:在父元素中以 水平方向靠左 排列

      Center:在父元素中以 水平方向居中 排列

      End:在父元素中以 水平方向靠右 排列

      Fill:在父元素中以 填充方式 排列

      StartAndExpand:在父元素中 將父元素剩余部分填充后 以 水平方向靠左 排列

      CenterAndExpand:在父元素中 將父元素剩余部分填充后 以 水平方向居中 排列

      EndAndExpand:在父元素中 將父元素剩余部分填充后 以 水平方向靠右 排列

      FillAndExpand:在父元素中以 填充方式 排列

    VerticalOptions :

      Start:在父元素中以 垂直方向靠上 排列

      Center:在父元素中以 垂直方向居中 排列

      End:在父元素中以 垂直方向靠下 排列

      Fill:在父元素中以 填充方式 排列

      StartAndExpand:在父元素中 將父元素剩余部分填充后 以 水平方向靠上 排列

      CenterAndExpand:在父元素中 將父元素剩余部分填充后 以 水平方向居中 排列

      EndAndExpand:在父元素中 將父元素剩余部分填充后 以 水平方向靠下 排列

      FillAndExpand:在父元素中以 填充方式 排列

 

  接下來我們用代碼和圖片預覽的方式分別來說明一下:

XAML 代碼:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="Layout.LayoutOptions">
    <ContentPage.Content>
        <StackLayout BackgroundColor="#A8A8A8">

            
            <StackLayout BackgroundColor="#f9d33c">
                <!--Start_1在水平選項最左側位置-->
                <StackLayout BackgroundColor="#91e2f4" HorizontalOptions="Start">
                    <Label Text="Start_1"/>
                </StackLayout>

                <!--Center_1在水平選項中間位置-->
                <StackLayout BackgroundColor="#91e2f4" HorizontalOptions="Center">
                    <Label Text="Center_1"/>
                </StackLayout>

                <!--End_1在水平選項中最右側位置-->
                <StackLayout BackgroundColor="#91e2f4" HorizontalOptions="End">
                    <Label Text="End_1"/>
                </StackLayout>

                <!--Fill_1占滿了整個水平選項-->
                <StackLayout BackgroundColor="#91e2f4" HorizontalOptions="Fill">
                    <Label Text="Fill_1"/>
                </StackLayout>
            </StackLayout>

            <StackLayout BackgroundColor="#f9d33c" Orientation="Horizontal">
                <!--Start_A在水平選項最左側位置-->
                <StackLayout BackgroundColor="#91e2f4" HorizontalOptions="Start">
                    <Label Text="Start_A"/>
                </StackLayout>

                <!--Center_A依次排列-->
                <StackLayout BackgroundColor="#91e2f4" HorizontalOptions="Center">
                    <Label Text="Center_A"/>
                </StackLayout>
            </StackLayout>

            <StackLayout BackgroundColor="#f9d33c" Orientation="Horizontal">
                <!--Start_2在水平選項最左側位置-->
                <StackLayout BackgroundColor="#91e2f4" HorizontalOptions="Start">
                    <Label Text="Start_2"/>
                </StackLayout>

                <!--EndAndExpand_2填充剩余區域后靠最右側顯示-->
                <StackLayout BackgroundColor="#91e2f4" HorizontalOptions="EndAndExpand">
                    <Label Text="EndAndExpand_2"/>
                </StackLayout>
            </StackLayout>

            <StackLayout BackgroundColor="#f9d33c" Orientation="Horizontal">
                <!--Start_3在水平選項最左側位置-->
                <StackLayout BackgroundColor="#91e2f4" HorizontalOptions="Start">
                    <Label Text="Start_3"/>
                </StackLayout>

                <!--CenterAndExpand_3填充剩余區域后再居中顯示-->
                <StackLayout BackgroundColor="#91e2f4" HorizontalOptions="CenterAndExpand">
                    <Label Text="CenterAndExpand_3"/>
                </StackLayout>
            </StackLayout>

            <StackLayout BackgroundColor="#f9d33c" Orientation="Horizontal">
                <!--StartAndExpand_4填充剩余區域后,在最左側顯示-->
                <StackLayout BackgroundColor="#91e2f4" HorizontalOptions="StartAndExpand">
                    <Label Text="StartAndExpand_4"/>
                </StackLayout>

                <!--CenterAndExpand_4填充剩余區域后,再居中顯示-->
                <StackLayout BackgroundColor="#91e2f4" HorizontalOptions="CenterAndExpand">
                    <Label Text="CenterAndExpand_4"/>
                </StackLayout>
            </StackLayout>

            <StackLayout BackgroundColor="#f9d33c" Orientation="Horizontal">
                <!--CenterAndExpand_5_1填充剩余區域后,再居中顯示-->
                <StackLayout BackgroundColor="#91e2f4" HorizontalOptions="CenterAndExpand">
                    <Label Text="CenterAndExpand_5_1"/>
                </StackLayout>

                <!--CenterAndExpand_5_2填充剩余區域后,再居中顯示-->
                <StackLayout BackgroundColor="#91e2f4" HorizontalOptions="CenterAndExpand">
                    <Label Text="CenterAndExpand_5_2"/>
                </StackLayout>
            </StackLayout>

            <StackLayout BackgroundColor="#f9d33c" Orientation="Horizontal">
                <!--FillAndExpand_6填充整個剩余部分-->
                <StackLayout BackgroundColor="#91e2f4" HorizontalOptions="FillAndExpand">
                    <Label Text="FillAndExpand_6"/>
                </StackLayout>
            </StackLayout>
            
            <StackLayout BackgroundColor="#f9d33c" VerticalOptions="FillAndExpand">
                <!--VerticalOptions=CenterAndExpand垂直方向填充居中顯示-->
                <StackLayout BackgroundColor="#91e2f4" VerticalOptions="CenterAndExpand">
                    <Label Text="VerticalOptions=CenterAndExpand"/>
                </StackLayout>
                <!--VerticalOptions=EndAndExpand垂直方向填充靠下顯示-->
                <StackLayout BackgroundColor="#91e2f4" VerticalOptions="EndAndExpand">
                    <Label Text="VerticalOptions=EndAndExpand"/>
                </StackLayout>
            </StackLayout>

        </StackLayout>
    </ContentPage.Content>
</ContentPage>

  可以看到:

    1. 在默認的布局方向(默認為垂直方向)中,加入 Start_1,Center_1,End_1,Fill_1。分別表現出 Start_1在水平選項最左側位置,Center_1在水平選項中間位置,End_1在水平選項中最右側位置,而Fill_1占滿了整個水平選項。

    2. 在布局方式為水平方向的布局中,加入 Start_A,Center_A。表現出 Start_A,Center_A 依次從左到右排列。

    3. 在布局方式為水平方向的布局中,加入 Start_2,EndAndExpand_2。表現出,Start_2 在布局最左側,而 EndAndExpand_2 將布局剩余部分填滿后靠最右側顯示。

    4. 在布局方式為水平方向的布局中,加入 Start_3,CenterAndExpand_3。表現出,Start_3 在布局最左側,而 CenterAndExpand_3 將布局剩余部分填滿后居中顯示顯示。

    5. 在布局方式為水平方向的布局中,加入 StartAndExpand_4,CenterAndExpand_4。表現出,StartAndExpand_4 將布局余部分填滿后在布局最左側顯示,而 CenterAndExpand_4 將布局剩余部分填滿后居中顯示。

    6. 在布局方式為水平方向的布局中,加入 CenterAndExpand_5_1,CenterAndExpand_5_2。表現出,CenterAndExpand_5_1 和 CenterAndExpand_5_2 都將布局剩余部分填滿后居中顯示。

    7. 在布局方式為水平方向的布局中,加入 FillAndExpand_6。表現出,FillAndExpand_6 都將布局剩余部分填滿后填充顯示(Fill后已經沒有剩余部分了)。

    8. 在布局方式為垂直方向的布局中,加入 VerticalOptions=CenterAndExpand 。表現出,VerticalOptions=CenterAndExpand垂直方向填充居中顯示。

    9. 在布局方式為垂直方向的布局中,加入 VerticalOptions=EndAndExpand。表現出,VerticalOptions=EndAndExpand垂直方向填充靠下顯示。

 

  布局控件之Grid

  Grid 中文翻譯又叫“網格布局”,意指在Xamarin.Forms中使用XAML代碼或者是C#代碼,通過網格的方式來完成布局。可以理解為 Excel 中 N行N列的方式來布局。

 

  我們可以看到Grid布局,是以行和列,以單元格的方式進行布局。行和列的索引只從0開始,讓我們先來演練一個簡單的 Grid :

 

XAML 代碼:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="Layout.Grid">
    <ContentPage.Content>
        <!--定義網格行間隙為2,列間隙為2-->
        <Grid RowSpacing="2" ColumnSpacing="2" BackgroundColor="#A8A8A8">
            
            <!--定義一個5行,4列的表格-->
            <Grid.RowDefinitions>
                <!--設置行高為100-->
                <RowDefinition Height="100"></RowDefinition>
                <!--減去行高100后,按照1/10高度(此網格定義了10)計算-->
                <RowDefinition Height="*"></RowDefinition>
                <RowDefinition Height="2*"></RowDefinition>
                <!--減去行高100后,按照3/10高度(此網格定義了10)計算-->
                <RowDefinition Height="3*"></RowDefinition>
                <!--減去行高100后,按照4/10高度(此網格定義了10)計算-->
                <RowDefinition Height="4*"></RowDefinition>
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
                <!--按照1/5寬度(此網格定義了5)計算-->
                <ColumnDefinition Width="*"></ColumnDefinition>
                <ColumnDefinition Width="*"></ColumnDefinition>
                <!--按照2/5寬度(此網格定義了5)計算-->
                <ColumnDefinition Width="2*"></ColumnDefinition>
                <ColumnDefinition Width="*"></ColumnDefinition>
            </Grid.ColumnDefinitions>

            <!--設置元素在網格的第0行0列-->
            <StackLayout BackgroundColor="#91e2f4" Grid.Row="0" Grid.Column="0">
                <Label Text="第0行0列"/>
            </StackLayout>
            <!--設置元素在網格的第0行1列,跨2列-->
            <StackLayout BackgroundColor="#91e2f4" Grid.Row="0" Grid.Column="1" Grid.ColumnSpan="2">
                <Label Text="第0行1列,跨2列"/>
            </StackLayout>
            <StackLayout BackgroundColor="#91e2f4" Grid.Row="0" Grid.Column="3">
                <Label Text="第0行3列"/>
            </StackLayout>
            <StackLayout BackgroundColor="#91e2f4" Grid.Row="1" Grid.Column="0">
                <Label Text="第1行0列"/>
            </StackLayout>
            <StackLayout BackgroundColor="#91e2f4" Grid.Row="1" Grid.Column="1" Grid.ColumnSpan="3">
                <Label Text="第1行1列,跨3列"/>
            </StackLayout>
            <StackLayout BackgroundColor="#91e2f4" Grid.Row="2" Grid.Column="0">
                <Label Text="第2行0列"/>
            </StackLayout>
            <StackLayout BackgroundColor="#91e2f4" Grid.Row="2" Grid.Column="1">
                <Label Text="第2行1列"/>
            </StackLayout>
            <StackLayout BackgroundColor="#91e2f4" Grid.Row="2" Grid.Column="2">
                <Label Text="第2行2列"/>
            </StackLayout>
            <StackLayout BackgroundColor="#91e2f4" Grid.Row="2" Grid.Column="3">
                <Label Text="第2行3列"/>
            </StackLayout>
            <StackLayout BackgroundColor="#91e2f4" Grid.Row="3" Grid.Column="0">
                <Label Text="第3行0列"/>
            </StackLayout>
            <StackLayout BackgroundColor="#91e2f4" Grid.Row="3" Grid.Column="1">
                <Label Text="第3行1列"/>
            </StackLayout>
            <!--設置元素在網格的第3行2列,跨2行-->
            <StackLayout BackgroundColor="#91e2f4" Grid.Row="3" Grid.Column="2" Grid.RowSpan="2">
                <Label Text="第3行2列,跨2行"/>
            </StackLayout>
            <StackLayout BackgroundColor="#91e2f4" Grid.Row="3" Grid.Column="3">
                <Label Text="第3行3列"/>
            </StackLayout>
            <StackLayout BackgroundColor="#91e2f4" Grid.Row="4" Grid.Column="0">
                <Label Text="第4行0列"/>
            </StackLayout>
            <StackLayout BackgroundColor="#91e2f4" Grid.Row="4" Grid.Column="1">
                <Label Text="第4行1列"/>
            </StackLayout>
            <StackLayout BackgroundColor="#91e2f4" Grid.Row="4" Grid.Column="3">
                <Label Text="第4行3列"/>
            </StackLayout>
        </Grid>
    </ContentPage.Content>
</ContentPage>

C# 代碼:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using Xamarin.Forms;
using Xamarin.Forms.Xaml;

namespace Layout
{
    [XamlCompilation(XamlCompilationOptions.Compile)]
    public partial class CSharpGrid : ContentPage
    {
        public CSharpGrid ()
        {
            InitializeComponent ();

            //定義網格行間隙為2,列間隙為2
            var grid = new Xamarin.Forms.Grid()
            {
                RowSpacing = 2,
                ColumnSpacing = 2,
                BackgroundColor = Color.FromHex("#A8A8A8")
            };

            /*
             * 定義一個5行,4列的表格
             */

            //設置行高為100
            grid.RowDefinitions.Add(new RowDefinition() { Height = 100 });
            //減去行高100后,按照1/10高度(此網格定義了10)計算
            grid.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(1, GridUnitType.Star) });
            //減去行高100后,按照2/10高度(此網格定義了10)計算
            grid.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(2, GridUnitType.Star) });
            //減去行高100后,按照3/10高度(此網格定義了10)計算
            grid.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(3, GridUnitType.Star) });
            //減去行高100后,按照4/10高度(此網格定義了10)計算
            grid.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(4, GridUnitType.Star) });

            //按照1/5寬度(此網格定義了5)計算
            grid.ColumnDefinitions.Add(new ColumnDefinition() { Width = new GridLength(1, GridUnitType.Star) });
            grid.ColumnDefinitions.Add(new ColumnDefinition() { Width = new GridLength(1, GridUnitType.Star) });
            //按照2/5寬度(此網格定義了5)計算
            grid.ColumnDefinitions.Add(new ColumnDefinition() { Width = new GridLength(2, GridUnitType.Star) });
            grid.ColumnDefinitions.Add(new ColumnDefinition() { Width = new GridLength(1, GridUnitType.Star) });

            //設置元素在網格的第0行0列
            var s_0_0 = new StackLayout() { BackgroundColor = Color.FromHex("#91e2f4") };
            s_0_0.Children.Add(new Label() { Text = "第0行0列" });
            grid.Children.Add(s_0_0);
            Xamarin.Forms.Grid.SetRow(s_0_0, 0);
            Xamarin.Forms.Grid.SetColumn(s_0_0, 0);


            //設置元素在網格的第0行1列,跨2列
            var s_0_1 = new StackLayout() { BackgroundColor = Color.FromHex("#91e2f4") };
            s_0_1.Children.Add(new Label() { Text = "第0行1列,跨2列" });
            grid.Children.Add(s_0_1);
            Xamarin.Forms.Grid.SetRow(s_0_1, 0);
            Xamarin.Forms.Grid.SetColumn(s_0_1, 1);
            Xamarin.Forms.Grid.SetColumnSpan(s_0_1, 2);

            var s_0_3 = new StackLayout() { BackgroundColor = Color.FromHex("#91e2f4") };
            s_0_3.Children.Add(new Label() { Text = "第0行3列" });
            grid.Children.Add(s_0_3);
            Xamarin.Forms.Grid.SetRow(s_0_3, 0);
            Xamarin.Forms.Grid.SetColumn(s_0_3, 3);

            var s_1_0 = new StackLayout() { BackgroundColor = Color.FromHex("#91e2f4") };
            s_1_0.Children.Add(new Label() { Text = "第1行0列" });
            grid.Children.Add(s_1_0);
            Xamarin.Forms.Grid.SetRow(s_1_0, 1);
            Xamarin.Forms.Grid.SetColumn(s_1_0, 0);

            var s_1_1 = new StackLayout() { BackgroundColor = Color.FromHex("#91e2f4") };
            s_1_1.Children.Add(new Label() { Text = "第1行1列,跨3列" });
            grid.Children.Add(s_1_1);
            Xamarin.Forms.Grid.SetRow(s_1_1, 1);
            Xamarin.Forms.Grid.SetColumn(s_1_1, 1);
            Xamarin.Forms.Grid.SetColumnSpan(s_1_1, 3);

            var s_2_0 = new StackLayout() { BackgroundColor = Color.FromHex("#91e2f4") };
            s_2_0.Children.Add(new Label() { Text = "第2行0列" });
            grid.Children.Add(s_2_0);
            Xamarin.Forms.Grid.SetRow(s_2_0, 2);
            Xamarin.Forms.Grid.SetColumn(s_2_0, 0);

            var s_2_1 = new StackLayout() { BackgroundColor = Color.FromHex("#91e2f4") };
            s_2_1.Children.Add(new Label() { Text = "第2行1列" });
            grid.Children.Add(s_2_1);
            Xamarin.Forms.Grid.SetRow(s_2_1, 2);
            Xamarin.Forms.Grid.SetColumn(s_2_1, 1);

            var s_2_2 = new StackLayout() { BackgroundColor = Color.FromHex("#91e2f4") };
            s_2_2.Children.Add(new Label() { Text = "第2行2列" });
            grid.Children.Add(s_2_2);
            Xamarin.Forms.Grid.SetRow(s_2_2, 2);
            Xamarin.Forms.Grid.SetColumn(s_2_2, 2);

            var s_2_3 = new StackLayout() { BackgroundColor = Color.FromHex("#91e2f4") };
            s_2_3.Children.Add(new Label() { Text = "第2行2列" });
            grid.Children.Add(s_2_3);
            Xamarin.Forms.Grid.SetRow(s_2_3, 2);
            Xamarin.Forms.Grid.SetColumn(s_2_3, 3);

            var s_3_0 = new StackLayout() { BackgroundColor = Color.FromHex("#91e2f4") };
            s_3_0.Children.Add(new Label() { Text = "第3行0列" });
            grid.Children.Add(s_3_0);
            Xamarin.Forms.Grid.SetRow(s_3_0, 3);
            Xamarin.Forms.Grid.SetColumn(s_3_0, 0);

            var s_3_1 = new StackLayout() { BackgroundColor = Color.FromHex("#91e2f4") };
            s_3_1.Children.Add(new Label() { Text = "第3行1列" });
            grid.Children.Add(s_3_1);
            Xamarin.Forms.Grid.SetRow(s_3_1, 3);
            Xamarin.Forms.Grid.SetColumn(s_3_1, 1);

            //第3行2列,跨2行
            var s_3_2 = new StackLayout() { BackgroundColor = Color.FromHex("#91e2f4") };
            s_3_2.Children.Add(new Label() { Text = "第3行2列,跨2行" });
            grid.Children.Add(s_3_2);
            Xamarin.Forms.Grid.SetRow(s_3_2, 3);
            Xamarin.Forms.Grid.SetColumn(s_3_2, 2);
            Xamarin.Forms.Grid.SetRowSpan(s_3_2, 2);

            var s_3_3 = new StackLayout() { BackgroundColor = Color.FromHex("#91e2f4") };
            s_3_3.Children.Add(new Label() { Text = "第3行3列" });
            grid.Children.Add(s_3_3);
            Xamarin.Forms.Grid.SetRow(s_3_3, 3);
            Xamarin.Forms.Grid.SetColumn(s_3_3, 3);


            var s_4_0 = new StackLayout() { BackgroundColor = Color.FromHex("#91e2f4") };
            s_4_0.Children.Add(new Label() { Text = "第4行0列" });
            grid.Children.Add(s_4_0);
            Xamarin.Forms.Grid.SetRow(s_4_0, 4);
            Xamarin.Forms.Grid.SetColumn(s_4_0, 0);

            var s_4_1 = new StackLayout() { BackgroundColor = Color.FromHex("#91e2f4") };
            s_4_1.Children.Add(new Label() { Text = "第4行1列" });
            grid.Children.Add(s_4_1);
            Xamarin.Forms.Grid.SetRow(s_4_1, 4);
            Xamarin.Forms.Grid.SetColumn(s_4_1, 1);

            var s_4_3 = new StackLayout() { BackgroundColor = Color.FromHex("#91e2f4") };
            s_4_3.Children.Add(new Label() { Text = "第4行3列" });
            grid.Children.Add(s_4_3);
            Xamarin.Forms.Grid.SetRow(s_4_3, 4);
            Xamarin.Forms.Grid.SetColumn(s_4_3, 3);

            
            Content = grid;
        }
    }
}

 

可以看到:

  1. RowSpacing 能設置網格 行間隙,ColumnSpacing 能設置網格 列間隙

  2. Grid 中定義行的方式為 Grid.RowDefinitions,具體定義行為 RowDefinition 。當設置 RowDefinition.Height 為一個數字時,該行高度為該數字,當設置 RowDefinition.Height 為 N*時,該行高度為 (總高度-指定數字高度) / 總星數 * N。 

  3. Grid 中定義列的方式為 Grid.ColumnDefinitions,具體定義行為 ColumnDefinition 。當設置 ColumnDefinition.WIdth 為一個數字時,該行寬度為該數字,當設置 ColumnDefinition.Width 為 N*時,該行寬度為 (總寬度-指定數字寬度) / 總星數 * N。 

  4. 我們可以通過設置 Height 或 Width 為 * 時 ,將 Height 或 Width 均等分布局。

  5. 在元素上指定 Grid.Row 或 Grid.Column 時,能將元素對應到網格的具體位置。

  6. 在元素上指定 Grid.ColumnSpan 或 Grid.RowSpan 時,能設置元素的是否跨行或跨列,跨列數。

 

  布局控件之AbsoluteLayout

  AbsoluteLayout 中文翻譯又叫“絕對布局”,意指在Xamarin.Forms中我們通過 AbsoluteLayout 能在視圖上直接指定 元素的坐標(X,Y)和高寬,並繪制成矩形的形狀進行布局。

  

  AbsoluteLayout  並不復雜,它是直接通過在視圖上給定起始位置布局。如果這句話不太容易理解,我們馬上用代碼預覽效果來理解一下:

 

XAML 代碼:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="Layout.AbsoluteLayout">
    <ContentPage.Content>

        <AbsoluteLayout>

            <!--LayoutBounds X,Y,Width,Height 為比例(最大1)-->
            <StackLayout BackgroundColor="#f9d33c" 
                         AbsoluteLayout.LayoutBounds="0,0,0.3,0.1" 
                         AbsoluteLayout.LayoutFlags="All">
                <Label Text="0,0,0.3,0.1,All"/>
            </StackLayout>

            <!--LayoutBounds X,Y,Width,Height 為絕對值-->
            <StackLayout BackgroundColor="#f9d33c" 
                         AbsoluteLayout.LayoutBounds="100,100,150,25" 
                         AbsoluteLayout.LayoutFlags="None">
                <Label Text="100,100,150,25,None"/>
            </StackLayout>

            <!--LayoutBounds Width 為比例,X,Y,Height 為絕對值-->
            <StackLayout BackgroundColor="#f9d33c" 
                         AbsoluteLayout.LayoutBounds="100,130,0.7,30" 
                         AbsoluteLayout.LayoutFlags="WidthProportional">
                <Label Text="100,130,0.7,30,WidthProportional"/>
            </StackLayout>

            <!--LayoutBounds Height為比例,X,Y,Width 為絕對值-->
            <StackLayout BackgroundColor="#f9d33c" 
                         AbsoluteLayout.LayoutBounds="20,180,260,0.05" 
                         AbsoluteLayout.LayoutFlags="HeightProportional">
                <Label Text="20,180,260,0.05,HeightProportional"/>
            </StackLayout>

            <!--LayoutBounds X 為比例,Y,Width,Height 為絕對值-->
            <StackLayout BackgroundColor="#f9d33c" 
                         AbsoluteLayout.LayoutBounds="0.6,250,330,30" 
                         AbsoluteLayout.LayoutFlags="XProportional">
                <Label Text="0.6,250,330,30,XProportional"/>
            </StackLayout>

            <!--LayoutBounds Y 為比例,X,Width,Height 為絕對值-->
            <StackLayout BackgroundColor="#f9d33c" 
                         AbsoluteLayout.LayoutBounds="0,0.45,260,30" 
                         AbsoluteLayout.LayoutFlags="YProportional">
                <Label Text="0,0.45,260,30,YProportional"/>
            </StackLayout>

            <!--LayoutBounds X,Y 為比例,Width,Height 為絕對值-->
            <StackLayout BackgroundColor="#f9d33c" 
                         AbsoluteLayout.LayoutBounds="0.01,0.5,260,30" 
                         AbsoluteLayout.LayoutFlags="PositionProportional">
                <Label Text="0.01,0.5,260,30,PositionProportional"/>
            </StackLayout>

            <!--LayoutBounds Width,Height 為比例,X,Y 為絕對值-->
            <StackLayout BackgroundColor="#f9d33c" 
                         AbsoluteLayout.LayoutBounds="10,400,0.5,0.05" 
                         AbsoluteLayout.LayoutFlags="SizeProportional">
                <Label Text="10,400,0.5,0.05,SizeProportional"/>
            </StackLayout>

            <!--AbsoluteLayout可以重疊,先Render的被后Render的元素覆蓋-->
            <StackLayout BackgroundColor="Red" 
                         AbsoluteLayout.LayoutBounds="10,450,0.5,0.05" 
                         AbsoluteLayout.LayoutFlags="SizeProportional">
                <Label Text="AbsoluteLayout可以重疊"/>
            </StackLayout>
            <StackLayout BackgroundColor="Blue" 
                         AbsoluteLayout.LayoutBounds="10,460,0.5,0.05" 
                         AbsoluteLayout.LayoutFlags="SizeProportional">
                <Label Text="AbsoluteLayout可以重疊"/>
            </StackLayout>

        </AbsoluteLayout>
        
    </ContentPage.Content>
</ContentPage>

 C# 代碼:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using Xamarin.Forms;
using Xamarin.Forms.Xaml;

namespace Layout
{
    [XamlCompilation(XamlCompilationOptions.Compile)]
    public partial class CSharpAbsoluteLayout : ContentPage
    {
        public CSharpAbsoluteLayout ()
        {
            InitializeComponent ();

            var absoluteLayout = new Xamarin.Forms.AbsoluteLayout();

            //LayoutBounds X,Y,Width,Height 為比例(最大1)
            var stackLayout_1 = new Xamarin.Forms.StackLayout();
            stackLayout_1.BackgroundColor = Color.FromHex("#f9d33c");
            stackLayout_1.Children.Add(new Label() { Text = "0,0,0.3,0.1,All" });
            Xamarin.Forms.AbsoluteLayout.SetLayoutBounds(stackLayout_1, new Rectangle(0, 0, 0.3, 0.1));
            Xamarin.Forms.AbsoluteLayout.SetLayoutFlags(stackLayout_1, AbsoluteLayoutFlags.All);
            absoluteLayout.Children.Add(stackLayout_1);

            //LayoutBounds X,Y,Width,Height 為絕對值
            var stackLayout_2 = new Xamarin.Forms.StackLayout();
            stackLayout_2.BackgroundColor = Color.FromHex("#f9d33c");
            stackLayout_2.Children.Add(new Label() { Text = "100,100,150,25,None" });
            Xamarin.Forms.AbsoluteLayout.SetLayoutBounds(stackLayout_2, new Rectangle(100, 100, 150, 25));
            Xamarin.Forms.AbsoluteLayout.SetLayoutFlags(stackLayout_2, AbsoluteLayoutFlags.None);
            absoluteLayout.Children.Add(stackLayout_2);

            //LayoutBounds Width 為比例,X,Y,Height 為絕對值
            var stackLayout_3 = new Xamarin.Forms.StackLayout();
            stackLayout_3.BackgroundColor = Color.FromHex("#f9d33c");
            stackLayout_3.Children.Add(new Label() { Text = "100,130,0.7,30,WidthProportional" });
            Xamarin.Forms.AbsoluteLayout.SetLayoutBounds(stackLayout_3, new Rectangle(100, 130, 0.7, 30));
            Xamarin.Forms.AbsoluteLayout.SetLayoutFlags(stackLayout_3, AbsoluteLayoutFlags.WidthProportional);
            absoluteLayout.Children.Add(stackLayout_3);

            //LayoutBounds Height為比例,X,Y,Width 為絕對值
            var stackLayout_4 = new Xamarin.Forms.StackLayout();
            stackLayout_4.BackgroundColor = Color.FromHex("#f9d33c");
            stackLayout_4.Children.Add(new Label() { Text = "20,180,260,0.05,HeightProportional" });
            Xamarin.Forms.AbsoluteLayout.SetLayoutBounds(stackLayout_4, new Rectangle(20, 180, 260, 0.05));
            Xamarin.Forms.AbsoluteLayout.SetLayoutFlags(stackLayout_4, AbsoluteLayoutFlags.HeightProportional);
            absoluteLayout.Children.Add(stackLayout_4);

            //LayoutBounds X 為比例,Y,Width,Height 為絕對值
            var stackLayout_5 = new Xamarin.Forms.StackLayout();
            stackLayout_5.BackgroundColor = Color.FromHex("#f9d33c");
            stackLayout_5.Children.Add(new Label() { Text = "0.6,250,330,30,XProportional" });
            Xamarin.Forms.AbsoluteLayout.SetLayoutBounds(stackLayout_5, new Rectangle(0.6, 250, 330, 30));
            Xamarin.Forms.AbsoluteLayout.SetLayoutFlags(stackLayout_5, AbsoluteLayoutFlags.XProportional);
            absoluteLayout.Children.Add(stackLayout_5);

            //LayoutBounds Y 為比例,X,Width,Height 為絕對值
            var stackLayout_6 = new Xamarin.Forms.StackLayout();
            stackLayout_6.BackgroundColor = Color.FromHex("#f9d33c");
            stackLayout_6.Children.Add(new Label() { Text = "0,0.45,260,30,YProportional" });
            Xamarin.Forms.AbsoluteLayout.SetLayoutBounds(stackLayout_6, new Rectangle(0, 0.45, 260, 30));
            Xamarin.Forms.AbsoluteLayout.SetLayoutFlags(stackLayout_6, AbsoluteLayoutFlags.YProportional);
            absoluteLayout.Children.Add(stackLayout_6);

            //LayoutBounds X,Y 為比例,Width,Height 為絕對值
            var stackLayout_7 = new Xamarin.Forms.StackLayout();
            stackLayout_7.BackgroundColor = Color.FromHex("#f9d33c");
            stackLayout_7.Children.Add(new Label() { Text = "0.01,0.5,260,30,PositionProportional" });
            Xamarin.Forms.AbsoluteLayout.SetLayoutBounds(stackLayout_7, new Rectangle(0.01, 0.5, 260, 30));
            Xamarin.Forms.AbsoluteLayout.SetLayoutFlags(stackLayout_7, AbsoluteLayoutFlags.PositionProportional);
            absoluteLayout.Children.Add(stackLayout_7);

            //LayoutBounds Width,Height 為比例,X,Y 為絕對值
            var stackLayout_8 = new Xamarin.Forms.StackLayout();
            stackLayout_8.BackgroundColor = Color.FromHex("#f9d33c");
            stackLayout_8.Children.Add(new Label() { Text = "10,400,0.5,0.05,SizeProportional" });
            Xamarin.Forms.AbsoluteLayout.SetLayoutBounds(stackLayout_8, new Rectangle(10, 400, 0.5, 0.05));
            Xamarin.Forms.AbsoluteLayout.SetLayoutFlags(stackLayout_8, AbsoluteLayoutFlags.SizeProportional);
            absoluteLayout.Children.Add(stackLayout_8);

            //AbsoluteLayout可以重疊,先Render的被后Render的元素覆蓋
            var stackLayout_9 = new Xamarin.Forms.StackLayout();
            stackLayout_9.BackgroundColor = Color.Red;
            stackLayout_9.Children.Add(new Label() { Text = "AbsoluteLayout可以重疊" });
            Xamarin.Forms.AbsoluteLayout.SetLayoutBounds(stackLayout_9, new Rectangle(10, 450, 0.5, 0.05));
            Xamarin.Forms.AbsoluteLayout.SetLayoutFlags(stackLayout_9, AbsoluteLayoutFlags.SizeProportional);
            absoluteLayout.Children.Add(stackLayout_9);

            var stackLayout_10 = new Xamarin.Forms.StackLayout();
            stackLayout_10.BackgroundColor = Color.Blue;
            stackLayout_10.Children.Add(new Label() { Text = "AbsoluteLayout可以重疊" });
            Xamarin.Forms.AbsoluteLayout.SetLayoutBounds(stackLayout_10, new Rectangle(10, 460, 0.5, 0.05));
            Xamarin.Forms.AbsoluteLayout.SetLayoutFlags(stackLayout_10, AbsoluteLayoutFlags.SizeProportional);
            absoluteLayout.Children.Add(stackLayout_10);

            Content = absoluteLayout;

        }
    }
}

 

  絕對布局有兩個關鍵屬性,分別是 AbsoluteLayout.LayoutBounds 和 AbsoluteLayout.LayoutFlags 。

  AbsoluteLayout.LayoutBounds 返回的類型為 Xamarin.Forms.Rectangle ,那 Rectangle 是什么呢?翻譯過來就是 “矩形”,意思是,我們要布局一個什么樣的矩形。F12轉到代碼定義上看 Rectangle 4個重要的屬性。

 

  Rectangle 4個重要的屬性:

    X:繪制矩形的起始位置X坐標。(屏幕最左上角為 X = 0 , Y = 0)

    Y:繪制矩形的起始位置Y坐標。

    Width:需要繪制的矩形的寬度。

    Height:需要繪制的矩形的高度。

 

  來看看代碼上 AbsoluteLayout.LayoutBounds="10,450,0.5,0.05"  是什么意思呢? 10為X的值,450為Y的值,0.5為Width的值,0.05為Height的值。構造函數為:

public Rectangle(double x, double y, double width, double height);

 

  AbsoluteLayout.LayoutFlags 定義了 AbsoluteLayout.LayoutBounds 如何來解析,解析的方式分為 比例解析(比例最大為1)絕對值解析。 

  AbsoluteLayout.LayoutFlags 是一個枚舉,枚舉的值分別是:

    All:X,Y,Width,Height 都通過比例進行解析。

    None:X,Y,Width,Height 都通過絕對值進行解析。

    WidthProportional:Width 通過比例進行解析;X,Y,Height 通過絕對值解析。

    HeightProportional:Height 通過比例進行解析;X,Y,Width 通過絕對值解析。

    XProportional:X 通過比例進行解析;Y,Width,Height 通過絕對值解析。

    YProportional:Y 通過比例進行解析;X,Width,Height 通過絕對值解析。

    PositionProportional:X,Y 通過比例進行解析;Width,Height 通過絕對值解析。

    SizeProportionalWidth,Height 通過比例進行解析;X,Y 通過絕對值解析。

  AbsoluteLayout可以重疊,先Render的被后Render的元素覆蓋,我們可以通過Blue元素,覆蓋Red元素觀察到。

 

  布局控件之ScrollView

   ScrollView 中文翻譯又叫“滾動視圖”,意指在Xamarin.Forms中我們通過 ScrollView 能創建滾動到屏幕外的視圖,不好理解沒關系,往下看就對了。

  我們可以從圖上看到 ScrollView 顏色范圍超出了屏幕,類似於我們用App某個頁面,一屏顯示不完,需要上下滑動或左右滑動觀看。

  我們先來演練一個簡單的 ScrollView :

 

XAML 代碼:

 

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="Layout.ScrollView">
    <ContentPage.Content>

        <ScrollView x:Name="scrollView" Orientation="Vertical" VerticalScrollBarVisibility="Never">
            <StackLayout>
                <StackLayout BackgroundColor="Red" HeightRequest="500"></StackLayout>
                <StackLayout BackgroundColor="Yellow" HeightRequest="500"></StackLayout>
                <StackLayout BackgroundColor="Blue" HeightRequest="500"></StackLayout>
                <StackLayout BackgroundColor="Green" HeightRequest="500">
                    <Button x:Name="button" Text="回到頂部(X,Y)" Clicked="Button_Clicked" VerticalOptions="EndAndExpand"/>
                </StackLayout>
            </StackLayout>
        </ScrollView>
    </ContentPage.Content>
</ContentPage>

 

C# 代碼:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using Xamarin.Forms;
using Xamarin.Forms.Xaml;

namespace Layout
{
    [XamlCompilation(XamlCompilationOptions.Compile)]
    public partial class ScrollView : ContentPage
    {
        public ScrollView ()
        {
            InitializeComponent ();

            scrollView.Scrolled += (object sender, ScrolledEventArgs e) =>
            {
                button.Text = string.Format("回到頂部({0},{1})", e.ScrollX, e.ScrollY);
            };

        }

        private void Button_Clicked(object sender, EventArgs e)
        {
            scrollView.ScrollToAsync(0, 0, true);
        }
    }
}

 

  從代碼運行起來后可以看到,我們定義了一個 ScrollView 在 ScrollView 中加入了 Red,Yellow,Blue,Green的總高度超出屏幕外的布局。通過手勢往下拖動,我們可以看到 “回到頂部” 按鈕的Text文本在不斷的跟隨當前滑動的位置變化。當我們點擊 “回到頂部” 按鈕,滾動視圖又回到了頂部位置。。

  接下來,我們來詳細了解一下 ScrollView 的使用方式。

 

屬性

ScrollY:獲取當前 ScrollView 滾動位置的坐標 Y 值。

ScrollX:獲取當前 ScrollView 滾動位置的坐標 X 值。

Orientation:設置 ScrollView 滾動的方向。Orientation 的類型為枚舉 Xamarin.Forms.ScrollOrientation;值 Vertical 表示只以垂直方式滾動,不允許以水平方式滾動。值 Horizontal 表示只以水平方式滾動,不允許以垂直方式滾動。值 Both 表示可以通過垂直和水平方式滾動。

VerticalScrollBarVisibility:設置 ScrollView 在垂直方向的滾動條是否可見。VerticalScrollBarVisibility 的類型為枚舉 Xamarin.Forms.ScrollBarVisibility;值 Default 表示垂直滾動條是否顯示,需要根據平台本身默認的設置而定;值 Always 表示垂直滾動條總是顯示。值 Never 表示垂直滾動條總是不顯示。

HorizontalScrollBarVisibility:設置 ScrollView 在水平方向的滾動條是否可見。HorizontalScrollBarVisibility 的類型同樣也是 Xamarin.Forms.ScrollBarVisibility。

 

方法

ScrollToAsync:將 ScrollView 直接定位到一個(x,y)的位置或定位到一個元素的位置。 

ScrollToAsync(Element element, ScrollToPosition position, bool animated);
ScrollToAsync(double x, double y, bool animated);

 

事件

Scrolled:滾動完成后引發的事件。該事件會傳入一個類型為 Xamarin.Forms.ScrolledEventArgs 的參數。通過該參數我們可以得到 當前滾動正處於的 X,Y位置。

scrollView.Scrolled += (object sender, ScrolledEventArgs e) =>
{
        button.Text = string.Format("回到頂部({0},{1})", e.ScrollX, e.ScrollY);
};

 

小節:個人認為 ScrollView 在真實項目中會經常用到,比如 ScrollView  能實現 輪播圖,實現產品預覽圖等。

 

  布局控件之RelativeLayout

  RelativeLayout 中文翻譯又叫“相對布局”,意指在Xamarin.Forms中我們通過界面中的某個元素為基准,設置另外一個元素的位置和大小。通過這種布局方式可以實現動態布局,使得設計的界面可以在不同分辨率的屏幕中有更好的顯示效果。

 

 

 從圖上可以看到,RelativeLayout 是以頁面中某個元素為基准,來設置另外一個元素。因為 RelativeLayout 用的比較少,所以下面只簡單了解一下。

 

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="Layout.RelativeLayout">
    <ContentPage.Content>
        <StackLayout>

            <RelativeLayout>
                
                <BoxView Color="Red" x:Name="redBox"
        RelativeLayout.YConstraint="{ConstraintExpression Type=RelativeToParent,
            Property=Height,Factor=.15,Constant=0}"
        RelativeLayout.WidthConstraint="{ConstraintExpression
            Type=RelativeToParent,Property=Width,Factor=1,Constant=0}"
        RelativeLayout.HeightConstraint="{ConstraintExpression
            Type=RelativeToParent,Property=Height,Factor=.8,Constant=0}" />
                
                <BoxView Color="Blue"
        RelativeLayout.YConstraint="{ConstraintExpression Type=RelativeToView,
            ElementName=redBox,Property=Y,Factor=1,Constant=20}"
        RelativeLayout.XConstraint="{ConstraintExpression Type=RelativeToView,
            ElementName=redBox,Property=X,Factor=1,Constant=20}"
        RelativeLayout.WidthConstraint="{ConstraintExpression
            Type=RelativeToParent,Property=Width,Factor=.5,Constant=0}"
        RelativeLayout.HeightConstraint="{ConstraintExpression
            Type=RelativeToParent,Property=Height,Factor=.5,Constant=0}" />
                
            </RelativeLayout>

        </StackLayout>
    </ContentPage.Content>
</ContentPage>

 

  布局控件之FlexLayout

  FlexLayout 中文翻譯又叫“彈性布局”,FlexLayout 布局 是Xamarin.Forms 版本 3.0 中新增功能,基於 CSS靈活布局思想。因為它包括許多靈活的選項來排列子級在布局,因此稱為 Flex 布局或 彈性布局。

FlexLayout 非常靈活,讓我們直接運行一個示例來說明:

XAML 代碼:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="Layout.FlexLayout">
    <ContentPage.Content>

        <FlexLayout Direction="Column" JustifyContent="Start" AlignItems="Start" >

            <StackLayout FlexLayout.Grow="1" BackgroundColor="Red" FlexLayout.Order="1"/>
            <StackLayout FlexLayout.Grow="2" BackgroundColor="Green" FlexLayout.Order="2"/>
            <StackLayout FlexLayout.Grow="3" BackgroundColor="Blue" FlexLayout.Order="3"/>

            <Label BackgroundColor="Blue" FlexLayout.AlignSelf="Stretch" FlexLayout.Order="4">底部狀態欄</Label>

            <Label BackgroundColor="Green" FlexLayout.Basis="150" Text="歡迎使用Xamarin Forms"/>
            <Label FlexLayout.Basis="150" Text="歡迎來到hexu6788"/>
            <Button Text="點擊按鈕" FlexLayout.AlignSelf="Center" />
        </FlexLayout>
        
    </ContentPage.Content>
</ContentPage>

FlexLayout 布局中包含了以下重要的概念。

屬性

Direction:指定 FlexLayout 布局的方向。類型為 Xamarin.Forms.FlexDirection,枚舉 FlexDirection 包含4個值;

  Row 指示子元素將以區域設置的默認行布局方向分布;

  RowReverse 指示子元素將以區域設置默認行布局方向的反方向分布;

  Column 指示子元素將以區域設置的默認列布局方向分布;

  ColumnReverse 指示子元素將以區域設置默認列布局方向的反方向分布;

AlignItems:指定 FlexLayout 布局每一項的對齊方式。類型為 Xamarin.Forms.FlexAlignItems,枚舉 FlexAlignItems 包含4個值;

  Start 指示子元素將向父級的首端對齊;

  Center 指示子元素將在父級內居中顯示;

  End 指示子元素將向父級的末端對齊;

  Stretch 指示子元素將從父級的首端拉伸至末端;

JustifyContent:指定 FlexLayout 布局軸的對齊方式。跟 AlignItems 相似,AlignItems 指橫軸,而 JustifyContent 指縱軸。JustifyContent 的 類型為 Xamarin.Forms.FlexJustify,枚舉 FlexJustify 包含6個值;

  Start 指示子元素將向該行首端對齊;

  Center 指示子元素將聚集到父級中心;

  End 指示子元素將向該行末端對齊;

  SpaceBetween 指示子元素之間的間距相等,行的兩端沒有空格,以此形式在行中填充元素和空間;

  SpaceEvenly 指示子元素之間的間隔和首尾兩個子元素與其最接近的父級邊緣之間的間距相同;

  SpaceAround指示子元素在開頭和結尾處將以一個空格單位隔開,並且各元素之間間隔兩個單位,以此形式在行中填充元素和空間;

FlexLayout.AlignSelf:通過設置子元素的 FlexLayout.AlignSelf 屬性,可以重寫父元素的 AlignItems 設置。FlexLayout.AlignSelf 有5種選項;

  Start 指示該元素將向父級的首端對齊;

  Center 指示該元素將在父級內居中顯示;

  End 指示該元素將向父級的末端對齊;

  Stretch 指示該元素將從父級的首端拉伸至末端;

  Auto 指示該元素會采用供其父級使用的 FlexAlignItems 值提供的對齊方式;

FlexLayout.Order:此屬性是一個整數,其默認值為 0。 此屬性可用於更改在布局順序。

FlexLayout.Grow此屬性的類型是 float 和默認值為 0。如果該軸還有剩余空間時,會將設置為正值的元素進行剩余空間分配。分配空間的比例則按照設置Grow的比例來,類似於 Grid 中的星型規范按比例分配空間。

FlexLayout.Base:此屬性的類型是 float 型。 此屬性定義一個絕對基本長度的值,FlexLayout.Base 支持百分比,寫法如 Base = "80%",在 FlexLayout 自動 Render 的時候不受影響,比如 FlexLayout 方向為 Column 時 Base = 150 定義了一個高度為150的元素。

  布局壓縮LayoutCompression

  再擴展一個概念,LayoutCompression(布局壓縮)。布局壓縮功能從可視化樹中刪除指定的布局,以試圖提升頁面呈現性能。

  為什么需要布局壓縮呢?因為在我們寫 Xamarin.Forms 代碼的時候,也許布局有很多重復的地方,也許還會有很多簡寫但是不影響功能的寫法。因此,微乳提出了性能優化方式,布局壓縮。

<StackLayout>
  <StackLayout ...>
    
<Button ... />     <Image ... />     <Image ... />     <BoxView ... />     <Label ... /> </StackLayout>
  <Label ... /> </StackLayout>

通過 Xamarin Inspector 工具, 在 Android 上,嵌套的視圖層次結構包含 17 層視圖:

 

  在 XAML 中,可以在元素上設置 CompressedLayout.IsHeadless 屬性設置為 true 來啟用布局壓縮 ,也可以通過 C# 代碼來設置 布局壓縮:

<StackLayout CompressedLayout.IsHeadless="true">
  ...
</StackLayout> 
CompressedLayout.SetIsHeadless(元素, true);

  通過壓縮以后,嵌套的視圖層次結構變成 14 層視圖。與原始的嵌套的視圖17 層相比有 17%的減少。 雖然減少不多,但這個性能對整個頁面的性能影響也很大。設置過屬性的元素不適合布局壓縮,因為某些元素設置了 背景色,或者手勢事件等。壓縮了后這個節點就沒有了,會影響軟件功能。

  結束語

實際上 Xamarin.Forms 還有其他的布局控件,因為文章已經很長了,所有就准備放到后面再寫了。個人博客已經幾年沒寫了,現在有點閑所以又繼續寫博客。

本次寫這篇文章,我自己學到了很多東西,希望 Xamarin 在中國有更多的人用起來,感謝博客園提供平台,感謝各位大神提供支持。

以上所有示例代碼都在 https://github.com/hexu6788/XamarinForms-Samples/tree/master/Code/XamarinLayout/Layout/Layout

 

 

參考資料:

[1] Xamarin官方英文文檔  來源:https://docs.microsoft.com/en-us/xamarin/

 

如果你覺得本篇文章對您有幫助的話,感謝您的【推薦】。

如果你對 .NET 或 Xamarin 有興趣的話可以關注我,我會定期的在博客分享我的學習心得。

本文地址:http://www.cnblogs.com/hexu6788/p/9916038.html

作者博客:何旭


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM