在PC上我一直使用“小番茄”做为个人番茄钟软件,我把它打开后放在副显示器最大化,这样不只可让它尽到本分,并且还能够告诉个人同事“我正在专心工做”。但是我老是嫌弃它的手感不够愉悦,总想本身写一个番茄钟软件,正好最近好久没写UWP应用了很手痒,因而就抽空写了个自用的番茄钟并发布到微软应用商店。html
结果手感也并不愉悦。git
另外,原本原本我也打算用Storyboard实现动画,但火火老是劝我不要搞Storyboard,要用Composition API作动画。Storyboard的能力是有极限的,我从短暂的UWP生涯当中学到一件事,人越是玩弄动画,动画就越可能由于没有料到的事态而失败……除非超越Storyboard。因此我也不作Sotryboard啦。程序员
微软的应用商店是一个还不错的平台,WPF程序员能够基于现有的知识轻易地建立一个UWP应用并发布到应用商店。尤为是如今微软的审核比较宽松,只要是对得起本身良心的应用通常都能经过审核。虽然由于商店抽风我本身都很难下载到本身的应用。这篇文章将讲解从建立UWP项目到发布到商店的整个流程。github
我只想要一个能够倒计时的Timer,顺便玩玩UWP的新API,因此原则上越简单越好,而后想到什么作什么。编程
不少番茄钟软件都会提供任务列表功能,还能够经过图表展现番茄数量、完成任务数量的统计。不过我已经有To-Do和Azure Devops,平时的工做还会记录在OneNote上,我更放心把个人数据放到微软那里而不是番茄钟那里,并且我认为衡量番茄工做法是否执行得好的标准是个人工做,而不是图表里展现给个人番茄数量,因此我对图表、统计、任务列表这些功能不是太感兴趣。windows
说了这么多其实仍是由于我懒,平时上班已经处理这么多数据了,图表我也玩腻了,本身玩玩的东西就不想作这些工做,并且存储数据是要负责任的,我可不想负责任。api
首先安装Windows Template Studio,它能够帮助开发者简单地建立UWP项目。并发
安装后在建立新项目界面选择Windows Template Studio(Universal Windows)
,而后在打开的精灵控件窗口一步步建立一个已经包含基础功能的UWP应用。app
项目名称是OnePomodoro
,项目类型选择Blank
,Design pattern选择了Prism,由于在WPF中用惯了Prsim。不过我不懂UWP中Prism怎么用,因此我也没打算立刻就用,只是个小项目轻轻松松地CodeBehind一把梭。async
页面项选择了Settings页面。功能项添加了好像颇有趣的Toast Notifications、Live Tile等一系列的通知功能。
稍等几分钟后,一个包含了基本功能的UWP项目就建立好了,项目中还贴心地提示了不少须要处理的Todo项,运行效果以下:
而后添加Microsoft.Toolkit.Uwp.UI和Microsoft.Toolkit.Uwp.UI.Animations引用,这两个包是Windows Community Toolkit的一部分,提供了不少有用的Converter和动画。
第一次运行应用时会弹出一些示例通知,如今还不须要作到这么全面,找到App.xaml.cs里的LaunchApplicationAsync
把里面一些通知相关的代码注释掉,而后就能够开始实现咱们的功能了。
protected override async Task OnLaunchApplicationAsync(LaunchActivatedEventArgs args) { await LaunchApplicationAsync(PageTokens.MainPage, null); } private async Task LaunchApplicationAsync(string page, object launchParam) { await ThemeSelectorService.SetRequestedThemeAsync(); NavigationService.Navigate(page, launchParam); SetupTitlebar(); Window.Current.Activate(); //await Container.Resolve<IWhatsNewDisplayService>().ShowIfAppropriateAsync(); //await Container.Resolve<IFirstRunDisplayService>().ShowIfAppropriateAsync(); //Container.Resolve<ILiveTileService>().SampleUpdate(); //Container.Resolve<IToastNotificationsService>().ShowToastNotificationSample(); }
最终效果就是这样,一个单页面的应用,点击开始启动工做的计时器,点击中止(或者倒计时结束)转到休息的计时器,如此往返几回,一天的工资就到手了。
不少计时器是个由分针和秒针组成的表盘,但我已经玩腻了这种作法,简单些反而有更多的快乐。
为了好看首先要移除应用的标题栏,将CoreApplicationViewTitleBar.ExtendViewIntoTitleBar 属性设置为True:
private void ExtendAcrylicIntoTitleBar() { CoreApplication.GetCurrentView().TitleBar.ExtendViewIntoTitleBar = true; ApplicationViewTitleBar titleBar = ApplicationView.GetForCurrentView().TitleBar; titleBar.ButtonBackgroundColor = Colors.Transparent; titleBar.ButtonInactiveBackgroundColor = Colors.Transparent; }
由于背景是黑色,须要将主题也改成黑色主题,在App.xaml中修改RequestedTheme为Dark:
RequestedTheme="Dark"
而后实现MainViewModel。使用到DispatcherTimer
进行计时,用DelegateCommand
实现命令,这些都只用到WPF的知识:
public class MainViewModel: ViewModelBase { public bool IsInPomodoro { get; } public bool IsTimerInProgress { get; } public TimeSpan RemainingBreakTime { get; } public TimeSpan RemainingPomodoroTime { get; } public DelegateCommand StartTimerCommand { get; } public DelegateCommand StopTimerCommand { get; } }
整个UI上就只有4行字,以及中止和开始两个按钮。(上面效果图里还有一个按钮,那是第二版作的)。整个番茄钟由IsInPomodoro和IsTimerInProgress组合成准备开始工做,正在工做,准备休息,正在休息四种状态,MainView上的元素就由IsInPomodoro和IsTimerInProgress这两个属性控制是否显示。
为了用这两个Bool属性控制UI元素的显示和隐藏须要用到Converter,Microsoft.Toolkit.Uwp.UI
提供了BoolToVisibilityConverter,我须要再实现一个反过来的NegationBoolToVisibilityConverter
:
public class NegationBoolToVisibilityConverter : BoolToObjectConverter { public NegationBoolToVisibilityConverter() { base.TrueValue = Visibility.Collapsed; base.FalseValue = Visibility.Visible; } }
这两个Converter配合IsInPomodoro和IsTimerInProgress两个属性用于控制是否显示。而后再使用BoolToObjectConverter定义两个Converter用于控制文字的水平和垂直对齐:
converters:BoolToObjectConverter TrueValue="Top" FalseValue="Bottom" x:Key="BoolToVerticalAlignmentConverter" /> converters:BoolToObjectConverter TrueValue="Left" FalseValue="Right" x:Key="BoolToVerticalHorizontalAlignment" />
整个布局大概这样
<StackPanel VerticalAlignment="{Binding IsInPomodoro,Converter={StaticResource BoolToVerticalAlignmentConverter}}" HorizontalAlignment="{Binding IsInPomodoro,Converter={StaticResource BoolToVerticalHorizontalAlignment}}"> <TextBlock Text="In Work" Visibility="{Binding IsInPomodoro,Converter={StaticResource BoolToVisibilityConverter}}"/> <TextBlock Text="{Binding RemainingPomodoroTime,Converter={StaticResource FormatStringConverter},ConverterParameter=mm\\:ss}" Visibility="{Binding IsInPomodoro,Converter={StaticResource BoolToVisibilityConverter}}"/> <TextBlock Text="{Binding RemainingBreakTime,Converter={StaticResource FormatStringConverter},ConverterParameter=mm\\:ss}" Visibility="{Binding IsInPomodoro,Converter={StaticResource NegationBoolToVisibilityConverter}}"/> <TextBlock Text="Take a Break" Visibility="{Binding IsInPomodoro,Converter={StaticResource NegationBoolToVisibilityConverter}}"/> </StackPanel> <StackPanel HorizontalAlignment="Left" VerticalAlignment="Bottom" Orientation="Horizontal"> <Button Content="" Visibility="{Binding IsTimerInProgress,Converter={StaticResource NegationBoolToVisibilityConverter}}" Command="{Binding StartTimerCommand}" /> <Button Content="" Visibility="{Binding IsTimerInProgress,Converter={StaticResource BoolToVisibilityConverter}}" Command="{Binding StopTimerCommand}" /> </StackPanel>
到这时候,MainView和MainViewModel几乎只用到WPF的知识,虽然据说DispatcherTimer比较伤性能,也没有用x:Binding代替Binding,主要是想到项目刚开始就尽可能用WPF的知识实现全部功能,之后再试用UWP的API。不过动画我却是没用Storyboard,而是用Composition API作动画。
composition-animation有不少种,我选择使用Windows Community Toolkit中的Implicit Animations,由于它很适合入门。
Implicit Animations(又称为隐式动画)是一种用于描述当属性(例如Opacity or Offset)改变时如何使用动画响应的Composition Animations。而ShowAnimations和HideAnimations分别用于定义当元素显示/隐藏或从VisualTree上添加/移除时的动画效果。
MainView里当状态改变只会引发元素显示/隐藏或者对齐的改变,因此很适合使用隐式动画。例如这段番茄钟倒计时的动画,即显示时从下面200像素向上移动,而且淡入,耗时1.5秒;隐藏时用0.5秒淡出。
<animations:Implicit.ShowAnimations> <animations:ScalarAnimation Target="Translation.Y" Duration="0:0:1.5" From="200" To="0" /> <animations:OpacityAnimation Duration="0:0:1.5" From="0" To="1" /> </animations:Implicit.ShowAnimations> <animations:Implicit.HideAnimations> <animations:OpacityAnimation Duration="0:0:0.5" From="1" To="0" /> </animations:Implicit.HideAnimations>
最终动画效果以下:
就这样一个基本的番茄钟就作好了,以后就是打包和发布。随便画个渐变的背景,再画个圈,Logo就作好了。而后在Package.appxmanifest里处理一下信息,就打包了,就发布了。
官方文档有很详尽的发布指南,微软合做伙伴中心的图形界面也简单易用,稍微折腾一下就能够发布,过几天就能够在Store里见到本身的应用。
每次本身打包都很麻烦,能够将Github仓库(假设有的话)和AppCenter关联起来,每次提交到Github都由AppCenter打安装包。AppCenter打包后便可把安装包下载回来,再发布(话说没有直接帮我发布的方法吗?)
林德熙的这篇文章详细介绍了如何操做。
还能够得到一个徽标,显示编译结果。
Edi.Wang被UWP伤害后抛弃了UWP还像个怨念少女那样天天对别人说实际上是要说服本身“我才没有喜欢UWP我最讨厌UWP讨厌讨厌最讨厌了”但这样天天天天天天都说UWP是个坏家伙是个坏家伙搞到我反而很想试一试这个坏家伙如今终于忍不了了晚上把switch扔在床上把本身关在书房里亲自动手调教UWP。
总的来讲这是个愉快的编程体验:用惯的WPF知识和官方文档,便可实现一个本身用的应用并发布——除了商店偶尔抽风致使本身都下载不了本身的应用外。
顺便一提OnePomodoro的中文名称是一个番茄钟(谢绝对命名品味的一切批评),已经能够在Store下载。
最后提一下左下角的按钮。由于1809的Button有了圆角的API,圆形的Reveal按钮很容易实现,只须要BasedOn ButtonRevealStyle
再把CornerRadius
有那么大就搞那么大:
<Style TargetType="Button" x:Key="EllipseButtonRevealStyle" BasedOn="{StaticResource ButtonRevealStyle}"> <Setter Property="CornerRadius" Value="100"/> <Setter Property="Background" Value="Transparent"/> </Style>
这样省去了不少修改ControlTemplate的麻烦,因此项目的最低版本便是1809,反正只是玩玩的东西不要顾虑太多。
能够打开这个连接安装 一个番茄钟,也能够在Microsoft Store中搜索“OnePomodoro”或“一个番茄钟”进行安装。
若是不能安装?我相信,等缘分到了天然能够安装。
Overview - Visual Studio App Center Microsoft Docs
合成动画 - Windows UWP applications Microsoft Docs
Implicit Animations XAML Attached Properties - Windows Community Toolkit Microsoft Docs