预览
1.项目地址
2.新功能
- 下载文件到音乐库
- 播放器背景透明
- 播放器新页面跳转
- 播放在线音乐
3.使用说明
- 点击下载将指定音乐下载到系统音乐库。
- 点击播放按键,如果本地音乐库没有缓存则使用网络播放,如果本地有音乐则使用本地音乐。
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
示例代码——下载
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则显示)。
示例代码
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打印出来。
一共四行,分别为 第一次进入和退出 与 第二次进入与退出 的LOG,可以看出,第一次进入时Source为空,再次进入后Source不为空,说明缓存成功。
我的代码
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,就能显示出磨砂效果了。
这时候,窗口的标题栏还不能显示磨砂效果,所以还需要将内容扩展到标题栏,并设置标题栏按钮背景为透明才可以。
纯粹的透明不利于阅读,所以又增加了一层半透明灰色做底色。
示例代码
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的系统默认位置来储存文件,因为开发者甚少,所以很不容易才在官方文档中找到相关信息,希望自己的检索技能可以越来越强,能够更快速的找到自己想找的东西。