The Will Will Web

記載著 Will 在網路世界的學習心得與技術分享

WP7 修練 DAY 01:如何設計啟動畫面 (Splash screen)

每一個 Windows Phone 7 應用程式在啟動時多少會花上一些時間,在這個等待的時刻經常都會擺放一些啟動畫面 (Splash screen) 先來充充場面,以免載入時間過長而導致使用者不耐煩,而在這個啟動畫面裡你還能藉此廣告一下把公司的 LOGO、連絡方式、品牌資訊都顯示在這個頁面裡,當然時間不能太長、也不能太短,否則若這個啟動畫面跑得太快,那就沒有出現的必要性了,有時後這種必要的「慢」也是一種藝術。在第一篇修練文章中,我們就從最基本的建立專案開始講起,在開始之前,請先閱讀 Windows Phone 7.5 (Mango) 開發學習資源整理 這篇文章設立基本的開發環境。

首先,我們先建立一個名為 WP7XiuLianDay1_SplashScreen 的專案:

我們選用 ”芒果機” 的作業系統:Windows Phone OS 7.1

開啟專案後,直接新增一個 Windows Phone Portrait Page 頁面,並取名為 SplashScreen.xaml

新增完頁面後直接在 XAML 設計檢視按下 Ctrl + A 全選,並將所有頁面上的視覺元素刪除

然後拉一個 Image 控制項進來

由於 Windows Phone 7 的螢幕大小目前都固定為 480 (寬) x 800 (高),而且我們希望這個啟動畫面能夠佔滿整個畫面,所以我們必須調整 Image 控制項的屬性,這是透過拖曳的方式並不精準,直接修改屬性會比較快些。

接著準備一張 480 x 800 的圖片(注意:不能使用 GIF 格式的圖檔),在此我的圖檔名為 SplashScreen.png 並用一個 Images 目錄來收納它。

然後再調整 Image 控制項的屬性,將圖片選進此控制項(意即設定 Source 屬性)

補充說明:我們也可以將 SplashScreen.png 圖檔的 Build Action 專案屬性Embedded Resource 調整為 Content此舉的主要目的是為了降低 WP7 組件的大小,以加快整體應用程式的載入速度

不過修改了圖檔的專案屬性後,你的 Image 控制項的 Source 屬性也要跟著調整,且必須調整為相對於專案根目錄絕對路徑,用文字描述比較難懂,看圖吧:

 

此時,我們有了兩個頁面,分別是預設首頁 MainPage.xaml 與另一個啟動頁面 SplashScreen.xaml,如果你此時想要預覽剛剛設計的這個頁面的話,可能必須寫一些程式碼,才能把預設首頁自動轉向到這裡,我們先用「偷懶」的作法來實做頁面自動轉向功能。

先開啟 MainPage.xaml 的程式碼:

然後加上該頁面的 Loaded 事件,並透過 NavigationService 實做轉向功能

請注意:這是為了測試方便才這樣寫的,此寫法有 2 個嚴重的缺點:一個是頁面會先讓你看到 MainPage.xaml 然後才會進入到 SplashScreen.xaml 頁面裡;另一個問題是進入該頁面後,若按下手機的上一頁按鈕,又會繼續跳到 SplashScreen.xaml 這頁,這並非常規的設計法則,小朋友請不要亂學。

這時按下 F5 就可以立即看到 SplashScreen.xaml 頁面的出現,不過如果你的眼睛夠尖的話,可能會發現這張圖其實並沒有顯示的很完整,雖然你的圖片是 480x800 的大小,不過呈現在模擬器或手機裡時,其高度會被自動往下擠了 32px 的高度,也就是被 系統匣 (System Tray) 給佔用了!(如下圖示)

所以這張圖最下方的 32px 將會無法顯示在手機上,這一點其實在 XAML 設計檢視中也可以發現:

這時你可以有兩種抉擇,第一種是跟限制妥協,另外準備一張 480 x 768 的圖片來當 Splash 畫面(註:我不建議直接把圖片進行縮放,這樣圖片可能會稍微變形或失真)。

另一種方法,是透過設定把系統匣整個隱藏起來,如此以來便可將 480x800 的圖片完整的呈現在手機上,你可以直接修改 XAML 檔的設定,如下圖示把 shell:SystemTray.IsVisible 屬性調整為 False 即可。

另一種透過程式控制的方法可以在 SplashScreen.xaml 頁面 Code Behind 程式裡的建構子的最後面加入以下程式碼,就可以做到這個效果:

先引用 Microsoft.Phone.Shell 命名空間

using Microsoft.Phone.Shell;

再到頁面的建構子輸入以下程式

this.SetValue(SystemTray.IsVisibleProperty, false);

 

這時你再執行一次,比較一下跟剛剛的圖片是不是不一樣了呢?

 

目前我已經弄好一個 SplashScreen.xaml  頁面,當然,你也可以把這個頁面弄的很花俏,例如加上動畫或什麼的,但那不是本文章的重點,接下來就是本文的重頭戲,設計一個正規的應用程式啟動畫面

