作业要求
- 使用MediaElement或MediaPlayer打造一个播放器,可播放视频和音乐
- 实现暂停,播放,停止等操作
- 实现进度条,实时显示媒体的播放速度;同时,拖动进度条,可以使媒体快速前进,后退到相应的位置
- 播放视频时,可全屏显示、退出全屏
- 可手动选择本地的媒体资源进行播放
- 实现封面旋转:播放音乐时,封面开始旋转,播放结束,封面复位,暂停时,封面的旋转动作暂停,播放视频的时候封面隐藏
具体实现
MainPage.xaml
<Page
x:Class="week8.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:week8"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Page.Resources>
<local:PositionSliderConverter x:Key="converter" />
<Storyboard x:Name="AvrilStoryboard" RepeatBehavior="Forever">
<DoubleAnimation Duration="0:0:20"
To="360"
Storyboard.TargetProperty="(UIElement.RenderTransform).(CompositeTransform.Rotation)"
Storyboard.TargetName="Picture"
d:IsOptimized="True"/>
</Storyboard>
</Page.Resources>
<Page.BottomAppBar>
<CommandBar>
<AppBarButton x:Name="ButtonPlay" Icon="Play" Click="ButtonPlay_Click"/>
<AppBarButton x:Name="ButtonPause" Icon="Pause" Click="ButtonPause_Click"/>
<AppBarButton x:Name="ButtonStop" Icon="Stop" Click="ButtonStop_Click"/>
<AppBarButton x:Name="ButtonFullScreen" Icon="FullScreen" Click="ButtonFullScreen_Click"/>
<AppBarButton x:Name="ButtonFolder" Icon="Folder" Click="ButtonFolder_Click"/>
<AppBarButton x:Name="ButtonVolume" Icon="Volume" Click="ButtonVolume_Click"/>
</CommandBar>
</Page.BottomAppBar>
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid.RowDefinitions>
<RowDefinition Height="100"/>
<RowDefinition Height="*"/>
<RowDefinition Height="100"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Ellipse Grid.Row="1" Grid.Column="1" x:Name="Picture" Height="250" Width="250" Visibility="Collapsed" RenderTransformOrigin="0.5, 0.5">
<Ellipse.RenderTransform>
<CompositeTransform />
</Ellipse.RenderTransform>
<Ellipse.Fill>
<ImageBrush ImageSource="Assets/1.jpg"/>
</Ellipse.Fill>
</Ellipse>
<MediaPlayerElement x:Name="_mediaPlayerElement" HorizontalAlignment="Stretch" Grid.Row="1" Grid.Column="1"/>
<StackPanel Grid.Row="2" Grid.Column="1">
<Slider x:Name="sliderLine" Value="{x:Bind _mediaTimelineController.Position, Converter={StaticResource converter},Mode=TwoWay}"/>
</StackPanel>
<Slider Grid.Row="2" Grid.Column="2" Minimum="0" Maximum="1" x:Name="Volumn" Width="100" Value="0.5" StepFrequency="0.1" ValueChanged="Volumn_ValueChanged" Orientation="Vertical" HorizontalAlignment="Right" Visibility="Collapsed"/>
</Grid>
</Page>
<local:PositionSliderConverter x:Key="converter" />
,转换器,用于拖动Slider进度的时候,改变视频或音乐的进度。Storyboard
,实现动画的控件,这定义了一个旋转的动画。<Slider Grid.Row="2" Grid.Column="2" Minimum="0" Maximum="1" x:Name="Volumn" Width="100" Value="0.5" StepFrequency="0.1" ValueChanged="Volumn_ValueChanged" Orientation="Vertical" HorizontalAlignment="Right" Visibility="Collapsed"/>
这个是用来做声音的,没找到可以和声音图片绑定的方法,所以就直接用宽度调节到相应的位置上,用的是垂直方向的Slider,当点声音按钮的时候,就会出现,再点击就会消失。
MainPage.xaml.cs
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.Media;
using Windows.Media.Core;
using Windows.Media.Playback;
using Windows.Storage;
using Windows.Storage.Pickers;
using Windows.UI.ViewManagement;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;
// https://go.microsoft.com/fwlink/?LinkId=402352&clcid=0x804 上介绍了“空白页”项模板
namespace week8
{
/// <summary>
/// 可用于自身或导航至 Frame 内部的空白页。
/// </summary>
public sealed partial class MainPage : Page
{
MediaPlayer _mediaPlayer = new MediaPlayer();
MediaTimelineController _mediaTimelineController = new MediaTimelineController();
TimeSpan _duration;
public MainPage()
{
this.InitializeComponent();
_mediaPlayer = new MediaPlayer();
var mediaSource = MediaSource.CreateFromUri(new Uri("ms-appx:///Assets/DarkSouls.mp4"));
mediaSource.OpenOperationCompleted += MediaSource_OpenOperationCompleted;
_mediaPlayer.Source = mediaSource;
_mediaPlayer.CommandManager.IsEnabled = false;
_mediaPlayer.TimelineController = _mediaTimelineController;
_mediaPlayerElement.SetMediaPlayer(_mediaPlayer);
}
private void ButtonPlay_Click(object sender, RoutedEventArgs e)
{
DispatcherTimer timer = new DispatcherTimer();
timer.Interval = TimeSpan.FromSeconds(1);
timer.Tick += timer_Tick;
timer.Start();
AvrilStoryboard.Begin();
if (_mediaTimelineController.State == MediaTimelineControllerState.Paused)
{
_mediaTimelineController.Resume();
}
else
{
_mediaTimelineController.Start();
}
}
void timer_Tick(object sender, object e)
{
sliderLine.Value = ((TimeSpan)_mediaTimelineController.Position).TotalSeconds;
if (sliderLine.Value == sliderLine.Maximum)
{
_mediaTimelineController.Position = TimeSpan.FromSeconds(0);
}
}
private void ButtonPause_Click(object sender, RoutedEventArgs e)
{
_mediaTimelineController.Pause();
AvrilStoryboard.Pause();
}
private void ButtonStop_Click(object sender, RoutedEventArgs e)
{
AvrilStoryboard.Stop();
_mediaTimelineController.Position = TimeSpan.FromSeconds(0);
_mediaTimelineController.Start();
_mediaTimelineController.Pause();
}
private void ButtonFullScreen_Click(object sender, RoutedEventArgs e)
{
ApplicationView view = ApplicationView.GetForCurrentView();
bool isInFullScreenMode = view.IsFullScreenMode;
if (isInFullScreenMode)
{
view.ExitFullScreenMode();
}
else
{
view.TryEnterFullScreenMode();
}
_mediaPlayerElement.IsFullWindow = !_mediaPlayerElement.IsFullWindow;
}
private async void MediaSource_OpenOperationCompleted(MediaSource sender, MediaSourceOpenOperationCompletedEventArgs args)
{
_duration = sender.Duration.GetValueOrDefault();
await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
{
sliderLine.Minimum = 0;
sliderLine.Maximum = _duration.TotalSeconds;
sliderLine.StepFrequency = 1;
});
}
private async void ButtonFolder_Click(object sender, RoutedEventArgs e)
{
var openPicker = new FileOpenPicker();
openPicker.SuggestedStartLocation = Windows.Storage.Pickers.PickerLocationId.VideosLibrary;
openPicker.FileTypeFilter.Add(".wmv");
openPicker.FileTypeFilter.Add(".mp4");
openPicker.FileTypeFilter.Add(".mp3");
openPicker.FileTypeFilter.Add(".wma");
StorageFile file = await openPicker.PickSingleFileAsync();
if (file != null)
{
var mediaSource = MediaSource.CreateFromStorageFile(file);
mediaSource.OpenOperationCompleted += MediaSource_OpenOperationCompleted;
_mediaPlayer.Source = mediaSource;
if (file.FileType == ".mp3" || file.FileType == ".wma")
{
Picture.Visibility = Visibility.Visible;
}
else
{
Picture.Visibility = Visibility.Collapsed;
}
}
}
private void Volumn_ValueChanged(object sender, RoutedEventArgs e)
{
_mediaPlayer.Volume = (double)Volumn.Value;
}
private void ButtonVolume_Click(object sender, RoutedEventArgs e)
{
if(Volumn.Visibility == Visibility.Collapsed)
{
Volumn.Visibility = Visibility.Visible;
}
else
{
Volumn.Visibility = Visibility.Collapsed;
}
}
}
}
MediaTimelineController
用来调节时间线,因为直接用MediaPlayer
来调节有很大的局限性,所以用MediaTimelineController
来代替。MediaTimelineController
几个函数的解释,Resume
,从当前位置继续播放,Start
从开始位置播放,所以即使是正在播放,按下播放按钮还是重头播放,Pause
暂定在现在的位置,所以在暂定之后,点击播放按钮的时候要判断是否是暂定状态,否则就会从头播放。如果要实现Stop
功能直接Start
再接Pause
就可以了。DispatcherTimer
这个作用是随着时间,进度条会前进,如果没有相关的设置,虽然可以用进度条控制视频的进度,但是进度条是不会自己动的,所以还要把视频的进度赋给Slider的value。AvrilStoryboard
是之前Storyboard
的名字,因为我用的是Avril的封面,所以就取了这个名字,AvrilStoryboard
的Begin
,Pause
,Stop
是对应的。- 让
MediaPlayer
全屏,这个是比较难受的,因为实现方法是_mediaPlayerElement.IsFullWindow = !_mediaPlayerElement.IsFullWindow;
,这个必须要吐槽一下,用一个返回值是bool
类型的函数控制是否全屏,不是太奇怪了吗,我本来还想找set
前缀的函数,真是太年轻了。
PositionSliderConverter.cs
用于Slider位置转换成视频进度条的转换器
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.UI.Xaml.Data;
namespace week8
{
class PositionSliderConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
return ((TimeSpan)value).TotalSeconds;
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
return TimeSpan.FromSeconds((double)value);
}
}
}