[UWP]從頭開始創建併發布一個番茄鐘

来源:https://www.cnblogs.com/dino623/archive/2019/07/03/a_uwp_pomodoro.html
-Advertisement-
Play Games

1. 自己用的番茄鐘自己做 在PC上我一直使用“小番茄”作為我的番茄鐘軟體,我把它打開後放在副顯示器最大化,這樣不僅可以讓它盡到本分,而且還可以告訴我的同事“我正在專心工作”。可是我總是嫌棄它的手感不夠愉悅,總想自己寫一個番茄鐘軟體,正好最近很久沒寫UWP應用了很手癢,於是就抽空寫了個自用的番茄鐘並 ...


1. 自己用的番茄鐘自己做

在PC上我一直使用“小番茄”作為我的番茄鐘軟體,我把它打開後放在副顯示器最大化,這樣不僅可以讓它盡到本分,而且還可以告訴我的同事“我正在專心工作”。可是我總是嫌棄它的手感不夠愉悅,總想自己寫一個番茄鐘軟體,正好最近很久沒寫UWP應用了很手癢,於是就抽空寫了個自用的番茄鐘併發布到微軟應用商店。

結果手感也並不愉悅。

另外,本來本來我也打算用Storyboard實現動畫,但火火總是勸我不要搞Storyboard,要用Composition API做動畫。Storyboard的能力是有極限的,我從短暫的UWP生涯當中學到一件事,人越是玩弄動畫,動畫就越可能因為沒有料到的事態而失敗……除非超越Storyboard。所以我也不做Sotryboard啦。

微軟的應用商店是一個還不錯的平臺,WPF程式員可以基於現有的知識輕易地創建一個UWP應用併發布到應用商店。尤其是現在微軟的審核比較寬鬆,只要是對得起自己良心的應用一般都能通過審核。雖然因為商店抽風我自己都很難下載到自己的應用。這篇文章將講解從創建UWP項目到發佈到商店的整個流程。

2. 需求

我只想要一個可以倒計時的Timer,順便玩玩UWP的新API,所以原則上越簡單越好,然後想到什麼做什麼。

很多番茄鐘軟體都會提供任務列表功能,還可以通過圖表展示番茄數量、完成任務數量的統計。不過我已經有To-Do和Azure Devops,平時的工作還會記錄在OneNote上,我更放心把我的數據放到微軟那裡而不是番茄鐘那裡,而且我認為衡量番茄工作法是否執行得好的標準是我的工作,而不是圖表裡展示給我的番茄數量,所以我對圖表、統計、任務列表這些功能不是太感興趣。

說了這麼多其實還是因為我懶,平時上班已經處理這麼多數據了,圖表我也玩膩了,自己玩玩的東西就不想做這些工作,而且存儲數據是要負責任的,我可不想負責任。

3. 創建項目

首先安裝Windows Template Studio,它可以幫助開發者簡單地創建UWP項目。

安裝後在創建新項目界面選擇Windows Template Studio(Universal Windows),然後在打開的精靈控制項視窗一步步創建一個已經包含基礎功能的UWP應用。

項目名稱是OnePomodoro,項目類型選擇Blank,Design pattern選擇了Prism,因為在WPF中用慣了Prsim。不過我不懂UWP中Prism怎麼用,所以我也沒打算馬上就用,只是個小項目輕輕鬆松地CodeBehind一把梭。

頁面項選擇了Settings頁面。功能項添加了好像很有趣的Toast Notifications、Live Tile等一系列的通知功能。

稍等幾分鐘後,一個包含了基本功能的UWP項目就創建好了,項目中還貼心地提示了很多需要處理的Todo項,運行效果如下:

然後添加Microsoft.Toolkit.Uwp.UIMicrosoft.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();
}

4. 具體實現

最終效果就是這樣,一個單頁面的應用,點擊開始啟動工作的計時器,點擊停止(或者倒計時結束)轉到休息的計時器,如此往返幾次,一天的工資就到手了。

很多計時器是個由分針和秒針組成的表盤,但我已經玩膩了這種做法,簡單些反而有更多的快樂。

為了好看首先要移除應用的標題欄,將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="&#xE768;"
            Visibility="{Binding IsTimerInProgress,Converter={StaticResource NegationBoolToVisibilityConverter}}"
            Command="{Binding StartTimerCommand}" />
    <Button Content="&#xE769;"
            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>

最終動畫效果如下:

5. 發佈

