2017/2/20

Xamarin.Forms 教學系列文(四.貳)Frame、BoxView & 讀取文字資源檔




學習目標
  • Frame - 自帶邊緣陰影的容器。
  • BoxView - 可填滿顏色的矩形。
  • 如何讀取文字 (.txt) 實體檔。

這小節介紹兩個滿常用到的畫面元件,Frame BoxView

先來介紹 Frame:

Frame

Frame 是一個與內部元件預設間隔 20 (padding = 20) ,自帶邊緣陰影特效的容器物件,有幾個常用的屬性可以設定:
  • HasShadow - 預設 True
  • BackgroundColor 
  • OutlineColor
看名字就知道這些屬性在設定什麼了吧...

不論是 Frame 或 BoxView,如沒有特別指定寬高,或是設定 LayoutOptions、HorizontalOptions,預設都是 LayoutOptions.Fill (填滿畫面)

舉例來說,我們把一個置中的 Label 放在 Frame 內:
public class FramedTextPage : ContentPage
{
    public FramedTextPage()
    {
        Padding = new Thickness(20);
        Content = new Frame
        {
            OutlineColor = Color.Accent,
            Content = new Label
            {
                Text = "I've been framed!",
                FontSize = Device.GetNamedSize(NamedSize.Large, typeof(Label)),
                HorizontalOptions = LayoutOptions.Center,
                VerticalOptions = LayoutOptions.Center
            }
        };
    }
}

執行結果如下:可以觀察到 Farme 預設填滿整個畫面。

接著我們改把 Frame 置中,可以觀察到 Frame 內邊緣和 Label 間隔 20 單位。
public class FramedTextPage : ContentPage
{
    public FramedTextPage()
    {
        Padding = new Thickness(20);
        Content = new Frame
        {
            OutlineColor = Color.Accent,
            HorizontalOptions = LayoutOptions.Center,
            VerticalOptions = LayoutOptions.Center,
            Content = new Label
            {
                Text = "I've been framed!",
                FontSize = Device.GetNamedSize(NamedSize.Large, typeof(Label))
            }
        };
    }
}

再來改一下顏色設定:
public class FramedTextPage : ContentPage
{
    public FramedTextPage()
    {
        BackgroundColor = Color.Aqua;
        Content = new Frame
        {
            OutlineColor = Color.Black,
            BackgroundColor = Color.Yellow,
            HorizontalOptions = LayoutOptions.Center,
            VerticalOptions = LayoutOptions.Center,
            Content = new Label
            {
                Text = "I've been framed!",
                FontSize = Device.GetNamedSize(NamedSize.Large, typeof(Label)),
                FontAttributes = FontAttributes.Italic,
                TextColor = Color.Blue
            }
        };
    }
}

執行結果:



BoxView

用法簡單,直接看程式:
public class SizedBoxViewPage : ContentPage
{
    public SizedBoxViewPage()
    {
        Content = new BoxView
        {
            Color = Color.Accent
        };
    }
}

執行結果:同樣預設是填滿畫面,iOS 甚至連最上方的 Bar 一起填滿。

將 BoxView 置中一下:
public class SizedBoxViewPage : ContentPage
{
    public SizedBoxViewPage()
    {
        Content = new BoxView
        {
            Color = Color.Accent,
            HorizontalOptions = LayoutOptions.Center,
            VerticalOptions = LayoutOptions.Center
        };
    }
}

執行結果:可以觀察到 BoxView 有基本的寬高, 40 單位。

這邊要提到兩個重要的屬性:
  • WidthRequest
  • HeightRequest

BoxView 就是將這兩個屬性預設為 40 單位。

為什麼會說這兩個屬性重要,因為畫面元件 ( VisualElement ) 其實也有 Width Height 這兩個 唯獨 屬性,但:
物件寬高要設定 WidthRequest 和 HeightRequest 才有效!
一般來說 WidthRequest 和 HeightRequest 預設是 -1 (代表無定義),而這兩個屬性真正的涵義是你希望(或要求)這個物件的寬高。

*原因,是因為當你針對畫面元件 (VisualElement) 設定 HorizontalOptions 或 VerticalOptions 時,WidthRequest 和 HeightRequest 會自動失效 (所以稱之為要求的寬高)。

舉例來說,若你設定了 LayoutOptions.Fill,VisualElement 會填滿畫面而不鳥 WidthRequest 或 HeightRequest 的設定。

來設定下寬高:
public class SizedBoxViewPage : ContentPage
{
    public SizedBoxViewPage()
    {
        BackgroundColor = Color.Pink;
        Content = new BoxView
        {
            Color = Color.Navy,
            HorizontalOptions = LayoutOptions.Center,
            VerticalOptions = LayoutOptions.Center,
            WidthRequest = 200,
            HeightRequest = 100
        };
    }
}

執行結果:

