前面有一篇博客說到了淘寶UWP的"四核驅動的三維導航—淘寶新UI(需求分析篇)",花了兩周的時間實現了這個框架,然後又陸陸續續用了三周的時間完善它。 多視窗導航,與傳統的導航方式的最大的不同點是: 本篇博客先說一下前三個問題如何解決。 在從頁面A跳到頁面B時,單視窗模式下,很簡單,只要在頁面A中調用 ...
前面有一篇博客說到了淘寶UWP的"四核驅動的三維導航—淘寶新UI(需求分析篇)",花了兩周的時間實現了這個框架,然後又陸陸續續用了三周的時間完善它。
多視窗導航,與傳統的導航方式的最大的不同點是:
- 需要指定目標視窗(target Frame)
- 維護上下文關係(context)
- Back Stack的維護
- 頁面間的非同步通信
- 適配Desktop和Mobile界面
- 支持Continuum(optional)
本篇博客先說一下前三個問題如何解決。
在從頁面A跳到頁面B時,單視窗模式下,很簡單,只要在頁面A中調用系統方法就可以了:
this.Frame.NavigateTo(typeof (PageB));
因為頁面A使用的Frame和頁面B使用的Frame相同。但是在多視窗(多Frame)時,你需要指定頁面B使用那個Frame作為目標視窗。
比如,淘寶首頁:
在點擊了"淘搶購"頻道的入口後,你想在什麼地方顯示淘搶購的主頁面呢?在目前的Taobao UWP中,我們選擇在主頁的右側顯示(但是考慮到淘搶購的使用頻率,我們可能會考慮在下一版中把它作為一個獨立的Scenario來對待,也就是說可能要在目前的主頁的位置顯示)。於是,界面就會變成這樣:
這就要求在導航方法中多一個參數來指定具體的位置。按照我們的設計,在Taobao UWP中有以下4個Frame承擔具體的導航工作:
Home Frame:作為一個Scenario的首頁,必須存在。
List Frame:顯示商品列表,可以存在。有些Scenario,它的首頁就是一個商品列表,就在Home Frame里顯示了,List Frame可以不存在。
Detail Frame:詳情頁,必須存在。基本上所有的商品,都是要通過詳情頁來展示具體的商品信息的。當然,在別的Scenario中,如"我的淘寶"場景,可以用此Frame顯示別的非商品內容。
Chat Frame:附加頁,一般用於顯示內置旺信聊天視窗,必須存在。
淘寶首頁是顯示在Home Frame中的,點擊淘搶購入口後,code里都做了什麼壞事兒呢?
Nav.To(NavToUrls.RushBuy_Home, mode: NavMode.List_Replace);
其中,Nav是我們自定義的一個導航幫助類,To()是它的靜態方法,定義如下:
public static bool To(string url, object param = null, NavMode mode);
參數解釋:
- string url:這個是必須的。我們把所有的頁面都指定了一個URL,淘搶購的主頁的URL定義如下:
public const string RushBuy_Home = "taobao://go/RushBuy/index";
- object param:指定頁面間傳遞的參數。
- NavMode mode:指定導航模式,是一個枚舉值,定義如下:
public enum NavMode { Default, // show page in scenario frame Home_Add, // show page in scenario frame List_Add, // show page in list frame, keep old page (if exists) in back history List_Replace, // show page in list frame to replace the old page (clear old page in back history) Detail_Replace, // show page in detail frame to replace the old page (clear old page in back history) Detail_Replace_Clear, // same as above, also delete page in list frame Chat_Add, // show page in chat frame, keep old page (if exists) in back history Chat_Replace, // show page in chat frame to replace the old page (clear old page in back history) Chat_Replace_Clear, // same as above, also delete pages in list/detail frame }
每個枚舉值都包含有兩個信息:位置,方式。如List_Add,下劃線前面的"List"表示位置,在List Frame中顯示目標頁面;下劃線後面的"Add"是方式,一共有三種方式:
- Add:如果在目標Frame中已經有其它頁面存在,則在已有頁面基礎上再做一次Navigation,也就是back stack歷史中會多出一個記錄。
- Replace:如果目標Frame中已有其它頁面存在,則"刪除"它,也就是在back stack中清空歷史記錄,然後做一次Navigation,最後back stack中只有最新的頁面記錄。
- Replace_Clear:同Replace,另外,如果前面的Frame中(除了Home Frame以外),如果有頁面存在,則清除它們。
我們逐個解釋一下這些參數的用法。
為什麼需要Add/Replace方式?舉個例子,在淘寶首頁,假設用戶先點擊了天貓:
然後用戶又在左側首頁上點擊了淘搶購:
由於天貓和淘搶購都是在List Frame中顯示,於是淘搶購把天貓覆蓋了。此時用戶按Back鍵,希望看到什麼?顯然不是要回天貓!於是,我們是這樣指定淘搶購的導航方式的:
Nav.To(NavToUrls.RushBuy_Home, mode: NavMode.List_Replace);
它指定了淘搶購在List Frame中顯示,用Replace方式,也就是清掉前面的天貓的痕跡。
何時用Add方式呢?比如用戶在首頁點了搜索:
右側進入了搜索起始頁面,用戶輸入搜索詞後按回車,再進入搜索結果頁面:
此時用戶想改一下搜索的關鍵字為lumia 950 xl,點擊一下搜索結果頁上方的搜索框,就會回到搜索起始頁。對於這個Scenario,我們需要這樣使用導航方法:
1)在首次進入搜索起始頁時,使用Replace方式來清空歷史:
Nav.To(NavToUrls.Go_Search, mode: NavMode.List_Replace);
2)在進入搜索結果頁時,使用Add方式附加搜索結果頁:
Nav.To(NavToUrls.Search_Result, mode: NavMode.List_Add);
如此一來,當用戶在搜索結果頁按返回鍵,或者點擊搜索框(等同於按返回鍵)時,就會在List Frame中調用Navigation Back方法,回到搜索起始頁,因為此時搜索起始頁一直在List Frame的歷史stack中等候。
何時使用Replace_Clear這種方式呢?對於有些Scenario,有一些特殊的導航需求,主要是為了保證左右兩側視窗的上下文關係。比如店鋪:
假設我們先看了店鋪簡介,現在左右兩側的視窗內容是有上下文關係的。此時,店鋪首頁在Home Frame中,店鋪簡介在List Frame中。我們用這個方法來顯示店鋪簡介頁:
Nav.To(NavToUrls.Shop_Brief, param: ViewModelData.ShopInfo?.ShopId, mode: NavMode.List_Replace);
現在,左右兩側的情況是:Home Frame <-> List Frame。然後用戶在左側的店鋪首頁上點擊了一個商品,想看該商品的詳情,頁面就會變成這個樣子:
此時左右兩側的情況是:Home Frame <-> Detail Frame。那麼剛纔的用於顯示店鋪簡介的List Frame哪裡去了呢?被清掉了!因為我們使用了這個方式來顯示詳情頁:
Nav.To(data?.H5Url, mode: NavMode.Detail_Replace_Clear);
請大家註意第二個參數,使用了Detail_Replace_Clear方式,表示在Detail Frame中顯示詳情頁,Replace該Frame中以前的內容,並且Clear掉List Frame中的所有頁面。於是,店鋪簡介頁面神奇地消失了,因為它不符合現在的上下文關係。當然,此時還需要其他一些手段來隱藏空白的List Frame,比如判斷List Frame的Content是否為空。
寫累了,喘口氣。剛纔在寫博客做截圖時,還發現了個Navigation的bug,順手給fix了,是調用參數寫錯了,應該寫成Detail_Replace_Clear,但是寫成了Detail_Replace,導致現線上上的版本,在店鋪頁,先點擊店鋪簡介,再在左側點擊全部寶貝,再點擊任意商品,此時,隨著視窗左滑,左側是店鋪簡介,右側是一個商品詳情,上下文關係不對。
本篇博客只是簡述了多視窗導航的原理和一些code片段,具體的實現可以根據個人應用場景的需求自己定製,尤其要註意多視窗之間的上下文關係。下次再說說頁面間通信和屏幕適配吧,Continuum其實也是一種屏幕適配。