就這樣一個基本的番茄鐘就做好了,之後就是打包和發佈。隨便畫個漸變的背景,再畫個圈,Logo就做好了。然後在Package.appxmanifest里處理一下信息,就打包了,就發佈了。

官方文檔有很詳盡的發佈指南,微軟合作伙伴中心的圖形界面也簡單易用,稍微折騰一下就可以發佈,過幾天就可以在Store里見到自己的應用。

每次自己打包都很麻煩,可以將Github倉庫(假設有的話)和AppCenter關聯起來,每次提交到Github都由AppCenter打安裝包。AppCenter打包後即可把安裝包下載回來,再發佈(話說沒有直接幫我發佈的方法嗎?)

林德熙的這篇文章詳細介紹瞭如何操作。

還可以獲得一個徽標,顯示編譯結果。

Build status

6. 結語

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,反正只是玩玩的東西不要顧慮太多。

7. 如何安裝

可以打開這個鏈接安裝 一個番茄鐘,也可以在Microsoft Store中搜索“OnePomodoro”或“一個番茄鐘”進行安裝。

如果不能安裝?我相信,等緣分到了自然可以安裝。

8. 參考

通過《番茄工作法圖解》複習番茄工作法

Windows Template Studio quickly builds a UWP app, using a wizard-based UI to turn your needs into a foundation of Windows 10 patterns and best practices

Overview - Visual Studio App Center Microsoft Docs

合成動畫 - Windows UWP applications Microsoft Docs

Implicit Animations XAML Attached Properties - Windows Community Toolkit Microsoft Docs

9. 源碼

OnePomodoro


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • [Think in Java]第2章 一切都是對象 如果我們說另一種不同的語言,那麼我們就會發覺有一個有些不同的世界 -- Luduing Wittgerstein 儘管Java基於C++,但相比之下,Java是一種更純粹的面向對象程式設計語言. Java語言假設我們只進行面向對象的程式設計(OOP ...
  • 棧:先入後出,後入先出 像電梯一樣,先進入電梯的,走到電梯最深處,後進入電梯的,站在電梯門口, 所以電梯打開的時候,後進入的會先走出來,先進入的會後走出來。 push,對應入電梯,把數據往裡面壓 pop, 對應出電梯,把數據往外拿 棧頂,對應電梯門口 棧底,對應電梯最深處 這裡使用鏈表實現棧。 先創 ...
  • 1.1原理 1.session是伺服器端的技術 2.session是基於cookie技術的 1.2session操作 1.預設情況下,會話不會自動開啟,通過session_start()開啟會話 2.通過session_id()獲取會話的編號 3、通過$_SESSION操作會話 4、會話可以保存除了 ...
  • 8-9 魔術師:創建一個包含魔術師名字的列表,並將其傳遞一個名為show_magicians()的函數,這個函數列印列表中每個魔術師的名字。 8-10 了不起的魔術師:在8-9的程式中,編寫一個名為make_great()的函數,對函數列表進行修改,在每個魔術師的名字中都加入字樣“the Great ...
  • 小猿圈作業: 編寫裝飾器,為多個函數加上認證的功能(用戶的賬號密碼來源於文件),要求登錄成功一次,後續的函數都無需再輸入用戶名和密碼 tip:account文件的格式 1 dadada 123456 2 da da 3 da1 da 4 da2 1234 5 da3 1111 編寫裝飾器,為多個函數 ...
  • 一、JDBC 1、JDBC簡介 (1) JDBC(Java Database Connectivity),即Java資料庫連接。用於在Java程式中實現資料庫操作功能。 (2)是一種用於執行SQL語句的Java API,使用戶以相同的方式連接不同的資料庫。 (3)JDBC提供一套標準介面,即訪問數據 ...
  • 一、環境: 系統:win7 版本:Python 3.7.2 (32位) 二、問題: 利用Pyinstaller封裝exe 時, 報錯:TypeError: expected str, bytes or os.PathLike object, not NoneType 如下圖: 三、解決方法: 1.這 ...
  • 一直想瞭解python網路爬蟲方面的知識,苦於沒有小白方面的視頻教程,網路基本都是需要有一定基礎才可以學習的 這款由大講師平臺推出的教程,絕對是小白入門的不二之選 首先,這門課程循序漸進。從Python的基本語法函數開始介紹 並且留有獨立的作業,看完視頻教程獨立完成課後作業 鞏固練習 其次,這門課程 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...