博客园客户端UAP开发随笔 -- 奔跑吧,页面!

前言

页面导航,是App中的基本功,一般的App,一来一去,只需要简单的Navigate + Back就行了,一个复杂的App可能需要很多导航模式的混合才能实现最佳用户体验。

SplashScreen 启动屏幕

我们先从最开始的SplashScreen说起吧。如果你把启动屏幕做成一个Page,启动时先显示一下,然后假装忙乎两秒,跳到下一个主页面开始进入正题,这个好像看上去也很美好。但是当用户玩命儿按Back键时,哦,露出马脚了,启动页面被唤出了。不过这个bug倒是不妨作为一个新奇的体验。

MSDN里有一节专门讲了如何添加一个SplashScreen,但是说实话,我试了两次,都没成功,人太笨!

算了,自己想办法吧!如果在MainPage里加一个开关会不会简单一些呢?于是这样改装MainPage.xaml:

<Page x:Name="page"
… >
    <Grid>
        <Grid x:Name="grid_Splash">
            <Image Height="100" Source="ms-appx:///Assets/Logo.100.White.png" />
        </Grid>
        <Grid x:Name="grid_Main" Visibility="Collapsed">
            ……content here……
        </Grid>
    </Grid>
</Page>

这里把不重要的代码都删除了,只看干货:<Grid x:Name=”grid_Splash”>,这一项定义了一个Grid, 盖在了主要内容的前面,因为下面的<Grid x:Name=grid_Main Visibility=”Collapsed”>在初始状态被搞成隐藏了,如此一来,grid_Splash中的Image就会在应用启动后,首先映入眼帘。

什么时候把它从用户眼中抠走呢?有两种方法可供选择。

1)在MainPage的构造函数里开始装载你的data,比如是从远程,考虑到网络状况,可能需要几秒钟。那么你就在Splash里放一个ProgressRing,让它转啊转,转啊转……差不多等用户烦了,你的远程数据也拿回来了,然后写一句:

this.grid_Splash.Visibility = Windows.UI.Xaml.Visibility.Collapsed;
this.grid_Main.Visibility = Windows.UI.Xaml.Visibility.Visible;

如此一来Splash被隐藏,你的主要内容在数据来到后也化妆完毕(绑定好了),可以出来见公婆了。

2)如果你不需要从远程调用数据,而是从本地取数据,那么上面的过程就会一闪而过,晃瞎用户的K金G眼,体验很糟糕。这时你可以采用第二种办法:在启动应用时启动一个2秒的计时器:

ThreadPoolTimer.CreateTimer(SplashTimeOut, new TimeSpan(0, 0, 2));

其中定义了回调函数(应该叫做delegate),当两秒时间到时,在函数SplashTimeOut里面:

void SplashTimeOut(......)
{
    this.grid_Splash.Visibility = Windows.UI.Xaml.Visibility.Collapsed;
    this.grid_Main.Visibility = Windows.UI.Xaml.Visibility.Visible;
}

这样会和第一种方法的体验一样,只不过是假装忙活了一下,干等2秒而已,目的是让用户有一种有人替他干活的虚荣感,而且让你漂亮的启动页面给用户流下深刻印象。

Basic页面

除了MainPage以外,如果你还有其它二级页面需要添加的话,请在VS中选择这里:

你如果偏爱Blank Page我也不拦着你,但是Basic Page这个选择能帮你省老鼻子事儿了,它在你的项目中自动添加了这些帮助文件:

其中,NavigationHelper.cs里面实现了在页面上处理Back按键的事件,即,当用户在底层页面按Back键时,会回到上级页面。但是在主页面中不建议处理Back键,而是让系统自动处理,把App放到后台。

基本页面导航

基本页面导航MSDN里有,和Silverlight有很大不同。记得SL理面有个什么NavigationService,听上去蛮不错的,到了WinRT里,一律用这个:

this.Frame.Navigate(typeof(NewsReadingPage), obj);

如果在Control里面做页面导航,比如按了个自定义Control,而上层代码又不太容易能得到具体是按了哪个Control,就只好在Control里触发导航动作了,尽管我们不建议这样做:

Frame frame = Window.Current.Content as Frame;
frame.Navigate(typeof(....),....);

这里的Frame很模糊,看上去像个全局的,但是在前面那个例子里,又是用this.Frame,就是说在Page对象里还有个Frame。有个文档专门说这事儿,但是绕来绕去的我没看懂,人太笨!如果有搞明白了这事儿的园友们可以给大家一个说明,谢谢先!

参数传递

