博客園客戶端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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM