预览

1.项目地址

播放器Github地址

2.新功能

  1. 下载文件到音乐库
  2. 播放器背景透明
  3. 播放器新页面跳转
  4. 播放在线音乐

3.使用说明

  1. 点击下载将指定音乐下载到系统音乐库。
  2. 点击播放按键,如果本地音乐库没有缓存则使用网络播放,如果本地有音乐则使用本地音乐。

4.最终效果

最终效果

技术问题

1.播放在线音乐

通过ide的代码提示,完成了这个播放在线音乐的任务。

1
video_player.Source = MediaSource.CreateFromUri(new Uri(this.url));

2.下载音频到音乐库并播放

我们可以把一个问题分为简单的两个问题:1. 如何下载音频 2.如何获取系统音乐库目录。

第一个问题不可以简单复制示例代码,因为我们需要将网络文件保存下来,使用文件保存显然不是好想法,所以需要采用stream方式读取返回的内容。

第二个问题,在观察保存到音乐库的样例代码的时候,除了需要替换 KnownFolders.CameraRoll 为 KnownFolders.MusicLibrary,还应该将原本本地文件流式保存换为在线文件的流式保存。

除此之外,还应注意,如果创建一个已经存在的文件时,会产生异常,所以可以通过这个异常来检测目录里有没有相同文件名的文件。

在播放方法中,通过 KnownFolders.MusicLibrary.GetFileAsync(uri); 来获取待播放音乐的 StorageFile 对象,在赋值给player就可以进行播放了。

最后还要注意访问权限的问题,如图所示在项目清单中勾选音乐库。

音乐库

资料链接 Files and folders in the Music, Pictures, and Videos libraries

资料链接 httpclient

示例代码——下载

1
2
3
httpResponse = await httpClient.GetAsync(requestUri);
httpResponse.EnsureSuccessStatusCode();
httpResponseBody = await httpResponse.Content.ReadAsStringAsync();

示例代码——保存

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
StorageFolder testFolder = await StorageFolder.GetFolderFromPathAsync(@"C:\test");
StorageFile sourceFile = await testFolder.GetFileAsync("TestImage.jpg");
StorageFile destinationFile = await KnownFolders.CameraRoll.CreateFileAsync("MyTestImage.jpg");

using (var sourceStream = await sourceFile.OpenReadAsync())
{
    using (var sourceInputStream = sourceStream.GetInputStreamAt(0))
    {
        using (var destinationStream = await destinationFile.OpenAsync(FileAccessMode.ReadWrite))
        {
            using (var destinationOutputStream = destinationStream.GetOutputStreamAt(0))
            {
                await RandomAccessStream.CopyAndCloseAsync(sourceInputStream, destinationStream);
            }
        }
    }
}

我的代码——下载与保存

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
/**
*下载uri指示的网络音乐,并保存到音乐库中
*@parm uri 网络音乐
*/
public async Task Gets(Uri uri)
        {

            try
            {
                StorageFile destinationFile =
                await KnownFolders.MusicLibrary.CreateFileAsync(this.filename);
                try
                {
                    HttpClient httpClient = new HttpClient();
                    var response = await httpClient.GetAsync(uri);
                    var buffer = await response.Content.ReadAsBufferAsync();
                    var sourceStream = await response.Content.ReadAsInputStreamAsync();

                    using (var destinationStream =
                        await destinationFile.OpenAsync(FileAccessMode.ReadWrite))
                    {
                        using (var destinationOutputStream = destinationStream.GetOutputStreamAt(0))
                        {
                            await RandomAccessStream.CopyAndCloseAsync(sourceStream, destinationStream);
                        }
                    }
                    MessageDialog msg = new MessageDialog("校歌下载完毕");
                    await msg.ShowAsync();
                }
                catch (Exception e)
                {
                    Debug.WriteLine("xxxxx{0}", e);
                    MessageDialog msg = new MessageDialog("出了点问题");
                    await msg.ShowAsync();
                }
            }
            catch
            {
                MessageDialog msg = new MessageDialog("文件不能重复下载");
                await msg.ShowAsync();
            }
            finally
            {
                button_open.Visibility = Visibility.Visible;
                mainpage_progress_ring.IsActive = false;
            }
        }   

我的代码——播放

1
2
StorageFile file = await KnownFolders.MusicLibrary.GetFileAsync(uri);
video_player.Source = MediaSource.CreateFromStorageFile(file);

3.打开新页面并在标题栏显示返回按键

只需要监听两个事件:BackRequested 与 Navigated 就能实现标题栏显示返回的功能。

BackRequested 会在标题栏的返回按键被点击时触发,这时可以调用GoBack()来实现返回功能。

Navigated 是导航事件(前进、返回、刷新等)发生时的监听函数,每次导航事件发生时都检测一下当前窗口的可返回属性,根据返回值来决定是否显示标题栏返回按钮(AppViewBackButtonVisibility 设为 true则显示)。

资料链接 Win10的UWP之标题栏的返回键