基本页面导航中的obj,就可以当作参数传递给下级页面。但是有时候一些下级页面需要返回一些信息回来给调用者,怎么做呢?因为在页面的GoBack()方法中没有参数。有三种方法可以解决这个问题:

1)利用好那个obj,把它即作为[in],又作为[out],下级页面给obj里面的字段赋值,上级页面通过解析obj里的约定字段来得到参数。

2)在下级页面中用static字段,返回之前给它赋值,然后上级页面可以使用。

3)弄个外部类,比如一个静态类,或者一个单例的类,弄个变量在里面当参数。注意用完之后带上手套把指纹擦掉,把该变量“归零”就可以了,隐藏你的作案痕迹,避免下次使用时搞不清当前状态。

前跳式页面导航

z博客园UAP里没有这个例子,用我们做的另一个App--豆瓣一刻来举例说明吧(顺便做一广告,豆瓣一刻 for WP已经上线了,名字叫做“一刻”,link is here:一刻)。

咱们看图说话(注意,以下逻辑很绕,没有耐心的可能看不懂):

按正常逻辑,阅读文章时,可以查看评论;如果想发表评论,点击下方按钮,进入“写评论”页面。但是此时用户可能还没有登录,不能匿名发表评论,所以需要自动跳到登录页面,登录成功后,自动进入写评论页面。

每当PM说起“自动”这个词时,我就头大!什么所谓自动,都是我们程序员手动搞出来的!有时候自动能实现,有时候实现不了,这个要和PM讲清楚。

针对这个具体例子,有几种方式备选,我们先看第一种:

1)看评论页->点击发表评论->发现没登录->进入登录页->登录成功->进入发表评论页

这个流程是最朴素的想法了,但是先别动手,仔细想想:在用户提交评论后,页面该回到哪里?从目前的情况看,是回到登录成功的页面了,会让用户感到困惑。而且,在点击发表评论按钮后,还需要做一个分支判断:如果用户登录了,直接到写评论页;如果用户没登录,要进入登录页面。

看看第二种:

2)看评论页->点击发表评论->进入写评论页->发现没登录->进入登录页->登录成功->“自动”返回写评论

这个看上去好一些,没有第一种方式的两个缺点。用户写完评论后,从stack看,是能直接回到最开始的看评论页的。但是需要解决的问题是如何“自动“返回写评论页?我的刁民小计是:

a) 在CommentWritingPage.Page_Loaded事件中,判断用户是否登录:

private void Page_Loaded(object sender, RoutedEventArgs e)
        {
            if (!Settings.Current.IsLogin)
            {
                if (this.backFromLogonPage)
                {

                }
                else
                {
                    this.Frame.Navigate(typeof(SettingsPage2), new DataModel.SettingNavigationParameter() { targetPivot = TargetPivotItemName.LogonAndBack, targetCss = 0 });
                }
            }
        }

如果登录了,啥也不做,留在当前写评论页面即可;如果没登录,跳到SettingsPage.Logon页面,并且带上一个参数: LogonAndBack。

b) 在SettingsPage.Logon页面,当有登录成功的事件返回后(因为登录是一个异步过程),判断参数是不是LogonAndBack:

void Current_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
        {
            if (Settings.Current.IsLogin && this.logonAndBack)
            {
                Settings.Current.PropertyChanged -= Current_PropertyChanged;
                this.Frame.GoBack();
            }
        }

如果是LogonAndBack,就用this.Frame.GoBack()返回调用者页面,也就是发表评论页面。

如此一来,当用户发表完评论后,自动回阅读评论页面了,行云流水(本来想说飞檐走壁,但是想了想,觉得还没那么离谱)!

需要特别说明的是,在CommentWritingPage中,要在Page_Loaded事件中才能跳转到其它页,如果在OnNavigatedTo()事件中调用this.Frame.Navigate(),nothing happened,没用,什么也不会发生。

后跳式页面导航

让我们来看一个更复杂的例子。下图是我们开发的另一个App,还没有弄完,功能类似个浏览器。

第一张图是在一个WebViewPage里,已经加载了一个页面,点击右上角的窗口管理,进入第二张窗口管理页面(比较丑陋,因为designer休假了,还没完工)。可以看到一共6个窗口,只有第一个窗口被使用了。此时我们点击第二个窗口,想启动一个新窗口来浏览其它网站。按理说应用程序应该把第二个窗口激活,但是窗口中啥都没有,大白板一个,用户体验很差,应该自行惭愧地立刻返回到主控页让用户有更多的选择项(就是目前所显示的第三张蓝色页面)。这个如何做?

