你好

UWP学习(三)--My Naïve Mondrian Player

GitHub项目链接

项目成果

技术问题

1.界面设计

​ 可视化程序设计,重点还是在可视化上~ 即使是这样一个简单的程序,它也应该拥有开发者对其界面设计的一个端正态度的权利。因此我遇到的第一个问题就是,界面设计。
界面设计是建立在掌握了软件功能的前提之上。因为只有清楚了软件功能需求,才会知道界面的模块格局与主体控件。根据软件需求,主体控件自然是MediaPlayerElement。然后还需要有文件选择入口。我选择了使用最简单的Button实现。另外,我觉得还需要有显示文件名的一栏。在掌握着功能需要后,开始构思界面排版与风格。了解到几个模块特性,我想到的总体风格,没错,就是老师之前讲到过的蒙德里安经典风格的格子画。

但是在效果和技术完美结合上,耗费了我大部分时间。。整个过程是没有参考文档的,如果如何理解蒙德里安格子画精髓也算?

我的xaml代码:

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
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
<Page
x:Class="MediaPlayer.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:MediaPlayer"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">

<Grid>
<Grid.RowDefinitions>
<RowDefinition Height=".1*" />
<RowDefinition Height="auto"/>
<RowDefinition Height="*" />
<RowDefinition Height="auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width=".13*"/>
<ColumnDefinition Width="auto"/>
<ColumnDefinition Width=".9*"/>
</Grid.ColumnDefinitions>

<Border Grid.Column="0" Grid.Row="0" Width="auto" Height="auto" Background="Red"/>
<Border Grid.Column="1" Grid.Row="0" Width="5" Background="#353535"/>
<Border Grid.Column="1" Grid.Row="2" Width="5" Background="#353535"/>

<Border Grid.Column="1" Grid.Row="1" Height="5" Width="5" Background="#353535"/>

<Border Grid.Column="0" Grid.Row="1" Height="5" Background="#353535"/>
<Border Grid.Column="2" Grid.Row="1" Height="5" Background="#353535"/>


<StackPanel Grid.Column="0" Grid.Row="2" Height="auto" Orientation="Vertical">
<Border Background="#2586BB">
<TextBlock Name="chose" HorizontalAlignment="Center" Foreground="White" FontSize="14px" Margin="2,10,2,10" Padding="2,0,2,0" FontFamily="Microsoft YaHei" FontWeight="Normal">选择文件类型</TextBlock>
</Border>

<Border Height="3" Background="#2586BB"/>

<Border Background="White">
<Button Name="choose_mp3" HorizontalAlignment="Center" Content="MP3" Width="150" Height="40" Background="White"
Click="choose_mp3_Click" FontFamily="Adobe Gothic Std B" FontWeight="Bold"/>
</Border>

<Border Height="3" Background="#353535"/>

<Border Background="White">
<Button Name="choose_mp4" HorizontalAlignment="Center" Content="MP4" Width="150" Height="40" Background="White"
Click="choose_mp4_Click" FontFamily="Adobe Gothic Std B" FontWeight="Bold"/>
</Border>

<Border Height="5" Background="#353535"/>
<Border Height="100" Background="#2586BB"/>
<Border Height="5" Background="#353535"/>

<StackPanel Width="auto" Height="200" Grid.Column="0" Orientation="Horizontal">
<StackPanel Orientation="Vertical" Height="200" Width="73">

<Border Height="120" Background="Yellow"/>
<Border Height="5" Background="#353535"/>

<Border Height="35" Background="White"/>
<Border Height="5" Background="#353535"/>
<Border Height="35" Background="White"/>
</StackPanel>

<Border Width="4" Background="#353535"/>
<Border Width="500" Background="Black"/>

</StackPanel>

<Border Height="4" Background="#353535"/>
<Border Height="500" Background="Red"/>
</StackPanel>

<Border Grid.Column="2" Grid.Row="0" Background="Yellow">
<TextBlock Name="media_title" HorizontalAlignment="Center" VerticalAlignment="Center"
Text="Welcome" FontSize="20px" FontFamily="Arial Black" FontWeight="Bold"/>
</Border>

<Border Grid.Row="2" Grid.Column="2" Background="#FFF8FF">
<MediaPlayerElement Name="my_player"
AreTransportControlsEnabled="True" FontFamily="Adobe Arabic" >

</MediaPlayerElement>

</Border>
<Grid Grid.Column="2" Grid.Row="2">
<Image Name="mp3_logo" Width="300" Height="300" Source="Assets/mp32.jpg" Margin="0,0,0,100" Opacity="0"/>
<Image Name="welcome" Width="300" Height="50" Source="Assets/logo2.png" Margin="0,40,0,0" />
<Image Name="logo" Width="200" Height="150" Source="Assets/grid.jpg" Margin="0,0,0,200" Opacity="1"/>
</Grid>

</Grid>
</Page>

2.文件选择

文件选择这块是这个软件的核心板块。在实现播放器之前,我想先尝试着推测梳理一下整个软件实现的思路流程。

应着自己初步的思路,我先查找了C#实现文件选择器的相关文档。
参考文档链接
文档的参考代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//1.创建和自定义 FileOpenPicker
var picker = new Pickers.FileOpenPicker();
picker.ViewMode = Pickers.PickerViewMode.Thumbnail;
picker.SuggestedStartLocation = Pickers.PickerLocationId.PicturesLibrary;

picker.FileTypeFilter.Add(".jpg");
picker.FileTypeFilter.Add(".jpeg");
picker.FileTypeFilter.Add(".png");