示例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public MainPage()
{
    this.InitializeComponent();

    Windows.UI.Core.SystemNavigationManager.
        GetForCurrentView().BackRequested += BackRequested;
    SystemNavigationManager.GetForCurrentView().
        AppViewBackButtonVisibility =
        root.CanGoBack ?
        AppViewBackButtonVisibility.Visible : Windows.UI.Core.AppViewBackButtonVisibility.Collapsed;
    root.Navigated += OnNavigated;
}    
// 每次导航事件发生时,更新返回键的可见信息
private void OnNavigated(object sender, NavigationEventArgs e)
{
    SystemNavigationManager.GetForCurrentView().
        AppViewBackButtonVisibility =
        ((Frame)sender).CanGoBack ?
        AppViewBackButtonVisibility.Visible : AppViewBackButtonVisibility.Collapsed;
}
//titleBar 返回事件
private void BackRequested(object sender, BackRequestedEventArgs e)
{
    Frame rootFrame = Window.Current.Content as Frame;
    if (rootFrame == null)
        return;
    if (rootFrame.CanGoBack && e.Handled == false)
    {
        e.Handled = true;
        rootFrame.GoBack();
    }
}

将示例代码复制于主页面即可实现标题栏返回的功能。

4.页面缓存

当程序写好后,发现在退出播放页面时,播放并不停止,再次进入还会重新播放音乐,这时候播放的音乐重合在一起,显得不完美,通过在xaml中加入 NavigationCacheMode=”Required” 就可以实现页面缓存。

在进入页面和离开页面的监听函数中将player的Source打印出来。

source

一共四行,分别为 第一次进入和退出第二次进入与退出 的LOG,可以看出,第一次进入时Source为空,再次进入后Source不为空,说明缓存成功。

资料链接 UWP页面缓存

我的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<Page
    x:Class="NetEasePlayer_UWP.PlayerPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:NetEasePlayer_UWP"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    NavigationCacheMode="Required"
    mc:Ignorable="d">

    <Grid Name="gridBase">
        <Grid Name="glassHost"/>
        <Grid Background="#40ffffff"/>
        <MediaPlayerElement
            Name="video_player"
            AutoPlay="True"
            AreTransportControlsEnabled="True"/>
    </Grid>
</Page>

5.毛玻璃窗口

还记得在第一次的课堂上,老师给我们展示了一个带着美丽磨砂效果的计算器app,所以我希望这个简单的播放器也可以有磨砂的效果。

通过在全局背景多设置一个Grid作为毛玻璃效果的容器,然后在这个容器中添加一个用拥有磨砂笔刷的View,就能显示出磨砂效果了。

这时候,窗口的标题栏还不能显示磨砂效果,所以还需要将内容扩展到标题栏,并设置标题栏按钮背景为透明才可以。

纯粹的透明不利于阅读,所以又增加了一层半透明灰色做底色。

资料链接 (UWP)应用窗口实现毛玻璃效果

示例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/**
* 初始化毛玻璃特效
* @parm glassHost 毛玻璃组件容器
*/
private void InitializeFrostedGlass(UIElement glassHost)
{
    Visual hostVisual = ElementCompositionPreview.GetElementVisual(glassHost);
    Compositor compositor = hostVisual.Compositor;
    var backdropBrush = compositor.CreateHostBackdropBrush();
    var glassVisual = compositor.CreateSpriteVisual();
    glassVisual.Brush = backdropBrush;
    ElementCompositionPreview.SetElementChildVisual(glassHost, glassVisual);
    var bindSizeAnimation = compositor.CreateExpressionAnimation("hostVisual.Size");
    bindSizeAnimation.SetReferenceParameter("hostVisual", hostVisual);
    glassVisual.StartAnimation("Size", bindSizeAnimation);
}

//使内容扩展到标题栏
var coreTitleBar = CoreApplication.GetCurrentView().TitleBar;
coreTitleBar.ExtendViewIntoTitleBar = true;
//使标题栏案件背景为透明
var view = ApplicationView.GetForCurrentView();
view.TitleBar.ButtonBackgroundColor = Colors.Transparent;
view.TitleBar.ButtonForegroundColor = Colors.Black;
view.TitleBar.ButtonInactiveBackgroundColor = Colors.Transparent;
view.TitleBar.ButtonInactiveForegroundColor = Colors.Black;

我的页面代码

1
2
3
4
5
6
7
8
9
10
<Grid Name="gridBase">
    <Grid Name="glassHost"/>
    <Grid Background="#40ffffff"/>
    <MediaPlayerElement
          Name="video_player"
          AutoPlay="True"
          AreTransportControlsEnabled="True"/>
</Grid>

在示例代码的基础上,我发现,在窗口失去焦点的时候,画刷的磨砂效果会消失,由此想到,可以分别监听页面的 OnLostFocus 和 OnGotGocus 事件,在失去焦点时给页面背景截一个图,再通过Image显示出来,在获取焦点时将Image隐藏,来使页面好看一下。

但这个做法并没有成功,可能是磨砂效果并不能用普通的方法进行页面截取的缘故。

总结

通过老师的不断驱动,这次我们又了解了如何使用Windows10的系统默认位置来储存文件,因为开发者甚少,所以很不容易才在官方文档中找到相关信息,希望自己的检索技能可以越来越强,能够更快速的找到自己想找的东西。