一个很自然的想法就是,从窗口管理页Navigate到主控页。不行滴!如此一来,当用户在主控页按Back时,会回到窗口管理页,而窗口管理页只是一个辅助页面,类似弹出式菜单,不应该在stack里存留。主控页是程序的根,按Back时必须要退出应用。

解决办法是在窗口管理页里收到点击事件后,返回到WebViewPage(第一张图):

// 窗口管理页的点击事件
private void lv_ItemClick(object sender, ItemClickEventArgs e)
        {
            WebViewHelper wvh = e.ClickedItem as WebViewHelper;
            this.pool.SetActiveWindow(wvh);
            if (this.Frame.CanGoBack)
            {
                this.pool.BackFromStatusPage = true;
                this.Frame.GoBack();
            }
        }

在此页面中,判断当前活动窗口是否为空,注意,也是要在Page_Loaded事件中处理:

private void Page_Loaded(object sender, RoutedEventArgs e)
        {
            Windows.Phone.UI.Input.HardwareButtons.BackPressed += HardwareButtons_BackPressed;

            if (this.pool.BackFromStatusPage)
            {
                this.pool.BackFromStatusPage = false;
                if (this.pool.GetActiveWindow().IsEmptyView)
                {
                    // back to main page to wait for input
                    if (this.Frame.CanGoBack)
                    {
                        this.Frame.GoBack();
                    }
                }
                else
                {
                    // stay at webview page to show current web content
                }
            }
            else
            {
                this.ctrl_Input.Url = this.wvActive.Url;
            }
        }

如果IsEmptyView == true,再调用GoBack返回到主控页面(第三张图),而不是跳转(前进)到主控页面。

这个solution的基本思路,或者说是设计理念,就是窗口控制页面(第二张图)一定是个叶子节点,不能让它作为中间导航节点。

小结

我和一些桥牌的初学者讲过很多次:打每一张牌都要有你的思路,不能说”红桃花色没打过,我试试看“,而是说”从叫牌过程分析,我的同伴有红桃大牌,我要帮助他一下,穿过明手的红桃Q”。写程序做设计也一样,当有多种选择时,一定要首先确定一个设计理念,然后再确定解决方法。

分享代码,改变世界!

Windows Phone Store App link:

http://www.windowsphone.com/zh-cn/store/app/博客园-uap/500f08f0-5be8-4723-aff9-a397beee52fc

Windows Store App link:

http://apps.microsoft.com/windows/zh-cn/app/c76b99a0-9abd-4a4e-86f0-b29bfcc51059

GitHub open source link:

https://github.com/MS-UAP/cnblogs-UAP

MSDN Sample Code:

https://code.msdn.microsoft.com/CNBlogs-Client-Universal-477943ab

MS-UAP

2015/2

时间: 2024-10-15 22:40:58

博客园客户端UAP开发随笔 -- 奔跑吧,页面!的相关文章

博客园客户端UAP开发随笔 – 让自己的App连接世界(2):WinRT中的内置分享

看到一篇眼前一亮的博文,是不是有一种希望其他小伙伴都能看到的感觉呢?有没有一种“不转不是程序员”的冲动呢?在 PC 浏览器上看到还好办,直接网址复制,另一边 IM 上就发过去了,但是如果是 App 中的内容,就没这么方便了,总不能那边 IM 上喊话:“隔壁老王,博客园上有篇叫‘博客园客户端(Universal App)开发随笔 – 为应用插上分享的翅膀’的博文超好看,要不你也瞅瞅?”.隔壁老王再去搜索就太麻烦了.可能你会说了,嗨,直接分享不就完了么.嗯,没错,就是分享功能.那么如何把分享功能引入

博客园客户端UAP开发随笔 -- 搭建App之间的桥梁

开发Windows Phone应用的同学们应该都注意到了,Windows Phone 为了安全性,对应用的限制还是比较多的.我记得一位360的同学很无奈的说:WP太安全了,我们这些做WP上360卫士的基本上没啥可做的.但是当WP360那个App出来后,还是有很多用户安装了呢,尽管真的对安全没什么太大的帮助,但是对用户了解自己的手机的使用情况还是有帮助的.其中一位用户的评价是:从android到PC,我一直用360, 所以在WP上我也用.呵呵,粉丝啊!具体有什么用其实他也不关心铁粉而已. 扯远了!

博客园客户端UAP开发随笔 -- App的心动杀手锏:动画

