天天看點

跨平台開發 - Xamarin.Forms Shell 簡介

小編: 今天Xamarin.Forms 4.0正式釋出了,歡迎開始體驗。

對于熱衷 XAML 和 C# 的跨平台開發人員,Xamarin.Forms 是一款受歡迎的工具包,因為它最大限度地實作了代碼共享,同時還提供對所有本機平台 API 和 UI 控件的完全通路權限。此功能包含的技術和概念可能會令剛開始使用的人歡欣鼓舞,也可能會令他們困惑不解。事實上,一些開發人員一開始就覺得很沮喪。選擇 Xamarin 是為了提高工作效率,而最不想遇到的就是不必要的麻煩。在今年的 Connect(); 大會上,我們非常高興地引入 Xamarin.Forms Shell,這是移動應用開發的預設新起點,不僅可以降低複雜性,還能提高工作效率。

顧名思義,從根本上說,Shell 就是一個容器,負責處理每個應用都需要的基本 UI 功能,這樣開發人員就能以應用的核心工作為重點。現有 iOS 和 Android 應用也可以輕松采用 Shell,并立即受益于導航、UI 性能和擴充性方面的改進。Shell 的優勢如下:

在一個位置集中描述應用的可視結構

通用導航 UI 和無所不在的導航服務(含深層連結)

內建的搜尋處理程式,可提高整體應用内搜尋體驗

預設可擴充理念,可提高多功能性和靈活性

在每個項目開始時,都會有人為你勾勒出要生成的應用結構(但願不僅僅隻是在腦海中勾勒)。有時是通過設計構成形式提供,有時隻是用鉛筆在紙上畫出的草圖。Shell 可以非常輕松地擷取内容,并将内容轉換為正常運作的應用容器,以供任何人填充内容和功能。

在本文中,我将使用名為“Tailwind Traders”的移動購物應用示例。這是團隊生成的新參考應用,用于展示如何結合使用 Xamarin.Forms Shell、Azure、認知服務以及其他多種功能和服務。看一看我們卓越設計團隊提供的設計構成,如圖 1 所示。

跨平台開發 - Xamarin.Forms Shell 簡介

圖 1:Tailwind Traders 示例應用的設計構成

從顯示的螢幕中可以看出,應用提供了所需的全部明顯功能,包括登入和注冊流、産品類别浏覽體驗和搜尋以及簽出流。此應用還利用裝置攝像頭和 Azure 自定義視覺 API 的強大功能來實時識别産品。

接下來一起使用 Shell 快速生成此應用。打開 Visual Studio 2019,并使用 Xamarin.Forms 啟動新的跨平台應用。為了友善本文示範并了解 Shell 的強大功能,接下來将從空白項目開始生成 Tailwind Traders 應用結構。

生成項目檔案後,立即打開 App.xaml.cs,并注意 MainPage 是否已設定為新 Shell 執行個體。(可以從 aka.ms/xf-shell-templates 下載下傳 Shell 模闆。) 從結構上看,這是與過去典型 Xamarin.Forms 應用的唯一差別。代碼如下:

在 .NET Standard 庫項目的根目錄中,打開 AppShell.xaml,如圖 2 所示。

圖 2:單頁 Shell.xaml

接下來将分解此檔案的各個部分。Shell 由三個層次結構元素組成:ShellItem、ShellSection 和 ShellContent。每個 ShellContent 都是 ShellSection 的子級,ShellSection 又是 ShellItem(即 Shell 的所有部分)的子級。這三個元素本身都不代表 UI,而是代表應用體系結構組織。Shell 需要使用這些項,并針對運作平台生成适當的導航 UI:

ShellItem:應用的頂級結構,由浮出控件中的項表示。可以包含多個 ShellSection。

ShellSection:應用内容分組,可通過底部的頁籤進行導航。可以包含一個或多個 ShellContent(多個 ShellContent 可通過頂部頁籤進行導航)。

ShellContent:應用的 ContentPage。

我可以使用這三個元素來描述 Tailwind Traders 移動應用的可視結構。我将忽略登入和注冊流,并添加多個 ShellItem(由左側的浮出控件菜單表示)托管内容。

為什麼不對 Shell 概念使用 FlyoutItem BottomTab、TopTab 等名稱?我們的 Microsoft 團隊對此進行了多次讨論,并認為 Xamarin.Forms 适用于未來的已知平台,而這些平台有時并不共用确切的頁籤或菜單概念。通過保留命名法摘要,可以通過樣式和模闆來決定是應在不同平台之間一緻地表示這些元素,還是應遵循每個平台的設計美學。當然,始終歡迎大家提供這些方面的回報!

圖 3 提供了一個示例。在圖中,可以看到浮出控件中的菜單(UI 下三分之二),其中自動填充有 ShellItem。這樣就可以轉到應用的不同區域了。除了這些項之外,還可以顯式添加與 ShellItem 無關的菜單項。頂部的浮出控件标頭(兩個按鈕)表示特殊内容,由要在此空間内顯示的任何内容組成。若要在 Shell.xaml 中聲明自定義 FlyoutHeader,請運作下面的代碼:

跨平台開發 - Xamarin.Forms Shell 簡介

圖 3:FlyoutMenu 元素

使用标頭元素,可以控制标頭在使用者滾動顯示内容時的行為。有以下三個選項:

Fixed:标頭在下面的内容滾動時固定不變。

Scroll:與菜單項一起滾動。

CollapseOnScroll:在使用者滾動時以視差方式折疊。

若要調整此行為,請将 Shell 中的 FlyoutHeaderBehavior 屬性設定為前面剛剛詳述的相應值。現在,我們讓标頭在下面的内容滾動時固定不變:

接下來将設定内容。檢視設計時,可以看到有一個主螢幕、一系列産品類别、一個配置檔案,最後是登出螢幕。接下來從主螢幕開始,運作以下 XAML 代碼:

通過從裡到外分解此 XAML,我已将 HomePage 添加到應用中,這是第一個啟動的 ContentPage,因為它是 Shell 檔案中聲明的第一個内容。這與現有 Xamarin.Forms 應用中使用的 ContentPage 類型相同,現托管在 Shell 上下文中。

對于此設計,我隻需要設定标題,但 ShellItem 還提供 FlyoutIcon 屬性,可便于提供在項左側顯示的圖像。圖示可以是任意 Xamarin.Forms ImageSource。

現在運作應用。單擊首頁上的漢堡圖示,以打開浮出控件菜單。點選此菜單項可轉到主螢幕(這是目前的唯一螢幕)。我們一起來添加更多内容。

接下來,我将實作産品類别,如“節日裝飾”、“裝置”等。我可以為每種類别添加 ShellItem,但由于産品類别頁都是内容不同的相同頁面,是以我能夠巧妙添加。我将使用簡單的 Menu­Item 轉到相同頁面,并通過 CommandParameter 傳遞資料,以免不必要的頁面重複。下面是用于在 Shell.xaml 中添加 MenuItem 的代碼:

Shell 的特色功能之一是,支援資料綁定。在此示例中,我在視圖模型上有一個可執行導航操作的“Command”。與 ShellItem 一樣,MenuItem 也需要使用文本和圖示。此外,與 ShellItem 一樣,我也可以在 Shell 上設定 MenuItemTemplate 屬性,以提供樣式或自定義模闆來進一步自定義設計。

為了完成任務,我可以為每種類别添加更多菜單項。圖 4 展示了所有菜單項的代碼,而圖 5 則展示了應用浮出控件菜單中的可視結果。

圖 4:所有菜單項

跨平台開發 - Xamarin.Forms Shell 簡介

圖 5:包含所有菜單項的浮出控件

現在,将設計中的配置檔案頁面添加到應用。向項目添加新的 ContentPage,再傳回到 Shell.xaml 檔案。可以複制對 HomePage 使用的 XAML(如圖 4 所示),并将它替換為配置檔案頁面,但這樣做有阻礙應用的風險,因為 HomePage 是在應用啟動期間立即建立的。為了避免一次加載應用的所有頁面,我使用如下的資料模闆:

我提供資料模闆,而不是直接将 ContentPage 提供給 ShellContent 的 content 屬性。當使用者轉到螢幕時,Shell 動态執行個體化所請求的頁面。

這種處理方式與 Home­Page 相比,需要注意的一點是,我已省略 ShellItem 和 ShellSection 包裝器,并直接将标題置于 ShellContent 中。這樣會大大降低詳細程度,Shell 知道如何通過提供必需邏輯包裝器來為我處理它。還需要注意的是,這些包裝器不會将 UI 視圖引入樹。在編寫 Shell 時,考慮到了呈現速度和記憶體消耗。這樣做的結果是,通過在此新 Shell 上下文中托管目前相同的内容和 UI,可以降低對 Android OS 造成的性能影響。當然,你最終仍負責搭建應用的内部架構,但 Shell 提供了一個很好的起點。

可以使用 CSS 或 XAML 樣式設定來設定 Shell 和 FlyoutMenu 各個方面的樣式,就像設定其他任何 XAML 元素的樣式一樣。若要進一步了解浮出控件菜單項的顯示方式,該怎麼辦?回頭看看圖 3 中的設計,就會發現菜單項比其他 Shell 項更醒目。

菜單項和 Shell 項的顯示是可擴充的,具體方法是向 Shell 提供 DataTemplate。MenuItem 是使用 Shell 的 MenuItemTemplate 在浮出控件菜單中呈現,ShellItem 是使用 ItemTemplate 呈現。若要完全控制這些外觀,請将每個屬性設定為包含自定義 ContentView 的 DataTemplate。Shell 将向模闆 BindingContext 提供 Title 和 Icon 可綁定屬性,如圖 6 所示。圖 7 展示了可視結果。

圖 6:在 Shell.xaml 中自定義 ShellItem 的項模闆

跨平台開發 - Xamarin.Forms Shell 簡介

圖 7:浮出控件項模闆結果圖像

除了自定義項呈現器外,還将向浮出控件添加精美标頭,其中包括一個帶有标簽的框,以及兩個可便于快速通路攝像頭功能的按鈕。與其他模闆一樣,在 Shell.xaml 檔案中為 FlyoutHeaderTemplate 添加一個。内容可以是任意 ContentView,是以此時使用 StackLayout 垂直定位子控件,如圖 8 中的代碼所示。添加一些樣式以讓它接近設計構成,并運作應用以檢視結果,如圖 9 所示。可以在 Shell 元素中設定 FlyoutHeaderBehavior,以确定标頭是固定不變,還是在使用者滾動螢幕時一起滾動或折疊。

圖 8:FlyoutHeaderTemplate

跨平台開發 - Xamarin.Forms Shell 簡介

圖 9:包含标頭的浮出控件圖像

現在是時候實作轉到菜單項頁面的指令了。為此,我将使用 Shell 引入的基于 URI 的新路由。使用 URI,使用者可以立即跳轉到應用的任何部分,甚至能向後跳轉,而無需在兩個點之間建立所有頁面。接下來将介紹此過程是如何完成的。

首先,我需要聲明路由,從适用于我的應用的方案和主機開始,如下所示:

通過将這些片段組合到 URL 中,我最後得到以下 URI:app://www.microsoft.com/tailwindtraders。

我在 Shell 檔案中定義的每個 Shell 元素還有 route 屬性,稍後可用于以程式設計方式導航。對于不使用 Shell 元素表示的頁面,我可以顯式注冊路由。這就是我将對添加到浮出控件中的菜單項所執行的操作。其中每一項都會轉到 ProductCategoryPage,此頁面列出了特定類别的産品。下面是路由注冊代碼:

現在,我可以在 Shell.cs 的構造函數中聲明必要的路由,也可以在調用路由之前運作的任意位置聲明。菜單項公開用于實作必要導航的指令,如下面的代碼所示:

Shell 的另一大優勢是,它有可從應用中的任意位置通路的靜态導航方法。過去需要擔心導航服務是否可用、将它從視圖傳遞到視圖模型,還需要添加導航頁面來包裝所有内容,這樣的日子已經一去不複返了。現在,可以擷取對應用 Shell 的引用,即可作為 App.Current 屬性通路的應用 MainPage。前面的代碼片段中展現了這一點。若要執行導航,請調用 GoToAsync 方法,同時将有效 URL 作為 ShellNavigationState 傳入。可以通過字元串或 URI 構造 ShellNavigationState。再次檢視代碼,可以看到 GoToAsync 也隻允許提供字元串,Shell 将執行操作來執行個體化 ShellNavigationState。

資料可以通過查詢字元串參數在視圖和視圖模型之間進行傳遞。當你使用 QueryProperty 屬性修飾相應屬性時,Shell 将在 ContentPage 或 ViewModel 上直接設定這些值,如圖 10 所示。

圖 10:查詢屬性示例

QueryProperty 從接收類中擷取公共屬性名(在此示例中為“TypeID”),并擷取 URL 中使用的查詢字元串參數名稱(在此示例中為“id”)。

攔截回退操作是移動應用開發中的常見要求,這對 Xamarin.Forms 來說是一個挑戰。Shell 修複了此問題,允許在完成前後挂鈎到導航路由,以實作大量自定義需求。下面是一個導航處理示例,它先使用 XAML 代碼配置設定事件處理程式:

再對事件處理程式使用 C# 代碼:

在 Shell 執行個體上,将事件處理程式添加到導航事件。在代碼隐藏中,ShellNavigatingEventArgs 提供了導航的基本詳細資訊,如圖 11 所示。

圖 11:ShellNavigatingEventArgs

元素

類型

說明

目前

ShellNavigationState

目前頁的 URI。

ShellNavigatinState

表示導航起源位置的 URI。

目标

表示導航目标位置的 URI。

CanCancel

Boolean

指明能否取消導航的屬性。

