2017/2/24

Xamarin.Forms 教學系列文(五)Size單位 - device-independent units & StartTimer!


學習目標
  • 了解 Xamarin 的單位 Device-independent Units
  • SizeChanged 事件
  • 如何判斷目前手機方向,portrait 或 landscape
  • Device.StartTimer 方法

前四章已經有出現一些使用單位的屬性:
  • iOS 狀態列高度是 20
  • BoxView 預設寬高是 40
  • Frame 內緣的間距 (Padding) 是 20
  • StackLayout 的物件間距 (Spacing) 是 6

這些尺寸的 "單位" 到底是什麼??

這小節讓我們從最常見的螢幕單位 - DPI,來討論為何我們在 Xamarin.Forms 需要一個特殊的單位,原文書上有更多詳細的解釋 (更多英文),有興趣的可以翻來看看。


DPI & Device-independent Units

DPI (Dots per inch),意思是每一英吋下螢幕可顯示的輸出點 (points) 數目,(point 可以想成手機螢幕硬體顯示單位)。

而我們常用的成像單位為像素點 (pixel),伴隨著解析度越高,每一螢幕輸出點 (point) 就能顯示越多的 pixel。

舉例來說:
3.5 吋 的 iPhone4,螢幕像素點為 640 x 960 pixel,DPI 約為 320,一個顯示點 (point) 內會包含 2 個 pixel。 

而 5.5 吋的 iPhone 6 Plus,螢幕像素點為 1080 x 1920 pixel,DPI 約為 401,一個顯示點 (point) 內會包含 3 個 pixel


重點就是,會因為各手機型號不同,而有不同尺寸的顯示單位。

Xamarin.Forms 為了方便且統一處理這些不同顯示尺寸的問題,幫我們定義了一個特殊單位,叫做 device-independent units

device-independent units 會自動幫我們去處理不同手機螢幕輸出,而其解析度為:
  • 一英吋 160 units
  • 或是 一公分 64 units

每個 Visual Element 類別都定義了 Width 和  Height,這兩個屬性的初始值為 -1,表示無定義,接著若程式有設定 HorizontalOptions 或是  VerticalOptions,才會去更改 Width 和 Height 的值。
若要自行設定 Visual Element 的寬或高,要設定 WidthRequest HeightRequest,Width 和 Height 為唯讀屬性 (系統用,只可查詢不可更改)。


SizeChanged

每個 VisualElement 都定義了 SizeChanged 事件 ,當物件的寬或高發生改變時就會觸發此事件,包括 Page 本身。

很單純的顯示 Page 寬和高:
public class WhatSizePage : ContentPage
{
    Label label;

    public WhatSizePage()
    {
        label = new Label
        {
            FontSize = Device.GetNamedSize(NamedSize.Large, typeof(Label)),
            HorizontalOptions = LayoutOptions.Center,
            VerticalOptions = LayoutOptions.Center
        };

        Content = label;
        SizeChanged += OnPageSizeChanged;
    }

    void OnPageSizeChanged(object sender, EventArgs args)
    {
        label.Text = String.Format("{0} \u00D7 {1}", Width, Height);
    }
}

結果為:

若將轉成橫向的結果為:頭請橫著看...


辨識手機方向為橫向 (landscape) 或直向 (portrait) 

唯一方法就是判斷其寬跟高的關係,當寬大於高時就是橫向 (landscape),反之為直向 (portrait)。

if( Width > Height )
{
    ...
}

往後的書上也會看到實際的程式範例,當我們要在不同方向時處理不同事情時,會將寬高判斷寫在 SizeChanged 內。

此外,若你想鎖定 App 方向時:

iOS:Visual Studio 內, iPhone Deployment Info 選項,選擇 Supported Device Orientations,就能指定 App 的方向。



Android:在 Droid 專案底下,MainActivity.cs 檔案內,Activity 加上
ScreenOrientation = ScreenOrientation.Landscape

ScreenOrientation = ScreenOrientation.Portrait



Device.StartTimer

Device 類別內有提供一個 StartTimer 方法可以使用,設定好間隔時間後,就能週期性的觸發事件。
StartTimer 內要帶入的參數為 TimeSpan 類別。
底下的範例程式,其功能為使用 Label 顯示目前時間,

且在 SizeChanged 事件時會去更改 Label 字體大小,讓畫面更...美觀:
public class FitToSizeClockPage : ContentPage
    {
        public FitToSizeClockPage()
        {
            Label clockLabel = new Label
            {
                HorizontalOptions = LayoutOptions.Center,
                VerticalOptions = LayoutOptions.Center
            };
            Content = clockLabel;
            // Handle the SizeChanged event for the page.
            SizeChanged += (object sender, EventArgs args) =>
            {
                // Scale the font size to the page width
                // (based on 11 characters in the displayed string).
                if (this.Width > 0)
                    clockLabel.FontSize = this.Width / 6;
            };
            // Start the timer going.
            Device.StartTimer(TimeSpan.FromSeconds(1), () =>
            {
                // Set the Text property of the Label.
                clockLabel.Text = DateTime.Now.ToString("h:mm:ss tt");
                return true;
            });
        }
    }

這程式有三點要注意的:
  1. 間隔時間參數 TimeSpan. 的用法。
  2. 更改 Label.Text 的事件寫在 Lambda 語法內,而 Lambda 寫法的優點就是便於維護 (事件較靠近物件)。
  3. Device.StartTimer 內的事件要 return true,計時器才會繼續執行下一個週期。

執行結果:


當然,倒過來時文字會較大 (美觀~):


第五章我其實省略滿多東西... 只挑我覺得重要的部分來說明,有興趣的可以翻翻原文,這裡提供官方 PDF :
https://download.xamarin.com/developer/xamarin-forms-book/XamarinFormsBook-Ch05-Apr2016.pdf



沒有留言:

張貼留言