zoukankan      html  css  js  c++  java
  • 博客园客户端UAP开发随笔 -- 狡兔三窟:App内的三种通知消息的实现

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

    1. 弹出窗口提示

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

    image

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

    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

    image

    自定义样式:

    <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" />

    image

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

    image

     

    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 过期,不再显示,但是系统会在右上角的通知栏遗留一个图标,很让强迫症患者受不了。

    image

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

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

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

    image

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

    总结

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

    分享代码,改变世界!

    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

  • 相关阅读:
    javaWeb快速入门+——初体验-HelloWorld
    简单理解Linux系统的挂载是什么鬼
    STM32MP157 Cortex®-M4高性能系列MCU
    常见六种锂电池特性及参数对比
    IIC通信详解
    stm32微秒延时问题
    STM32 HAL库实现微秒级别延时
    开关电源波纹的产生、测量及抑制
    图解DIY 1pA超微电流测试器
    stm32
  • 原文地址:https://www.cnblogs.com/ms-uap/p/4249612.html
Copyright © 2011-2022 走看看