取消

用于取消已請求導航的方法。

已取消

指明目前導航是否已取消的屬性。

浮出控件菜單是常用于導航的 UI 模式。考慮應用中的内容層次結構時,頂級或最外層導航是浮出控件菜單。随後,下一個詳細級别是底部頁籤。如果沒有浮出控件,通常将底部頁籤視為應用中的頂級導航。然後,在底部頁籤中,下一個級導航是頂部頁籤。除此之外,就是從一個頁面推送到另一個頁面的單頁。這就是 Shell 提供導航 UI 所采取的一種固執己見的方法。

先從底部頁籤開始。一個 ShellItem 中的每個 ShellSection(如果有多個的話)都可以表示為底部頁籤。下面的 XAML 代碼示例展示了如何為應用生成底部頁籤:

此代碼在一個 ShellItem 中顯示兩個 ShellSection。這些 ShellSection 在 UI 中表示為螢幕底部頁籤。如果不需要浮出控件,又如何呢?如果隻有一個 ShellItem,可以通過将“FlyoutBehavior”設定為“Disabled”來隐藏它。可以使用現有樣式選項設定頁籤樣式,也可以通過提供自定義呈現器來設定樣式。與可以是自定義資料模闆的浮出控件菜單項不同,頁籤更特定于平台。若要設定頁籤的顔色樣式,請對 TabBar 項使用 Shell 類的樣式屬性,如下所示:

将樣式類配置設定給 ShellItem 會将這些顔色應用到相應部分中的所有頁籤。

現在将轉向頂部頁籤。若要讓内容可從頂部頁籤進行導航,可以在一個 ShellSection 中添加多個 ShellContent 項。樣式應用就像上一底部頁籤示例一樣。代碼如下:

Xamarin.Forms Shell 中還有更多功能有待發現。我可以繼續描述如何自定義導航欄、後退按鈕,以及功能非常強大的搜尋處理程式的所有方面。使用此搜尋處理程式,向頁面添加搜尋功能不再是難事。這些功能及其他功能現已釋出,相關文檔将會在穩定版快要釋出時推出。

Shell 之旅才剛剛開始。我們從 Xamarin.Forms 開發人員那裡清楚地了解到,通常需要讓 iOS 和 Android 應用看起來差不多或完全一樣。為了解決此問題,我們計劃釋出 Material Shell,這是一種 Shell 實作,用于将 Google 的 Material Design 樣式應用為所有受支援控件的起點。這些控件仍是本機的,是以不會影響性能或功能。

導航轉換和 segue 也在開發中。通過轉換,可以控制一個頁面如何以動畫效果切換到另一個頁面(從左到右、從右到左、交叉淡入淡出、卷曲等)。segue 是一種聲明方式,比如可聲明“當此按鈕操作發生時,執行此路由”。 通過它,無需編寫 GoToAsync 導航代碼,并能在 XAML 中更清晰地表達内容是如何互相關聯的。通過結合使用轉換和 Material Shell,我們可以提供一些額外動畫效果,如便于圖像圖示等元素可以從一個頁面無縫轉換到另一個頁面的主圖動畫效果。

目前,Xamarin.Forms Shell 在 Xamarin.Forms 4.0 預覽版中提供,其中包括令人驚歎的新功能,如 CollectionView、CarouselView 和全新 Material Visual。使用 Material Visual,從一緻、通用的 UI 樣式點(而不是平台專用空白點)啟動 Xamarin.Forms 應用不再是難事。通過切換預發行版選項,使用 Visual Studio NuGet 包管理器更新到版本 4.0 預覽版 1。

為了簡化操作,我們建立了已更新的項目模闆包,這些模闆在 Shell 上統一,且預設提供 4.0 預覽版。從 aka.ms/xf-shell-templates 下載下傳并安裝模闆。完成後,便能在建立 Xamarin.Forms 項目時使用新的 Shell 強力驅動模闆。

随着 Visual Studio 2019 預發行版本的不斷演變,Xamarin.Forms 4.0 和 Shell 也将不斷演變。我們需要聆聽你的回報。請通過通路 aka.ms/xf-4-feedback,告訴我們你的想法和體驗。

David Ortinau 是 Microsoft 移動開發人員工具進階項目經理,主要負責 Xamarin.Forms 領域。自 2002 年以來,Ortinau 一直是 .NET 開發人員,他精通各種程式設計語言,為各種行業開發了 Web、環境和移動體驗。在科技創業公司取得了幾次成功并營運了自己的軟體公司之後,Ortinau 加入 Microsoft 來追尋自己的理想:開發有助于開發人員打造更優質應用體驗的工具。不工作或不陪家人時,他就在林中快跑。