2017/2/18

Xamarin.Forms 教學系列文(四.壹)StackLayout & ScrollView




學習目標
  • StackLayout - 物件堆疊排放
  • ScrollView - 畫面捲動
  • Expand 屬性在 StackLayout 內的使用

如果你已經拜讀完 三.壹 章,或許你會很興奮的開啟 VisualStudio ,接著把全部的顏色列出來寫在不同的 Lable 放到 ContentPage 內。

BUT,就是這個 BUT,會發現全部的 Label 都疊在同一個位子上...

小弟我寫網頁多年... textbox、div、button,物件不是放到畫面上就會自己往後排隊放好嗎...?

Xamarin.Forms 物件的排版有一些特定的 "畫板" 需要使用:
  • AbsoluteLayout
  • Grid
  • RelativeLayout
  • StackLayout

這一小節先來介紹 StackLayout


StackLayout

顧名思義,StackLayout 就像是堆積物品一樣,將物件在畫面上一件一件疊上去,而 StackLayout 的順序可以是 從上到下 或 從左到右。

StackLayout 有兩個屬性:
  • Orientation,可設定為 Vertical (預設) 或是 Horizontal
  • Spacing of type double,預設值為 6.0

底下來看程式碼,這邊自己手動設定一組 Tuple<Color, string> 陣列資料,再用迴圈將
每筆資料加入 StackLayout 內:
    class ColorLoopPage : ContentPage
    {
        public ColorLoopPage()
        {
            var colors = new[]
            {
                new { value = Color.White, name = "White" },
                new { value = Color.Silver, name = "Silver" },
                new { value = Color.Gray, name = "Gray" },
                new { value = Color.Black, name = "Black" },
                new { value = Color.Red, name = "Red" },
                new { value = Color.Maroon, name = "Maroon" },
                new { value = Color.Yellow, name = "Yellow" },
                new { value = Color.Olive, name = "Olive" },
                new { value = Color.Lime, name = "Lime" },
                new { value = Color.Green, name = "Green" },
                new { value = Color.Aqua, name = "Aqua" },
                new { value = Color.Teal, name = "Teal" },
                new { value = Color.Blue, name = "Blue" },
                new { value = Color.Navy, name = "Navy" },
                new { value = Color.Pink, name = "Pink" },
                new { value = Color.Fuchsia, name = "Fuchsia" },
                new { value = Color.Purple, name = "Purple" }
            };

            StackLayout stackLayout = new StackLayout();

            foreach (var color in colors)
            {
                stackLayout.Children.Add(new Label
                {
                    Text = color.name,
                    TextColor = color.value,
                    FontSize = Device.GetNamedSize(NamedSize.Large, typeof(Label))
                });
            }

            Padding = new Thickness(5, Device.OnPlatform(20, 5, 5), 5, 5);
            Content = stackLayout;
        }
    }

執行結果:


BUT,會發現 Android 或 WP 有一些 Label 因為數量太多被擠出螢幕外了...

當然你可以試著把字縮小或是設定 Spacing = 0 讓每個 Label 無間距的靠在一起,這樣消失的 Label 就有機會出現在螢幕內...

但我們還是將 StackLayout 放到 ScrollView 內,讓物件可以上下滑動比較實在...

* StackLayout 的高度會自動調整和內部所有物件加起來的高度一樣,所以一般來說我們不會特地對物件設定 VerticalOptions。

ScrollView

書上這裡有介紹 typeof 的用法,這邊也提一下,因為等等程式碼會用到...

在 .NET 內,你可以利用 typeof 運算式取得任何類別或是結構 (Structure)。
舉例來說,typeof(Color) 就會回傳一個類別為 Type 的 Color 結構。

聽不懂沒關係,總之,你可以用迴圈配合方法將所有 Color 取出來...

看程式碼了解一下...
    public class ReflectedColorsPage : ContentPage
    {
        public ReflectedColorsPage()
        {
            StackLayout stackLayout = new StackLayout();
            // Loop through the Color structure fields.
            foreach (FieldInfo info in typeof(Color).GetRuntimeFields())
            {
                // Skip the obsolete (i.e. misspelled) colors. 
                if (info.GetCustomAttribute<ObsoleteAttribute>() != null)
                    continue;

                if (info.IsPublic && 
                    info.IsStatic && 
                    info.FieldType == typeof(Color))
                {
                    stackLayout.Children.Add( CreateColorLabel((Color)info.GetValue(null), info.Name));
                }
            }

            // Loop through the Color structure properties. 
            foreach (PropertyInfo info in typeof(Color).GetRuntimeProperties())
            {
                MethodInfo methodInfo = info.GetMethod;
                if (methodInfo.IsPublic && 
                    methodInfo.IsStatic && 
                    methodInfo.ReturnType == typeof(Color))
                {
                    stackLayout.Children.Add( CreateColorLabel((Color)info.GetValue(null), info.Name));
                }
            }

            Padding = new Thickness(5, Device.OnPlatform(20, 5, 5), 5, 5);

            // Put the StackLayout in a ScrollView.
            Content = new ScrollView
            {
                Content = stackLayout
            };

        }

        Label CreateColorLabel(Color color, string name)
        {
            Color backgroundColor = Color.Default;
            if (color != Color.Default)
            {
                // Standard luminance calculation. 
                double luminance = 0.30 * color.R + 0.59 * color.G + 0.11 * color.B;
                backgroundColor = luminance > 0.5 ? Color.Black : Color.White;
            }

            // Create the Label.
            return new Label
            {
                Text = name,
                TextColor = color,
                FontSize = Device.GetNamedSize(NamedSize.Large, typeof(Label)),
                BackgroundColor = backgroundColor
            };
        }
    }

