在最初接觸Xamarin.Forms的時候,我是跟着Xamarin官方的名為“learning-xamarin-ebook”的pdf文檔進行學習的,我在成功運行Hello world程序之后,我開始跟着pdf寫上面的monkey實例,然而我卻遇到了一個問題,我在列表頁點擊某一個Item的時候,不能如示例上面所展示的那樣跳轉到詳細頁面。也正因為此我在官方文檔和電子書中尋找答案,也將自己的學習的東西做個簡單的筆記和分享。
在Froms的中的提供了如下方法用於導航:
Task PushAsync(Page page)和Task PushModalAsync(Page page)導航到其他頁面;
Task<Page> PopAsync()和Task<Page> PopModalAsync()返回到前一頁;
這四個方法都被定義在一個INavigation接口中,而VisualElement中又定義了一個名為Navigation的只讀的INavigation的屬性,其他Page又直接或間接繼承了這個類。所以我們可以在代碼中直接做如下調用:
await Navigation.PushAsync(Page page)
await Navigation.PopAsync()
然而你要想成功調用這四個導航方法,你必須在App類的構造函數中做一些改變,即使用NavigationPage對你得主頁(MainPage)做一個簡單的封裝,而這也就是我先前跳轉頁面不成功的原因,如下:
為什么要這樣呢?因為畢竟跨平台,只有這樣才能在各個平台上實現Page的生命周期,其實就是為了兼容Android(畢竟英文,有些記得不是太清楚,有問題大家指正)。這四個方法都提供了另一個重載,都多了一個參數:bool animated(默認為true),根據參數名,你應該也知道這個參數的作用了,但實際上,默認的跳轉頁面的動畫還算可以,通用的那種,當然貌似也可以重寫,在電子書的動畫那章應該有講,不過目前我還沒看。。。。。。
好了,在繼續正題之前,還有兩個要點,一是在Froms中的Page在各個平台中是不一樣的,一個Froms的Page相當於IOS的view Control,也相當於WP的Page,但是並不是Android的Activity,這里給大家給大家貼一下電子書中的原文,官網中的自己搜一下就行
Programmers familiar with Android architecture are sometimes curious how Xamarin.Forms page navigation integrates with the aspect of Android application architecture known as the activity. A Xamarin.Forms application running on an Android device comprises only one activity, and the page navigation is built on top of that. A ContentPage is a Xamarin.Forms object; it is not an Android activ-ity, or a fragment of an activity.
也正是因為各個平台的不同,Page中的可重寫的方法的調用順序也多不同。這個后面說。第二個就是modal page和modeless page,正常的講在各個平台都沒有對他們明確的區分和定義,在視覺上最直觀的區別就是默認情況下(導航欄也是可以通過設置進行顯示和隱藏)在左上角是否有返回按鈕(WP沒有,WP和Android都有返回的物理按鍵,只不過Android在頁面也有返回鍵),而在代碼中則體現為跳轉頁面時調用的是PushAsync還是PushModalAsync方法。如下圖(截得的電子書中的圖):
如上圖左邊是modeless page而右邊則是modal page,正如你所看到的,如果你跳轉的頁面是modeless page在各個平台,你是不需要在頁面中再提供返回按鍵的,而modal page則有不同,由於WP和Android都有物理的返回按鍵支持,所以如果你在modal page中不提供返回按鈕,也並沒有關系,然而在IOS中,你必須提供,為什么,當然是因為它沒有提供返回的物理按鍵,只有Home鍵。當然WP和Android的物理返回按鍵也是也已禁用的,如果你存在非要用戶完成某些操作的“流氓”行為,就是重寫OnBackButtonPressed方法即可,如下:
protected override bool OnBackButtonPressed() { if(flag) { return base.OnBackButtonPressed(); } return true; }
並且modal page只能跳轉到另一個modal page,而modeless page能跳轉到modal page和modeless page。
現在說一下NavigationPage的常用屬性和方法:
1、BarBackgroundColor和BarTextColor都是Color類型的屬性,看名字就知道其用途,這兩個屬性在IOS和Win10都會生效,在Android上去只有背景色(BarBackgroundColor)會生效.
代碼如下:
public static Page GetMainPage() { var monkeyPage = new MonkeysPage(); return new NavigationPage(monkeyPage) { BarBackgroundColor=Color.Green,BarTextColor=Color.Blue }; }
如圖(我只有安卓機,所以自己的圖只有安卓的,對比圖我就直接截電子書的,以后都是):
官方免費電子書對比圖如下:
2、SetBackButtonTitle方法,這個只有IOS才會生效,應為只有IOS會顯示前一頁的Title。
3、SetTitleIcon方法,這個在IOS中將替換Title屬性,Android替換圖標。
以上兩個並不會作用於Windows和WP。
4、SetHasNavigationBar和SetHasBackButton這兩個就是分別設置是否顯示導航欄和是否顯示導航欄的的返回圖標。
方法並不能在App中設置,只能在Page的構造函數中設置,調用方式如下:
namespace App3.Views { public partial class DetailsPage : ContentPage { public DetailsPage(Monkey monkey) { InitializeComponent();
//其他方法大同小異 NavigationPage.SetHasNavigationBar(this, false); this.BindingContext = monkey; } } }
咱們繼續,當我們調用Push或者Pop方法是返回的總是Task對象,是不是意味着如果我們使用await來調用方法,會出現等待呢?官方的描述為:task completes rather quickly。而且官方建議在調用Push或者Pop方法的時候,一定要用await,為什么?因為在某些情況下page stack是不安全的,我需要使用await來確保已經獲取到了下一個頁面的實例,並且已安全的寫入到了page stack中。如果你在看這篇文章的時候你也F12到了INavigation接口中,你會看到接口內定義了兩個IReadOnlyList<Page>的對象。
IReadOnlyList<T>看名字你就知道他是一個只讀的,但是他跟Stack<T>相似,都是先進后出,或者你就把它當作是Stack<T>理解也行,只不過前者是只讀的而已。在IOS中ModalStack始終為空的。
我們NavigationPage對象中的CurrentPage的值為NavigationStack的最后一個進棧的,簡單點就是CurrentPage如果有值,那只能是modeless page。
在Page對象中我們還看到了兩個可以重寫的方法:
protected virtual void OnDisappearing();
protected virtual void OnAppearing();
這兩個方法又是什么時候執行,執行順序又是什么?我們這里用PushAsync和PushModalAsync:
1、頁面調用PushAsync或者PushModalAsync通常將會獲取當前頁面的OnDisappearing方法重寫的調用
2、然后獲取下一個頁面的OnAppearing方法的重寫的調用
3、PushAsync或者PushModalAsync完成
而當我們調用PopAsync或者PopModalAsync的執行順序如下:
1、頁面調用PopAsync或者PopModalAsync將會獲取當前頁面的OnDisappearing方法重寫的調用
2、然后通常獲取上一個頁面的OnAppearing方法的重寫的調用
3、PopAsync或者PopModalAsync完成
是的,我把”通常“用紅色的標注了,為什么,先前也說過Froms Page並不是一個Android的Actively,所以有些不同,調用PushModalAsync的時候不會調用OnDisappearing,而調用PopModalAsync的時候是不會調用前一頁的OnAppearing,至於為什么,當我把下一個頁面設置為透明(在構造函數中設置BackgroupColor=Color.FromRgba(0,0,0,0.5)),然后用modal page的方式導航過去的時候就知道了,如下圖:
是的,如你所見,前一頁並沒有銷毀,而是存在的,so。。。不需要我再解釋了吧!如果你非要問為什么沒有銷毀,我的答案是:呵呵!
而在IOS中,OnDisappearing的調用是在一個頁面完全銷毀的時候,而不是在調用Push或者Pop的時候。
好了,就這么,艱苦的一年啊!