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

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

1. 弹出窗口提示

这个方法比较简单粗暴,直接调用了系统的 MessageDialog 方法,弹出一个窗口,需要用户点确定返回。效果如下图所示:

代码也比较简单,调用系统方法,传入需要显示的字符串即可。

public static async Task ShowMessage(string message)
        {
            var msgbox = new Windows.UI.Popups.MessageDialog(message);

            var result = await msgbox.ShowAsync();
        }

这是一种最简单的处理办法,无需太多代码就可以实现一个应用内的通知提醒,但是缺点是界面比较单调,而且需要用户手动点关闭返回,增加一步交互成本。如果频繁出现可能会让用户厌烦。

2. 自定义控件显示

这种方法比较灵活,思路是自定义一个控件,平时隐藏,需要应用内通知的时候,以类似 notification bar 或其他方式弹出,几秒钟以后自动消失,无需用户干预,界面也可以完全自定义。

举例如下:

首先添加 Templated Control

自定义样式:

<Grid x:Name="mainGrid"
                        Width="1920" Height="60" VerticalAlignment="Top" HorizontalAlignment="Center"
                          Background="{ThemeResource CNBlogsSummaryColor}" Visibility="Collapsed">
                        <Grid.RenderTransform>
                            <TranslateTransform x:Name="GridTrans" Y="-60"></TranslateTransform>
                        </Grid.RenderTransform>

                        <TextBlock HorizontalAlignment="Center" VerticalAlignment="Center"
                            Style="{ThemeResource NotificationBarFont}"
                                   x:Name="tb_Notify">
                        </TextBlock>
                    </Grid>

添加弹出时的动画:

<Grid.Resources>

                            <Storyboard x:Name="tb_Notify_in">
                                <ObjectAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="mainGrid" Storyboard.TargetProperty="(UIElement.Visibility)">
                                    <DiscreteObjectKeyFrame KeyTime="00:00:00">
                                        <DiscreteObjectKeyFrame.Value>
                                            <Visibility>Visible</Visibility>
                                        </DiscreteObjectKeyFrame.Value>
                                    </DiscreteObjectKeyFrame>
                                    <DiscreteObjectKeyFrame KeyTime="00:03:00">
                                        <DiscreteObjectKeyFrame.Value>
                                            <Visibility>Collapsed</Visibility>
                                        </DiscreteObjectKeyFrame.Value>
                                    </DiscreteObjectKeyFrame>
                                </ObjectAnimationUsingKeyFrames>

                                <DoubleAnimationUsingKeyFrames Storyboard.TargetName="GridTrans"
                                Storyboard.TargetProperty="Y"
                                BeginTime="0:0:0">
                                    <SplineDoubleKeyFrame  KeyTime="00:00:00.00" Value="-60"/>
                                    <SplineDoubleKeyFrame  KeyTime="00:00:00.10" Value="-38"/>
                                    <SplineDoubleKeyFrame  KeyTime="00:00:00.20" Value="-22"/>
                                    <SplineDoubleKeyFrame  KeyTime="00:00:00.30" Value="-10"/>
                                    <SplineDoubleKeyFrame  KeyTime="00:00:00.40" Value="-3"/>
                                    <SplineDoubleKeyFrame  KeyTime="00:00:00.50" Value="0"/>
                                    <SplineDoubleKeyFrame  KeyTime="00:00:02.50" Value="0"/>
                                    <SplineDoubleKeyFrame  KeyTime="00:00:02.60" Value="-3"/>
                                    <SplineDoubleKeyFrame  KeyTime="00:00:02.70" Value="-10"/>
                                    <SplineDoubleKeyFrame  KeyTime="00:00:02.80" Value="-22"/>
                                    <SplineDoubleKeyFrame  KeyTime="00:00:02.90" Value="-38"/>
                                    <SplineDoubleKeyFrame  KeyTime="00:00:03.00" Value="-60"/>
                                </DoubleAnimationUsingKeyFrames>

                                <DoubleAnimationUsingKeyFrames Storyboard.TargetName="mainGrid"
                                Storyboard.TargetProperty="Opacity"
                                BeginTime="0:0:0">
                                    <SplineDoubleKeyFrame  KeyTime="00:00:00.00" Value="0"/>
                                    <SplineDoubleKeyFrame  KeyTime="00:00:00.50" Value="0.9"/>
                                    <SplineDoubleKeyFrame  KeyTime="00:00:02.50" Value="0.9"/>
                                    <SplineDoubleKeyFrame  KeyTime="00:00:03.00" Value="0"/>
                                </DoubleAnimationUsingKeyFrames>
                            </Storyboard>

                        </Grid.Resources>