這支程式碼利用 typeof 將所有 Color 取出後,會利用 CreateColorLabel() 產生有顏色的 Label 並逐一塞進 StackLayout 內,再將此 StackLayout 放到 ScrollView。

而這 CreateColorLabel() 方法內會去計算 Label 背景色該是黑色還白色,讓 Label 文字更明顯。

執行後就能上下滑看看了~


Expand

每個 VisualElement 都能去設定其 VerticalOptions 和 HorizontalOptions,在早一點的章節
(二.貳)畫面位移 & 物件置中 就有提到過。

其中有四個 LayoutOptions 唯讀屬性可以使用:
  • LayoutOptions.Start
  • LayoutOptions.Center
  • LayoutOptions.End
  • LayoutOptions.Fill (預設)
像上面  ReflectedColorsPage 程式,可以藉由 Label 黑色的背景觀察到, Label 在 StackLayout 左右寬度是 Fill 的。

這一小節要介紹的是另外四個屬性:
  1. LayoutOptions.StartAnd
  2. LayoutOptions.CenterAndExpand
  3. LayoutOptions.EndAndExpand
  4. LayoutOptions.FillAndExpand
*要注意的是,這四個屬性只有在 StackLayout 內有效果。

使用時有三個條件限制:
  1. StackLayout 的內容物件總高度不能大於 StackLayout 的高度。
  2. StackLyout 不能設定 VerticalOptions 為 Start、Center 或 End。
  3. StackLayout 內至少有一個子項目的 VerticalOptions 設定為 Expand。

完成以上條件就能觸發 Expand...而 Expand 的功能就是平均分配物件在 StackLayout 的配置的空間。

底下 Demo 將我們要的 LayoutOptions 取出來後放到 StackLayout 內:
public class VerticalOptionsDemoPage : ContentPage
    {
        public VerticalOptionsDemoPage()
        {
            Color[] colors = { Color.Yellow, Color.Blue };
            int flipFlopper = 0;
            // Create Labels sorted by LayoutAlignment property.
            IEnumerable


執行結果:可以看到四個黃色的 Expand Label 平均在畫面上分配的狀況。

*最後再提醒一下,Expand 在其他 Layout 是無效的! 只能用在 StackLayout 內!

9 則留言:

  1. 感謝作者整理的教學系列文。

    我發現ScrollView裡面的程式碼
    if (info.GetCustomAttribute() != null)
    continue;
    這句會有錯,看了你下一篇裡面貼的原文電子書,要下面這樣才可以編譯
    if (info.GetCustomAttribute < ObsoleteAttribute> () != null)
    continue;
    好像< ObsoleteAttribute> 沒空一格,字會被吃掉

    回覆刪除
  2. 請問羅根大大,您在做這教學時,所看的書是哪本書呢?能否提供參考?小弟在此感激不盡

    回覆刪除
    回覆
    1. YOOOOOOO!!!! 你一定沒從第一章開始看XD
      教學都是微軟官方的原文電子書來的喔,大概花了兩年的時間才把這本看過一次並做成教學,
      有任何問題也歡迎從粉絲團私訊給我
      https://developer.xamarin.com/guides/xamarin-forms/creating-mobile-apps-xamarin-forms/

      刪除
  3. Device.OnPlatform(20, 0, 0)

    有關於這個語法 在vs2017 上表示好像過時了

    有更好的寫法嗎?

    回覆刪除
    回覆
    1. http://www.loganedge.tw/2018/01/xamarinforms-oplatform.html 這篇有詳解喔,有問題再問我

      刪除
    2. 原來如此 要換成SWITCH CASE

      又讓我想起不好的回憶XDDD

      謝謝^^

      刪除
    3. Xamarin.Fomrs 能支援的開發平台越來越多了,最近的版本狂推 Mac os app,我猜為了擴充性才改成那樣吧

      刪除
    4. 最近才剛接觸Xamarin 很多功能都還不了解XD

      刪除