我們之所以要做「啟動畫面」,目的可能有兩種:

  1. 因為應用程式需要初始化的時間太長,所以需要先顯示一個簡單的等待頁面。而這也是 Windows Phone Application Certification Requirements 的要求之一(請參見 Technical Certification Requirements 頁面的 5.2.1 Launch Time 這一條規則),而這也是學會設計啟動畫面如此重要的原因之一。
  2. 程式執行的太快,使用者沒有 feel,但要慢也要慢的優雅而從容。 XD

要讓 MainPage.xaml 正式開始執行之前,我們希望能先跳 SplashScreen.xaml 頁面出來,使用的技巧並不是先轉向到 SplashScreen.xaml 再轉回 MainPage.xaml 頁面,我們使用的技巧是透過一個 Popup 視窗擋住 MainPage.xaml 頁面,而這個 Popup 視窗裡就是包裹著 SplashScreen.xaml 頁面,以下是程式的寫法:

1. 先在 MainPage.xaml.cs 最上方引用以下命名空間

using System.Threading;
using System.Windows.Controls.Primitives;
using System.ComponentModel;

2. 然後宣告兩個類別層級的私有變數、修正建構子、並加上一個背景處理的方法。這段程式碼主要是在頁面尚未載入完成之前就先顯示一個 Popup 視窗,並設定在 3 秒之後自動關閉(秒數你可以自行調整):

BackgroundWorker backroungWorker;
Popup myPopup;

public MainPage()
{
    InitializeComponent();

    myPopup = new Popup() { IsOpen = true, Child = new SplashScreen() };

    backroungWorker = new BackgroundWorker();
    RunBackgroundWorker();

}

private void RunBackgroundWorker()
{
    backroungWorker.DoWork += ((s, args) =>
    {
        Thread.Sleep(3000);
    });

    backroungWorker.RunWorkerCompleted += ((s, args) =>
    {
        this.Dispatcher.BeginInvoke(() =>
        {
            this.myPopup.IsOpen = false;
        });
    });

    backroungWorker.RunWorkerAsync();
}

這時按下 F5 執行你會看到 SplashScreen.xaml 顯示 3 秒鐘後會自動關閉,但有趣的是,我們剛剛第一次顯示這個頁面時,這張 480x800 的圖的最下方被截掉了 32px 的空間,而這次透過 Popup 的方式卻是最上方的 32px 被截掉!(我覺得我可能是被客戶磨到成精了,盡是注意這些雞毛蒜皮的事 XD

我們一樣套用剛剛學過的 Hack 技法,改寫這段程式碼,當我們顯示 Popup 頁面時 (SplashScreen.xaml) 關閉系統匣顯示,當關閉 Popup 頁面時再重新顯示系統匣,完整程式碼如下:

using System;
using Microsoft.Phone.Controls;

using System.Threading;
using System.Windows.Controls.Primitives;
using System.ComponentModel;
using Microsoft.Phone.Shell;

namespace WP7XiuLianDay1_SplashScreen
{
    public partial class MainPage : PhoneApplicationPage
    {
        BackgroundWorker backroungWorker;
        Popup myPopup;

        public MainPage()
        {
            InitializeComponent();

            myPopup = new Popup() { IsOpen = true, Child = new SplashScreen() };
            backroungWorker = new BackgroundWorker();
            RunBackgroundWorker();

            this.SetValue(SystemTray.IsVisibleProperty, false);
        }

        private void RunBackgroundWorker()
        {
            backroungWorker.DoWork += ((s, args) =>
            {
                Thread.Sleep(3000);
            });

            backroungWorker.RunWorkerCompleted += ((s, args) =>
            {
                this.Dispatcher.BeginInvoke(() =>
                {
                    this.myPopup.IsOpen = false;
                    this.SetValue(SystemTray.IsVisibleProperty, true);
                }
            );
            });
            backroungWorker.RunWorkerAsync();
        }
    }
}

 

今日修煉總結

今天我學到了如何有時後 GUI 介面不見得最方便,有時後手動修改 XAML 或調整屬性值比用圖形介面來拖曳來的方便。WP7 並不支援 GIF 圖檔(這是有專利的圖形格式),使用時應該避免使用 GIF 圖檔,否則看不到圖片你還會覺得是 WP7 的 Bug。也學到可以將 SplashScreen.png 圖檔的 Build Action 專案屬性從 Embedded Resource 調整為 Content,這樣可以降低 WP7 組件 (assembly) 的大小,以加快整體應用程式的載入速度。

除此之外,還學到了 WP7 手機一定會有個 系統匣 (System Tray) 顯示在頁面最上方,要完整顯示 480x800 的畫面則必須透過一些 Hack 技巧才能實現此功能。最後學到了如何正確的使用 Popup 類別來顯示啟動畫面,並透過 BackgroundWorker 的背景作業來設定固定時間後自動關閉啟動畫面

 

相關連結