前言程序員
孔子說:"軟件是對客觀世界的抽象"。算法
首先聲明,這裏的"三維導航"和地圖沒一毛錢關係,"四核驅動"和硬件也不要緊,而是爲了複雜的應用而發明創造的導航邏輯。說這是發明創造,也不是危言聳聽,由於它徹底突破了傳統意義的頁面導航概念,看完了本博客之後,相信會讓你腦洞大開。固然這也是一種嘗試,只有UWP的出現纔會帶來這種機遇,但願廣大開發者給予指正。windows
上週發佈了淘寶UWP的更新,地址在這裏:https://www.microsoft.com/zh-cn/store/apps/%e6%b7%98%e5%ae%9d/9nblggh6c2cd設計模式
之前淘寶UWP的mobile版叫「手機淘寶」,desktop版叫「淘寶HD」。上週把silverlight版的半殘「淘寶」下線了,把「淘寶」這個應用名稱拿回來了,如今終於把兩者統一爲「淘寶」了。數據結構
隨着UWP的誕生,一個App應該既能夠在狹小的手機屏幕上運行,也能夠在手持式平板電腦上運行,甚至能夠在寬大的桌面電腦屏幕上運行。而對咱們程序員來講,這纔是真正的挑戰。Windows 10,一統天下的宏偉計劃確實給操做系統自己的設計帶來了巨大的挑戰,同時也給UWP應用的開發帶來一樣大的困難,由於它要求你的App能夠運行在全部運行Windows 10的硬件上,而這些硬件有着大相徑庭的屏幕尺寸,從Band,到手機,到平板,到桌面電腦,到Hub。但從另一個方面看,統一的操做系統和UWP卻給用戶帶來了統一的體驗,給整個產業帶來了一樣巨大的利益。app
扯遠了……ide
一維Z軸導航模式函數
首先看看手機上運行的App採用的導航模式:學習
這是最簡單的導航模式了,從數據結構上看,是一個簡單的堆棧。A是起始頁面;在A上點擊個按鈕,進入B頁面;在B上點擊個圖片,又進入C頁面。按back鍵從C回到B,再按就回到A。測試
如下是淘寶UWP中,在手機屏幕上運行時的狀況。
首先進入主頁A,而後點擊了"淘搶購"頻道,進入淘搶購的列表頁B,而後在B中點擊了一個列表項,進入該商品的詳情頁面C。
手機屏幕有限,一次只能看一個頁面,當用戶想看其它商品信息時,必須先返回到B頁,而後再選擇另一個條目。
在UWP中,導航必須在Frame類上實現,下面代碼中所示的BackStack,是Frame類中的一個屬性,專門用於保存導航歷史記錄:
// // Summary: // Gets a collection of PageStackEntry instances representing the backward navigation // history of the Frame. // // Returns: // The backward navigation stack. public IList<PageStackEntry> BackStack { get; }
當用戶按了back鍵時,你就能夠簡單地用Frame.GoBack()方法來返回到上一頁了。
在Z軸導航模式中,咱們使用App.xaml.cs裏建立的Frame來作全部頁面的導航容器。
Frame rootFrame = new Frame(); Window.Current.Content = rootFrame; rootFrame.Navigate(typeof(PageA));
調用Frame.Navigate(typeof(PageA))時,會把Page A做爲該Frame的Content,同時,這個Frame的值會傳送到Page A的Frame屬性中,二者是一回事兒,是爲了方便再次Navigation時使用。好比this.Frame.Navigate(typeof(PageB)),這裏的this其實是Page A的實例,而this.Frame就是App.xaml.cs裏建立的rootFrame。
Z軸導航的實際UWP的例子不少,如網易雲音樂。Windows Store應用商店App是另一個例子,它的內容也是可擴展的,就是說在窄屏上,能夠一行列出4個項目,在寬屏上能夠根據屏幕寬度列出5~8個項目。還有一個例子就是著名的必應詞典Win10版啦,也是用了一個Frame搞定全部事情。
用這種模式最忌諱的事情是: 當應用場景稍微複雜一些時,也就是頁面較多(8個以上),有可能產生交叉跳轉,可能會形成用戶迷失;而在code方面,形成後退棧愈來愈大,不過這仍是小事情。只要單個Frame的導航能知足你的應用場景就能夠。
舉例來講,若是我在必應詞典裏的背單詞模塊中,想查一個詞,但不想跳轉到search頁,而是想在屏幕右側滑出一個區域來顯示該單詞的詞典解釋,那就須要兩個Frame來完成這個要求了。
不一樣的導航模式,對界面設計的要求是不一樣的,這一點請designer注意。在Z軸單頁模式中,只須要專一每一個頁面的設計便可。但在後面的複雜導航模式中,只這樣作就不夠了。
一維Master/Detail導航模式
在寬大的桌面電腦屏幕上,上面那種在手機的窄屏界面的展現方式是不可接受的。以下圖的Windows 10中的設置頁面,它的窗口是可縮放的,可寬可窄,爲所欲爲。在窄窗口中,顯示一頁的內容;在寬窗口中,side by side地顯示兩頁的內容,合理利用屏幕空間,同時簡化了用戶的操做: 用戶能夠在右圖中的左側列表控件中任意點擊感興趣的條目,在右側能夠馬上獲得響應;可是在左圖中,用戶點擊了Display,進入詳情頁,必須點back鍵回到列表框,才能再進入另一個條目。
咱們再來看看ITHome - IT之家UWP的界面:
整個屏幕分紅三列,最左側的窄條Vertical Menu Bar,中間偏左部分的資訊列表,右半部份的資訊詳情。能夠說這是一個標準的寬屏設計模式,易於理解,易於操做,適合推廣使用。
它對應的XAML應該是這個樣子:
1 <Grid Background="{ThemeResource WhiteColorBrush}"> 2 <Grid.ColumnDefinitions> 3 <!--vertical menu bar--> 4 <ColumnDefinition Width="48" x:Name="menuCol"/> 5 <!--list page container--> 6 <ColumnDefinition Width="2*" x:Name="leftCol" MinWidth="360"/> 7 <!--detail page container--> 8 <ColumnDefinition Width="3*" x:Name="middleCol"/> 9 </Grid.ColumnDefinitions> 10 11 <common:VerticalMenuBarControl x:Name="menuBar" Grid.Column="0"/> 12 13 <!--list content in this frame--> 14 <Frame x:Name="leftFrame" Grid.Column="1"/> 15 16 <!--detail content in this frame--> 17 <Frame x:Name="rightFrame" Grid.Column="2"/> 18 </Grid>
Column-0的寬度爲48 epx (effective pixel),在點擊了上方的漢堡控件展開菜單後,這個寬度能夠調整爲120左右。
Column-1和Column-2的常規比例爲2:3,可是當窗口太窄時,Column-1的寬度有個限制MinWidth="360",避免裏面的內容沒法以合理方式展現。固然也能夠設置一個MaxWidth="720",避免太寬時橫向拉伸太難看。
關於epx的概念,請看這裏:https://msdn.microsoft.com/windows/uwp/layout/design-and-ui-intro 。改天把這篇東西翻成中文給你們洗洗腦吧。
這種設計適合於簡單的二級內容應用場景,想看三級片是不行的了。如今市面上大多數的UWP,均可以用這種方式來實現,如微博UWP就是另一個典型。
若是這樣作的話,是否能讓mobile和desktop兩個device使用同一套UI代碼呢?固然能夠,不然就不能叫作UWP了,只是須要咱們多作一點點事情而已。具體須要作什麼事情,咱們在另一篇博客中(關於Win10 Mobile Continuum的使用方式)會詳細說明。
二維(Z+X)導航模式
用上述的master/detail模式,雖然能夠充分利用屏幕,可是隻適合於場景較爲簡單的應用,如新聞類,閱讀類。下面咱們看一下稍微複雜一些的應用,好比旺信UWP。先看看截圖得到感性認識:
聊天主頁,左側展現好友列表:
右側窗口爲具體聊天頁:
最右側的浮出窗口爲輔助的設置頁面,基本是popup類型的信息:
它的主控頁的XAML是這樣寫的:
<Grid Background="{ThemeResource WXWhiteColorBrush}"> <Grid.ColumnDefinitions> <!--vertical menu bar--> <ColumnDefinition Width="48" x:Name="menuCol"/> <!--list page container--> <ColumnDefinition Width="2*" x:Name="leftCol" MinWidth="360"/> <!--detail page container--> <ColumnDefinition Width="3*" x:Name="middleCol"/> </Grid.ColumnDefinitions> <!--left side vertical menu bar--> <common:VerticalMenuBarControl x:Name="menuBar" Grid.Column="0"/> <!--list content in this frame--> <Frame x:Name="leftFrame" Grid.Column="1"/> <!--detail content in this frame--> <Frame x:Name="middleFrame" Grid.Column="2"/> <!--popup content in this frame--> <Grid x:Name="rightFrameGrid" Grid.Column="0" Grid.ColumnSpan="3"> <Grid.ColumnDefinitions> <ColumnDefinition Width="*"/> <ColumnDefinition Width="500" x:Name="rightCol"/> </Grid.ColumnDefinitions> <Rectangle Grid.Column="0" Fill="Black" Opacity="0.5"/> <Frame x:Name="rightFrame" Grid.Column="1"/> </Grid> </Grid>
在旺信UWP中,咱們使用了三個Frame作navigation。在子模塊開發前,就實現了一個導航framework,使得每一個業務模塊的開發者只須要完成本身的頁面開發,而後直接調用該framework提供的導航方法便可。
固然,這裏的導航再也不是this.Frame.Navigate()那麼簡單,可是也沒複雜到哪裏去,也就是多加了一個參數而已,叫作that.Nav.To(page, left|middle|right),由後面那個參數指定你將要展現的頁面在哪一個frame裏出現。這樣作的基本條件是,作好需求分析,仔細梳理頁面之間的邏輯關係,使得每一個頁面都在一個預訂的frame裏展現。
旺信的導航邏輯能夠抽象成這個樣子:
具體是這樣運行的:
因此,此應用的導航是X+Z的二維模式。這種模式要求interactive designer對該應用的業務很是熟悉,上下文處理得很流暢,不然會有跳躍感。而對於user experience designer,更多地要考慮兩個頁面之間的差異和聯繫,和單頁設計的感受徹底不一樣,兩個頁面既不能長得徹底不一樣(由於有上下文關係),又不能看着差很少,覺得是讓用戶玩「找出不一樣處」的遊戲,由於有上下級關係。
淘寶UWP的應用場景分析
迄今爲止,咱們遇到的最複雜的UWP就是淘寶了。打橋牌的人知道,橋牌是叫牌難,打牌難,計分難,發牌好像最簡單;淘寶是設計難,實現難,測試難,使用起來但是爲所欲爲。
下面咱們先來看一個常見的用戶購物行爲:
琳琅滿目的商品,讓人流連忘返,迷失於虛擬的貨架之間。可是做爲UWP開發者,咱們不能迷失,要理智,理智,理智……老闆,那個快遞費能不能便宜些,再送一個贈品……迴歸理智!
之因此有超級跳轉按鈕的設計,是由於你不可能讓用戶back無數次後才能找到購物車頁面,就好像在超市裏你必須推着一個購物車,能夠隨時放置商品,而且能夠有無數通道通向收銀臺同樣。
下圖演示了此次購物行爲的頁面跳轉過程(篇幅有限,圖片較模糊,見諒):
讓咱們來仔細分析一下這個常見的購物行爲。
第一眼看上去,我丂!頁面是能夠亂跳的!根本不是普通的App那樣的進棧出棧的規矩,看上去像是一種網狀關係。再仔細看看!怎麼好像"詳情"頁出現了不少次呢?
在網購中,詳情是購物的關鍵環節,圖片,價格,評價,快遞,等等,它能夠很大程度決定是否馬上剁手。
懷着剁手黨的激動心情,咱們把詳情用藍色標記一下,變成以下圖:
均可以從哪些地方進入詳情頁呢?順藤摸瓜找上游業務鏈,那就是淘搶購,購物車,店鋪。還有不少其餘頁面能夠進入詳情,篇幅有限,不一一列出,好比天貓,聚划算等等。
因而乎,咱們把詳情的上游業務標記成橙色的,以下圖所示:
這裏要注意,店鋪雖然在下圖中,處於詳情的下游,可是從真實場景看,店鋪的概念顯然是大於詳情的概念的,因此它實際是詳情的上游。那個連接只是系統提供的一種方便的跳轉而已。
咱們還能夠繼續剛纔的思路,看看更遠端的上游和下游是什麼?
先看上游,主頁是一個確定的入口;進入淘寶後,也能夠不幹別的,直接進入購物車,所以咱們把購物車從橙色變爲更高級的紅色;個人淘寶是和主頁、購物車並列的入口,也標爲紅色;地址管理的scenario很是特殊,能夠做爲一個獨立的業務,所以也標爲紅色。
再看下游,詳情後面緊跟着的業務應該是確認訂單和支付,固然也能夠先和商家討價還價,進入旺信聊天頁面,這些個下游頁面咱們標記爲綠色的。以下圖所示:
圖中出現了4種顏色,紅色,橙色,藍色,綠色,也就是說,淘寶的絕大部分業務流程,均可以以詳情爲核心,串聯起先後一共3~5步,成爲業務鏈。而每條業務鏈,就是一個Scenario!
4色定理,怎麼讓我想起了微軟大田村的logo呢?
OK,讓咱們照着這個思路,回過頭瀏覽一下淘寶中的全部頁面,大概能總結出如下幾種scenario:
還有一些分支,處於第3層和第4層,我沒有列出來,不過重要。以上這些足夠咱們整理出一個合理的需求分析了,以下示意圖:
也就是說,主頁、店鋪、購物車、個人淘寶、地址管理等,均可以做爲後續一系列業務的入口看待,咱們把它們叫作Scenario.Home;
淘搶購、搜索、訂單管理、天貓、聚划算等,都是一種業務頻道,引導用戶更方便快捷地購物,咱們稱之爲Scenario.List;
詳情是業務核心,成爲Scenario.Detail;
後面的業務環節能夠叫作Scenario.Chat,裏面能夠放旺信聊天頁,也能夠放下單頁等等。
淘寶,必須作成四核的,須要4個Frame參與導航,才能符合咱們通過縝密需求分析得出的完美業務模型。
因而,咱們能夠把業務抽象成這個樣子:
三維(Z+X+Y)導航模式
若是你沒看懂前面說的二維(Z+X)導航模式,那就goto前一章節從新看。若是看懂了,那下面的東西就很容易理解了,就是在那個基礎上多加了一維Y軸導航。
我們仍是看圖說話吧,請看下圖:
從上到下,一共三層(能夠是N層)淡藍色的面板,每一層表示一個scenario的內部navigation,這一點與前面說的二維導航模式相同,只不過是多了一個frame 4。具體用幾個frame,是由這個App的Scenario決定的,原則上講,用的Frame的數量越少,對系統的performance越有利,code寫着也會簡單些。
淡藍色的面板的數量是隨着用戶navigation的深刻而增長的,隨着按下back鍵而減小的,能夠把藍色的面板定義爲Scenario類。在下面這張圖中,用戶首先在最下面的Scenario A中暢遊了6個頁面(A1,A2,A2',A3,A4,A4'),其中A1->A2作了X軸導航,也就是切換了Frame;在Frame 2中作了Z軸導航,而後從A2->A3作了X軸導航,而後再次X軸導航到A4,在Frame 4中作了最後一次Z軸導航以後,發現了一個神奇的按鈕,切換到了Scenario B,也就是到了第二層面板。以此類推,最後到了Scenario C中,又搞了一些事情。
此時,在Frame 1的導航歷史中,有3個頁面,A1/B1/C1;在Frame 2的導航歷史中,有7個頁面,A2/A2'/B2/B2'/C2/C2'/C2";在Frame 3的歷史中有個3個頁面,A3/B3/C3;在Frame 4中有7個頁面,A4/A4'/B4/B4'/B4"/C4/C4'。
用下面兩個類能夠表示以上數據模型:
public class Scenario { public FrameSlot HomeSlot; public FrameSlot ListSlot; public FrameSlot DetailSlot; public FrameSlot ChatSlot;
public Scenario(Frame scenarioFrame, Frame listFrame, Frame detailFrame, Frame chatFrame, string url) { this.HomeSlot = new FrameSlot(scenarioFrame); this.ListSlot = new FrameSlot(listFrame); this.DetailSlot = new FrameSlot(detailFrame); this.ChatSlot = new FrameSlot(chatFrame); this.Url = url;
} } public class FrameSlot { public Frame frame; // current frame int depth = 0; // navigation times }
在Scenario類的初始化函數中,從外面指定了Frame的實例,也就能夠保證不一樣的Scenario實例能夠共享同一組Frame。
而這些Frame,能夠這樣在XAML中定義:
<Grid x:Name="gridRoot" SizeChanged="gridRoot_SizeChanged"> <Grid.ColumnDefinitions> <!--vertical menu bar--> <ColumnDefinition Width="64" x:Name="menuColumn"/> <ColumnDefinition Width="*" x:Name="contentColumn"/> </Grid.ColumnDefinitions>
<localControls:VerticalMenuBarControl2 x:Name="menuBar" Grid.Column="0"/> <!--for content page--> <Canvas x:Name="panel" Grid.Column="1"> <Frame x:Name="homeFrame" Canvas.ZIndex="10"/> <localControls:VerticalSeperatorControl x:Name="listSep"/> <Frame x:Name="listFrame" Canvas.ZIndex="20"/> <localControls:VerticalSeperatorControl x:Name="detailSep"/> <Frame x:Name="detailFrame" Canvas.ZIndex="30"/> <localControls:VerticalSeperatorControl x:Name="chatSep"/> <Frame x:Name="chatFrame" Canvas.ZIndex="40"/> </Canvas> </Grid>
從XAML中能夠看到,最左側是個Vertical Menu Bar;右側是個Canvas面板。之因此使用Canvas面板,是由於這個panel類型最簡單,能夠任意調整裏面Frame的位置。其它的panel,如Grid, StackPanel, RelativePanel等等都有本身的一套Arrange Children的算法,咱們並不須要。固然,你也能夠本身從panel類派生出一個MyTaobaoPanel類,來組織這4個Frame。
好了,到目前爲止,咱們通過需求分析,找出了合理的模型,制定了基本類和界面邏輯,下面能夠着手開發了……此處略去10000字的開發過程……最後的樣子以下面的截圖所示:
主頁:
在主頁上點擊了淘搶購:
在淘搶購中點擊了第一個商品:
在詳情中點擊了旺旺:
在詳情中點擊了店鋪:
在左側的超級菜單中點擊了個人淘寶:
在個人淘寶中點擊了設置:
頁面太多了,上百種navigation的組合路徑,一網打盡!
如今各位看官可能明白了,「四核」指的是4個Frame參與頁面展現與導航,」三維「指的是在三個維度上的導航方向,一維是在一個frame上的Z軸棧式導航,二維是在兩個frame以上的橫向導航,三維是在兩個Frame以上的橫向導航,再加上虛擬層之間的Y軸導航,切換場景至關於OS的進程切換,只不過是棧式的。
孔子還說:"完美的抽象不能決定軟件的成功與否,但能判別開發者的素質。"
老子也說:"好的開發者是一個軟件成功與否的基石。"
莊子說:……哎,讓莊子喝口茶水吧,實在編不下去了,意思大家都知道的,你們努力提升本身的職業素質吧,多學習,多觀察,多思考,多實踐,更好地抽象這個世界。