書上 p.81 ~ 82 有個不錯的程式,讓你更加了解如何利用 Stacklayout、Frame 和 BoxView 的排版,這裡只貼上執行結果,有興趣看程式就自行看原文電子書~
https://download.xamarin.com/developer/xamarin-forms-book/XamarinFormsBook-Ch04-Apr2016.pdf



讀取 .txt 檔

小節最後要實作一個簡易的電子書程式。
本支程式會先讀出 .txt 檔案後,將第一行文字設定為固定於頂端的標題 Label,而標題下方的內容文字可以捲動閱讀。

讀取 .txt 檔最簡單的方式就是將檔案放在 PCL 的內嵌資源內,如下步驟:
  1. 於 VisualStudio 專案右鍵 → 新增資料夾,命名為 Texts
  2. 右鍵 Texts 資料夾,加入 → 現有項目 (選取 .txt 檔案)
  3. 右鍵檔案 → 屬性 → 更改建置動作 (Build Action) 為內嵌資源 (Embedded Resourc
  4. 建置專案
以上四個步驟就能將 txt 檔內嵌到 dll 檔案內當作資源使用了。

程式方面,我們將用 Assembly 類別提供的 GetManifestResourceStream 方法來獲取這個資源。

Assembly 可以如下宣告為物件使用:
Assembly assembly = GetType().GetTypeInfo().Assembly;
接著要注意的是使用 GetManifestResourceStream 時需要帶入的參數 Resource ID,這參數組成的格式如下:
Namespace.資料夾名.檔名.副檔名
底下範例就是:BlackCat.Texts.TheBlackCat.txt

GetManifestResourceStream 執行後會回傳一個 Stream 物件,我們就能用 .NET 的 StreamReader 來讀取物件內的每一行文字。

來看程式碼:
class BlackCatPage : ContentPage
{
    public BlackCatPage()
    {
        StackLayout mainStack = new StackLayout();
        StackLayout textStack = new StackLayout
        {
            Padding = new Thickness(5),
            Spacing = 10
        };
        // Get access to the text resource.
        Assembly assembly = GetType().GetTypeInfo().Assembly;
        string resource = "BlackCat.Texts.TheBlackCat.txt";
        using (Stream stream = assembly.GetManifestResourceStream(resource))
        {
            using (StreamReader reader = new StreamReader(stream))
            {
                bool gotTitle = false;
                string line;
                // Read in a line (which is actually a paragraph).
                while (null != (line = reader.ReadLine()))
                {
                    Label label = new Label
                    {
                        Text = line,
                        // Black text for ebooks!
                        TextColor = Color.Black
                    };
                    if (!gotTitle)
                    {
                        // Add first label (the title) to mainStack.
                        label.HorizontalOptions = LayoutOptions.Center;
                        label.FontSize = Device.GetNamedSize(NamedSize.Medium, label);
                        label.FontAttributes = FontAttributes.Bold;
                        mainStack.Children.Add(label);
                        gotTitle = true;
                    }
                    else
                    {
                        // Add subsequent labels to textStack.
                        textStack.Children.Add(label);
                    }
                }
            }
        }
        // Put the textStack in a ScrollView with FillAndExpand.
        ScrollView scrollView = new ScrollView
        {
            Content = textStack,
            VerticalOptions = LayoutOptions.FillAndExpand,
            Padding = new Thickness(5, 0),
        };
        // Add the ScrollView as a second child of mainStack.
        mainStack.Children.Add(scrollView);
        // Set page content to mainStack.
        Content = mainStack;
        // White background for ebooks!
        BackgroundColor = Color.White;

        // Add some iOS padding for the page.
        Padding = new Thickness(0, Device.OnPlatform(20, 0, 0), 0, 0);
    }
}

執行結果:

程式內要注意的還有排版的階層關係。最外面一層 mainStack,包含兩個子項目,標題 Label 和 內容 ScrollViewt。

利用外層的 Stacklayout 將這兩個子項目做堆疊排版,就能達到標題  Label 固定於最上方的效果。

6 則留言:

  1. 請問一下,我resource id要打成這樣 string resource = "BlackCat.Droid.Texts.BlackCat.txt"; 才能讀到檔耶
    這是為什麼??
    謝謝

    回覆刪除
  2. 感謝回覆,我後來解出來了,原來選到了Shared Project
    謝謝哦~

    回覆刪除
  3. 我想請問一下,我按照以上的步驟後,還是讀檔失敗,以下是我的error msg,我有截圖請問我要怎放上來,感謝(初入xamarin的菜逼)
    Unhandled Exception:

    System.ArgumentNullException: Value cannot be null.
    Parameter name: stream

    回覆刪除
    回覆
    1. 歡迎入坑XD
      看起來是沒有讀到檔案造成stream是null產生的錯誤
      也歡迎直接用fb粉絲團向我問問題

      刪除

注意:只有此網誌的成員可以留言。