Windows.Storage.StorageFile file = await picker.PickSingleFileAsync();
if (file != null)
{
// Application now has read/write access to the picked file
this.textBlock.Text = "Picked photo: " + file.Name;
}
else
{
this.textBlock.Text = "Operation cancelled.";
}

根据参考代码,看来跟自己之前的思路基本吻合。首先是通过FileOpenPicker声明一个对象,这个对象通过对文件格式的筛选,在用户选择完成后,生成某个类型(C#的var特性也是超级好用~)的file对象。我想有了这个file,只需要修改if(file != null){}内的内容,接下来交给mediaplayer处理,应该就实现了。
接下来,解决mediaplayerelement实现播放的问题。
首先参考的是xaml controls gallery。里面对控件的前端实现很简单,要实现后端媒体播放功能,还得查询微软官方文档
参考代码:

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
private async void Button_Click(object sender, RoutedEventArgs e)
{
await SetLocalMedia();
}

async private System.Threading.Tasks.Task SetLocalMedia()
{
var openPicker = new Windows.Storage.Pickers.FileOpenPicker();

openPicker.FileTypeFilter.Add(".wmv");
openPicker.FileTypeFilter.Add(".mp4");
openPicker.FileTypeFilter.Add(".wma");
openPicker.FileTypeFilter.Add(".mp3");

var file = await openPicker.PickSingleFileAsync();

// mediaPlayer is a MediaElement defined in XAML
if (file != null)
{
var stream = await file.OpenAsync(Windows.Storage.FileAccessMode.Read);
mediaPlayer.SetSource(stream, file.ContentType);

mediaPlayer.Play();
}
}

​ 这份参考代码与之前的参考代码惊人的相似。我便知道我离成功仅半步之遥了。不过微软这段官方的参考代码却有很多值得学习的地方,这个async await异步线程特性的使用。在UI线程里不处理复杂任务,async await特性完美解决了不需要new thread()便实现了这个任务。
根据两个参考代码,我的代码自然也就水到渠成了,只需要对自己的特性进行微小的调整。在map3选择button和mp4选择button下均处理异步线程的文件选取事件。但是为了区分格式,我设置了标志变量flag,在MP3按钮下设置为0,MP4按钮下设置为1,再当成形参传入异步线程任务里让其区分。另外,加入了界面切换模块。

我的代码:

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
48
49
50
51
52
53
54
55
public MainPage()
{
this.InitializeComponent();

}
int flag = -1; // to mark the mp3 or mp4 button choose
//音频文件选择处理事件
private async void choose_mp3_Click(object sender, RoutedEventArgs e)
{
//若点击MP3文件选择按钮,flag标志设为0
flag = 0;
await SetLocalMedia(flag);
//切换界面
mp3_logo.Opacity = 1;
logo.Opacity = 0;
welcome.Opacity = 0;
}
//视频文件选择处理事件
private async void choose_mp4_Click(object sender, RoutedEventArgs e)
{
//若点击MP4文件选择按钮,flag标志设为1
flag = 1;
await SetLocalMedia(flag);
//切换界面
logo.Opacity = 0;
welcome.Opacity = 0;
mp3_logo.Opacity = 0;
}

async private System.Threading.Tasks.Task SetLocalMedia(int flag)
{
FileOpenPicker openPicker = new FileOpenPicker();
if(flag == 0)
{
openPicker.FileTypeFilter.Add(".mp3");
openPicker.FileTypeFilter.Add(".wma");
}
else if(flag == 1)
{
openPicker.FileTypeFilter.Add(".mp4");
openPicker.FileTypeFilter.Add(".wmv");
}

var file = await openPicker.PickSingleFileAsync();

if (file != null)
{
//播放器标题栏设为文件名,文件名可通过file的属性值直接获取
media_title.Text = file.Name;
my_player.Source = MediaSource.CreateFromStorageFile(file);
my_player.MediaPlayer.Play();
}

}
}

3.界面切换和媒体名显示

​ 在播放器基本功能实现完成之后,还有一点小问题需要解决。就是播放音频文件时的标识。我想到了用一个Image控件显示音频图标,以明显展示正在播放的文件类型。但是在用户没有选择文件播放之前这个标示图是不能显示在主界面了,这涉及到对控件的隐藏。于是我查询了有关隐藏控件相关的文档。可惜的是C#关于visible和opacity的对比相关资料很少,css倒是一大把。我猜他们大概的区别都差不多吧。查阅文档后知道了visible与opacity(透明度)都能实现对控件的隐藏。区别是visible为false后相关的点击事件也取消了,而opacity调为0后虽然控件不可见但是你还是可以点击它触发事件。我采用的opacity实现。

代码就是修改控件的opacity而已:

1
2
3
4
5
{
mp3_logo.Opacity = 1; //切换到MP3文件,MP3 logo显示
logo.Opacity = 0; //播放器logo隐藏
welcome.Opacity = 0; //欢迎页隐藏
}

媒体名的显示也很简单。还记得刚才file对象,它里面应该有自己的name属性。直接调用显示在textblock就行了。代码很简单:

1
media_title.Text = file.Name;


总结:

​ 整个流程实现下来其实还是挺顺畅的(别做梦了,只是你实现的功能too young too naive而已..),我觉得一个好的界面是一个应用有趣的灵魂之一,所以我觉得花一定精力去设计你的软件界面是很有必要的一件事。在初步实现了我的界面
还有,在实现一些功能之前,自己不妨可以先推测并梳理一下究竟大概是以怎样的步骤去实现,必要条件是哪些(即使你暂时不知道具体的代码实现)、模块的结合是怎样的。然后在自己的问题中分条查询文档,当发现这些参考文档的异同中,你也许就有了自己的思路和解决方案。具体干货:善用var、善用async await、善用搜索引擎。

⬅️ Go back