这样前台就设计好了,此时需要在后台提供调用的接口:

public sealed class NotificationBar : Control
    {
        private TextBlock notifyBlock;
        private Grid mainGrid;
        private Storyboard storyBoard;

        public NotificationBar()
        {
            this.DefaultStyleKey = typeof(NotificationBar);
        }
        private void GetTextBlockControl()
        {
            if (this.notifyBlock == null)
            {
                this.notifyBlock = this.GetTemplateChild("tb_Notify") as TextBlock;
            }
        }
        private void GetStoryBoardControl(string name)
        {
            if (this.storyBoard == null)
            {
                this.storyBoard = this.GetTemplateChild(name) as Storyboard;
            }
        }

        public void ShowMessage(string message)
        {
            GetTextBlockControl();
            GetStoryBoardControl("tb_Notify_in");
            if (notifyBlock != null && storyBoard != null)
            {
                notifyBlock.Text = message;
                storyBoard.Begin();
            }
        }
    }

这样,我们只需要在页面的最上层加入这个控件,就可以调用 ShowMessage 方法弹出一个提醒框了。

<local:NotificationBar x:Name="notifyBlock" Grid.ColumnSpan="99" Grid.RowSpan="99" />

大约几秒后自动消失,无需用户手动干预,样式也可以完全自定义。在 Windows App 中也可以实现类似的效果:

3. 系统自带 Notification Bar

上面一种方法虽然效果好,自定义功能也强大,不过如果觉得代码量太大望而却步的话,可以试试一种折中的办法。这种办法使用了系统的 Notification Bar,将原来系统的通知用于应用内部。

使用起来主要分这么几个步骤:

首先定义需要显示的内容:

XmlDocument toastXml = ToastNotificationManager.GetTemplateContent(ToastTemplateType.ToastText01);
            XmlNodeList toastTextElements = toastXml.GetElementsByTagName("text");
            toastTextElements[0].AppendChild(toastXml.CreateTextNode(content));
            IXmlNode toastNode = toastXml.SelectSingleNode("/toast");

之后设定属性,例如是否静音,消失时间长短等等:

((XmlElement)toastNode).SetAttribute("duration", "short");
            XmlElement audio = toastXml.CreateElement("audio");
            audio.SetAttribute("silent", "true");
            toastNode.AppendChild(audio);

最后定义一个 toast:

ToastNotification toast = new ToastNotification(toastXml);
            toast.ExpirationTime = DateTimeOffset.UtcNow.AddSeconds(1);
            toast.Tag = "NOTI";
            toast.Dismissed += toast_Dismissed;
            ToastNotificationManager.CreateToastNotifier().Show(toast);

定义了 toast 以后,我们还定义了 ExpirationTime 这个属性,这是由于在 Windows Phone 8.1以后,系统添加了 Notification Center,所有的信息会被整合进去。如果不添加这一句,那么用户会在 Notification Center 中再次看到刚刚的提醒信息,非常多余,所以要设定它马上过期,不在 Notification Center 中显示。

另一个问题是,虽然 toast 过期,不再显示,但是系统会在右上角的通知栏遗留一个图标,很让强迫症患者受不了。

所以我们需要在 toast 消失的时候,强制去掉这一遗留提醒:

void toast_Dismissed(ToastNotification sender, ToastDismissedEventArgs args)
        {
            ToastNotificationManager.History.Remove("NOTI");
        }

这样,整个效果就完美了,如果用户觉得默认消失时间还是长,也可以直接左滑关闭这一通知:

若说美中不足,那就是如果用户在系统设置里开启了震动选项,那么提醒弹出的时候会震动一下。

总结

以上三种方法,第一种方法实现简单,效果也相对简单;第二种方法实现复杂,但是功能也最强大,完全随心所欲;第三种方法比较折中,代码量和效果都尚可。大家可以根据需求选择不同的方法,也欢迎大家说说自己都是怎么实现类似的应用内提醒功能的呢?

分享代码,改变世界!

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

时间: 2024-08-25 13:43:41

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

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

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

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

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

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

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

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

前言 页面导航,是App中的基本功,一般的App,一来一去,只需要简单的Navigate + Back就行了,一个复杂的App可能需要很多导航模式的混合才能实现最佳用户体验. SplashScreen 启动屏幕 我们先从最开始的SplashScreen说起吧.如果你把启动屏幕做成一个Page,启动时先显示一下,然后假装忙乎两秒,跳到下一个主页面开始进入正题,这个好像看上去也很美好.但是当用户玩命儿按Back键时,哦,露出马脚了,启动页面被唤出了.不过这个bug倒是不妨作为一个新奇的体验. MSD

博客园客户端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连接世界:WinRT中的微博分享

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