在最初接触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,所以有些不同,调用PushAsync或者PushModalAsync的时候不会调用OnDisappearing,而调用PopAsync或者PopModalAsync的时候是不会调用前一页的OnAppearing,至于为什么,当我把下一个页面设置为透明(在构造函数中设置BackgroupColor=Color.FromRgba(0,0,0,0.5)),然后用modal page的方式导航过去的时候就知道了,如下图:
是的,如你所见,前一页并没有销毁,而是存在的,so。。。不需要我再解释了吧!如果你非要问为什么没有销毁,我的答案是:呵呵!
而在IOS中,OnDisappearing的调用是在一个页面完全销毁的时候,而不是在调用Push或者Pop的时候。
好了,就这么,艰苦的一年啊!