前言 在前面一篇“新年快乐”的随笔中,我们介绍了WinRT中的简单动画实现.其实在使用Windows/Windows Phone时,我们都会看到一些动画,最简单的比如按下一个button时,该button的状态变化就是动画的一种.再比如弹出式窗口或菜单,也是一种动画.WinRT中的动画种类很多,但是分类有点儿让初学者摸不着头脑:主题过渡,主题动画,视觉转换,情节提要动画.这些我们就不说了,这里主要说说自定义动画,或者说是情节提要动画(Storyboard Animation),因为这种动画是我们

博客园客户端UAP开发随笔 -- App连接云端内容的桥梁:WebView

当你辛苦的从网上爬下来一篇文章之后,怎么在你的应用内展示这些包含HTML标记的文章?如果你使用的是Javascript开发应用,恭喜你,直接塞进页面就可以了,同时说明你很熟悉页面开发,而现在windows也支持这种方式.但是对于使用XAML开发的应用怎么办呢?我们还有WebView控件可以用. 越来越多的服务器端API返回的数据使用HTML了,所以我们也不得不对WebView多了解一些. WebView有个Bug:放在Grid里时,最右侧有一个pixel缝隙时隐时现.要小心,别让PM抓住你的小辫

博客园客户端UAP开发随笔--自定义控件的左膀右臂

前言 我们上一次说到了App的精灵:自定义控件.这一次,我们接着这一话题,说说自定义控件的两个得力助手: 选择器 - TemplateSelector 转换器 – Converter 这两个东西能帮助自定义控件更为简单方便地被使用,所以必须掌握. 数值转换器 Converter 这个大家可能不陌生,因为在MSDN里,介绍到Data Binding时,总会顺带着介绍一下数据转换,比如这个网页: http://msdn.microsoft.com/library/windows/apps/xaml/

博客园客户端UAP开发随笔 -- App UI设计的三大纪律八项注意

前言 每一个页面都是这个App的门面,尤其是主页面,看上去干净整洁清爽宜人容易操作,那么你的App就成功了一半.这也反映出了你这个开发团队的基本审美素质和设计理念.如果你不是一个团队,而是一个个人开发者,建议你好好读读以下心得体会,相信会帮助你做出好看而实用的App.用一堆拥有丑陋UI的App充斥Window Store,不是我们高大上的程序员所为,被其他手机开发平台的开发者们耻笑. 三大纪律: 1)不乱用颜色.一个页面内不要超过3种颜色 2)不乱用大图片当背景.你是想让用户看你的背景图片呢,还

博客园客户端UAP开发随笔 -- 让你自己和你的App有国际范儿

大家是不是发现,在商店中看到的高大上的应用都有着多语言支持,可以根据操作系统的语言自动适配:或者可以通过用户的选择,显示对应的语言界面,确实很高大上呢.不过这个可不是什么难事,通过简单的几个步骤,让你的应用也高大上起来,支持多语言.这样在你以后的简历里,也可以写上:面向国际市场开发过多语言应用.听上去相当有底气! 0. 准备工作 在建立多语言支持前,首先要看一下 Manifest 文件中的默认语言选项,将它设置为你希望的默认显示语言. 接下来建立存放语言字符串的文件夹.如果是Universal

博客园客户端UAP开发随笔 -- 狡兔三窟:App内的三种通知消息的实现

使用应用时,总会有各种各样的交互,其中有些是需要和用户交互的,有些是仅仅告知用户某些信息的.对于前者,通常的解决方案都是弹出一个带有按钮(或其他控件)的对话框,上面有需要用户知晓的信息,以及需要用户通过按钮(或其他控件)做出的响应交互,这里就不再介绍.对于后者,那些不需要用户做出交互,仅仅是告知用户信息的,实现方式大家各有不同,本文将提出几种解决思路,抛砖引玉,希望通过交流,得到更好的人机交互解决方案. 1. 弹出窗口提示 这个方法比较简单粗暴,直接调用了系统的 MessageDialog 方法

博客园客户端UAP开发随笔 -- 让自己的App连接世界:WinRT中的微博分享

近年来社交app可谓红红火火,大家每天发微博,分享到朋友圈也是不亦乐乎.我们的Universal应用自然也希望加入社交分享的功能.然而国内主流的社交平台微博和微信还没有推出适用于Universal应用的SDK,怎么办呢?当然敲碗等开饭也是办法,另一方面我们也可以自己做一些努力.今天我们就想分享一下我们在Universal应用中实现分享到微博功能上的探索. 准备 想要分享到微博,先得注册成为微博开发者.我们在http://open.weibo.com/上注册好开发者账号,建立个测试应用,就可以拿到