博客园客户端(Universal App)开发随笔 -- 样式管理与夜间模式
以今天的眼光来看,一个好应用首先是要有好的用户体验。而好的用户体验最直观的就来自于用户界面。好的用户界面则需要好的设计,更需要好的实现。今天我们就向大家分享一下我们在使用Xaml实现界面设计上的一点心得。
样式管理
我们拿到的设计,大多是一张红线图,布满了距离,字号,色号,事无巨细的量化了我们的用户界面。如果我们就这样把各种属性照搬到上Xaml文件中,那看起来就非常不妙了,比如这样:
<TextBlock Text="首页"FontFamily =" Segoe WP "FontSize ="24"FontWeight =" Normal "TextTrimming =" CharacterEllipsis "TextWrapping =" Wrap "MaxLines ="2" Foreground="#FF297ACD " ></TextBlock>
一个看起来还好,但是通常我们的页面上不可能只有这一个控件。要是有上10个20个,那如果要修改其中一个属性可真是得看花了眼啊。
很幸运,我们在Universal应用的开发中可以把控件的相同属性归纳为资源(Resource),再在需要的时候应用到控件上就可以了。
有些很重要的单独的属性,以颜色为例,不同控件都会使用,却只有几个值,Universal应用可以把它单独抽象成一种 StaticResource 资源--Brush 。将它应用到控件的 Foreground 或者Background 之类的属性上时,就会像"brush(刷子)"一样把控件变为它的颜色。其中常用的SolidColorBrush 可以写成这样:
<SolidColorBrush x:Key=" PostTitleFont" Color="#FF297ACD"/>
将它放到资源字典或者页面的<Page.Resources> 中,就可以通过{StaticResource Brush的x:Key的值} 的形式来应用了。
而同一种控件的多个相同属性,我们可以把它们归纳成Style这种StaticResource资源,存在当前页面的 <Page.Resources> 中:
<Page.Resources>
<SolidColorBrush x:Key=" PostTitleFont" Color="#FF297ACD"/>
<Style x:Key="PostTitleFont" TargetType="TextBlock">
<Setter Property="FontFamily" Value="Segoe WP"/>
<Setter Property="FontSize" Value="24"/>
<Setter Property="FontWeight" Value="Normal"/>
<Setter Property="TextTrimming" Value="CharacterEllipsis"/>
<Setter Property="TextWrapping" Value="Wrap"/>
<Setter Property="MaxLines" Value="2"/>
<Setter Property="Foreground" Value="{StaticResourcePostTitleFont }"/>
</Style>
</Page.Resources>
Style 的 TargetType 属性指明了Style可以应用的元素,比如TargetType="TextBlock" 的Style是不能应用到Image 元素上的。而Style中包含的就是要应用的属性了,以 <Setter Property="属性名" Value="属性值"/> 这样的形式。
那么我们在当前页面的需要应用Sytle的地方,比如上面的情况,就只需要通过Style的x:Key 属性来应用:
<TextBlock Text="首页" Style="{StaticResource PivotTitleFont}"></TextBlock>
我们可以看到 Style="{StaticResource PivotTitleFont}" 中的 PivotTitleFont 就是 Style的 x:Key 属性,这样style就能够应用到 TextBlock 上了。
而且Style还能继承。比如我要一个 PostSubTitleFont, 只要字体比PostTitleFont 小一点。那我就可以这么写:
<Style x:Key="PostSubTitleFont" TargetType="TextBlock" BasedOn="{StaticResource PostTitleFont}">
<Setter Property="FontSize" Value="20"></Setter>
</Style>
这样一来如果我想改动它们的字体,只要在PostTitleFont中修改就可以了。是不是很方便?
当然我们通常也不止有一个页面,如果不想在每个页面中都把 Style 和 Brush 都贴一遍,就需要资源字典文件出场了。在我们的项目中添加一个资源字典文件:
再把我们的Style和 Brush都写在里面。然后在需要使用这些Style和 Brush的页面添加形如下面的代码把资源词典引用上就可以啦:
<Page.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Dictionary.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Page.Resources>
当然,我们既然是Universal应用,能不能把Styl和 Brushe在Windows应用和Windows Phone应用间共享呢?答案是可以的。我们只要把资源文件放在.share项目下面,再在App.xaml中添加它的引用就可以:
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Dictionary.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
这样Dictionary.xaml中的Style和 Brush就在Windows应用和Windows Phone应用的所有页面都有效了。
等等,那Windows应用和Windows Phone应用间有所区别的Style和 Brush怎么办呢?
也有办法。只要在Windows项目和Windows Phone项目中各添加一个同名的资源词典文件比如DifferentDictionary.xaml,把不同的Style和 Brush写入,再在App.xaml中引用就能够实现了。
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Dictionary.xaml" />
<ResourceDictionary Source="DifferentDictionary.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
这样,我们通过Resource 和资源词典文件大大简化了用户界面设计的实现过程,是不是很方便呢?
夜间模式
说到阅读软件的用户体验,就不得不提到夜间模式了。下面就说说我们在Windows Phone应用上实现夜间模式的过程吧。
大家可能都在Windows Phone 设置的 开始屏幕+主题 中设置过背景和主题色,当我们修改了背景的 黑/白 后,几乎所有的系统应用的背景色和文字的颜色都立刻改变了,连重启应用都不用。
我们实现的夜间模式正是利用了这个功能。
在Windows Phone应用中,如果我们设置了当前 Page的 RequestedTheme 属性为Light 或者Dark的话就相当于我们在系统设置里修改了背景黑/白。比如通常情况下我们新建一个应用,默认的情况下都是黑底白字:
而如果我们把Page的 RequestedTheme 属性设为的话,可以看到在设计视图中应用变成了白底黑字:
那么我们通过settings页面的一个 ToggleSwitch控件实时切换RequestedTheme这一属性,就可以实时控制应用的各种颜色如背景色,文字颜色为Light或Dark对应的颜色。我们把Light设为日间模式,Dark设为夜间模式。简单一点的情况下可以把 ToggleSwitch 的Toggled事件响应方法设为如下:
private void ts_LightMode_Toggled(object sender, RoutedEventArgs e)
{
if (this.RequestedTheme == ElementTheme.Light)
{
this.RequestedTheme = ElementTheme.Dark;
}
else
{
this.RequestedTheme = ElementTheme.Light;
}
}
接下来如何设置日间模式/夜间模式对应的颜色呢?
在之前的样式管理一节中我们提到了通过{StaticResource PivotTitleFont } 的形式来应用 SolidColorBrush 资源,但是我们在资源 PostTitleFont 定义的时候只写了一个值,系统会自动帮我们转换么?事实上并没有那么简单。我们需要通过 ThemeResource 类型的资源预先设置好2种模式对应的颜色。应用了这种 ThemeResource 资源以后,这个控件就能够响应 RequestedTheme 属性的变化,即时切换显示对应模式的颜色。
那么怎样加入这种资源呢?我们可以打开我们的资源字典 Dictionary.xaml ,在里面加入这样一段代码:
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Key="Light">
</ResourceDictionary>
<ResourceDictionary x:Key="Dark">
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>
这里 x:Key 分别为Dark/ Light 的 ResourceDictionary 中,就是我们存放对应两种模式的资源的地方。比如我们希望页面背景在日间模式下是 蓝色,夜间模式下是深灰色,就可以这么写:
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Key="Light">
<SolidColorBrush x:Key="myPageBackground" Color="Blue"></SolidColorBrush>
</ResourceDictionary>
<ResourceDictionary x:Key="Dark">
<SolidColorBrush x:Key="myPageBackground" Color="DarkGray"></SolidColorBrush>
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>
在我们的page中就可以这样应用 ThemeResource 资源了:
<Page
x:Class="my_universal.CnblogsMainPage"
xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:my_universal"
xmlns:d="https://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="https://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
RequestedTheme="Light"
Background="{ThemeResourcemyPageBackground }">
是不是一点都不难呢?
另外我们还发现有两点值得注意:
一是我们可以覆盖系统的 ThemeResource 。我们可以选中任意一个 TextBlock 控件,打开属性栏,选择 Foreground 属性的画笔资源。
可以看到下方以ThemeBrush结尾的都是系统的 ThemeResource 资源,只要在我们的 x:Key 分别为Dark/ Light 的 ResourceDictionary 里加入和它们同名的资源,就能够覆盖它们了。
二是对于Flyout 类型的控件,应用到它上面的 ThemeResource 资源是相反的。就是说当RequestedTheme 属性为 Light 时,Flyout 类型的控件会应用 Dark ResourceDictionary 中的资源。反之亦然。
小结
今天就先向大家分享这些心得,欢迎大家继续关注和拍砖,让我们共同进步。
我们的已经发布的应用和代码可以在下面找到:
Windows Phone Store App link:
https://www.windowsphone.com/zh-cn/store/app/博客园-uap/500f08f0-5be8-4723-aff9-a397beee52fc
Windows Store App link:
https://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