在傳統桌面程序中,對圖標的使用大可能是直接嵌入JPG或者PNG的圖片。在祖傳的1366x768分辨率下,並無什麼問題。相對於手機硬件的日新月異,也側面反映了PC行業的落寞和桌面程序開發的不思進取。用360衛士的羣衆並不能倒推PC行業的升級。反卻是水果公司雙高的利潤和口碑讓人非常眼饞。加之某軟跳出來教豬隊友作硬件。如今卻是有些轉機,1080p的屏幕已經是標配,4k也算常見。那麼傳統桌面程序在升級過程當中,就會遇到今天要討論的,如何解決高分辨率下圖標模糊的問題。
一種解決方案是按最高的分辨率提供圖片。這種適合較大的圖片,好比背景啥的。另外一種就是今天要討論的,針對當前流行的、扁平化圖標的解決方案。html
從本篇的標題能夠看出,咱們但願應用SVG矢量圖來適應各類分辨率的狀況。以WPF程序爲例,首先要面對的問題是,WPF並不支持像嵌入JPG/PNG圖標這樣,直接使用SVG圖標。大動干戈的引用第三方library經過自定義類型來支持SVG並非本文的目的。這裏咱們要介紹如何經過字體文件,進而在WPF或UWP中使用SVG圖標的方式。
雖然WPF不支持直接使用SVG文件,可是Windows是支持矢量字體的。而咱們的目的就是要將圖標以字體的形式在WPF程序中顯示。具體使用的字體TrueType,則是由微軟和蘋果共同開發的字體類型標準,該字體文件的擴展名是.ttf。
https://en.wikipedia.org/wiki/TrueType
接下來咱們依然是經過Sample工程來講明。首先給出GitHub的地址:
https://github.com/manupstairs/WpfAppForFontIcon
首先咱們打開WpfAppWithPNGs工程,圖標的使用代碼以下:git
<Image Grid.Row="0" Grid.Column="0" Width="32" Height="32" Source="Resources/Airplane_Off.png" ></Image> <Image Grid.Row="0" Grid.Column="1" Width="64" Height="64" Source="Resources/Airplane_On.png" ></Image> <Image Grid.Row="0" Grid.Column="2" Width="96" Height="96" Source="Resources/Bluetooth_Off.png" ></Image> <Image Grid.Row="0" Grid.Column="3" Width="128" Height="128" Source="Resources/Bluetooth_On.png" ></Image>
這裏主要有兩個問題,由於咱們默認提供的是32x32的圖標,所以除了第一列Width和Height設置爲32的圖標,其餘的圖標都存在模糊的問題。第二個問題是針對圖標的每一種顏色,都須要對應提供不一樣的圖標文件(圖中的例子須要有灰色和藍色兩份文件)。相對的SVG圖標僅僅須要一份文件。便可在程序中動態設定不一樣的顏色了。
這裏先給出最終WPF項目中,對SVG圖標的引用的代碼,而後咱們再進行詳細解釋。對應的工程名爲WpfAppWithFontIcons。github
<TextBlock Grid.Row="0" Grid.Column="0" Text="{x:Static local:FontIcons.airplane_mode_circ}" Foreground="Gray" FontSize="32" ></TextBlock> <TextBlock Grid.Row="0" Grid.Column="1" Text="{x:Static local:FontIcons.airplane_mode_circ}" Foreground="{StaticResource dellBlue}" FontSize="64" ></TextBlock> <TextBlock Grid.Row="0" Grid.Column="2" Text="{x:Static local:FontIcons.bluetooth_inactive}" Foreground="Orange" FontSize="96" ></TextBlock> <TextBlock Grid.Row="0" Grid.Column="3" Text="{x:Static local:FontIcons.bluetooth_inactive}" Foreground="Brown" FontSize="128" ></TextBlock>
代碼最大的不一樣應該是由<Image/>標籤更改成<TextBlock/>標籤,這是由於咱們是經過ttf字體文件,曲線救國的方式來使用SVG圖標。
具體的步驟以下:
準備SVG圖標文件,將這些文件打包成一整個ttf字體文件。打包的方式有不少種,一般我使用的是IcoMoon的免費解決方案。地址以下:
https://icomoon.io/app/#/select
經過這個網站選擇SVG圖標文件上傳,打包生成一個zip文件。解壓後文件夾結構以下圖:app
ttf文件在fonts文件夾中,實際使用時,須要做爲資源文件,添加到WPF工程中。點擊圖中的demo.html會打開一個本地網頁,可用於查找ttf文件中包含的SVG圖標,以及對應的unicode。實際咱們是經過對unicode的引用來顯示SVG圖標的。svg
完整的project結構以下圖,Fonts文件夾是手動添加用來放置ttf文件。ttf文件名字都是根據項目須要來取,並不固定。工具
ttf字體文件須要以<FontFamily/>的形式添加到項目的<Resources/>節點中。而後再經過<Style/>指定給<TextBlock/>。固然不在<Resources/>節點定義Style,而是在每一個<TextBlock/>中指定FontFamily屬性也是能夠的。有關XAML的語法細節,回字的四種寫法什麼的,這裏略過不提。字體
<Window.Resources> <FontFamily x:Key="Fonticon">/Fonts/rcc-fonticon-ribbon-v2.ttf#rcc-fonticon-ribbon-v2</FontFamily> <Style TargetType="TextBlock"> <Setter Property="FontFamily" Value="{StaticResource Fonticon}" ></Setter> </Style> <SolidColorBrush x:Key="dellBlue">#007DB8</SolidColorBrush> </Window.Resources>
這裏說明一下「/Fonts/rcc-fonticon-ribbon-v2.ttf#rcc-fonticon-ribbon-v2」值的定義,#前面的是文件路徑,#後面的是font name,查看的方法是雙擊ttf文件,參考下圖。網站
在定義好FontFamily以後,咱們並不推薦直接將unicode寫到XAML或.cs文件中。由於在XAML中,你須要以下編寫:spa
<TextBlock Grid.Row="0" Grid.Column="0" Text="" Foreground="Gray" FontSize="32" ></TextBlock>
而在C#代碼中,又須要如下面這種格式:3d
textBlockAirplane.Text = "\ue900";
兩種不統一的格式會在未來修改時帶來極大的困難,特別是圖標被多處引用時,全局的查找替換根本就是噩夢。此外,毫無心義的unicode值的可讀性根本等於0。正常人類沒法將"","\ue900"和Airplane的圖標聯繫起來。
我推薦的作法是生成一個FontIcons Class,以string類型屬性的形式暴露出來。這樣能夠得到IDE智能語法提示的支持,更新時也僅需修改這個Class,Find All Reference更是方便無比。同時不管在XAML文件,仍是C#代碼中,咱們看到的都是統一的「FontIcons.airplane_mode_circ」。
public static class FontIcons { public static string airplane_mode_circ { get; } = "\ue900"; public static string bluetooth_inactive { get; } = "\ue901"; public static string brightness { get; } = "\ue902"; public static string brightness_inactive { get; } = "\ue903"; public static string browse_inactive { get; } = "\ue904"; public static string camera { get; } = "\ue905"; }
那麼咱們是否是須要手工來編寫FontIcons Class呢?大哥咱們是能把午餐(我不愛喝咖啡)轉換成Code的生物啊!固然是寫個小工具來自動生成了。在Sample庫中,參考IcoMoonReader工程,只需將IcoMoon生成的.svg文件(icomoon.zip解壓後的fonts文件夾裏)丟在IconMoonReader.exe同級目錄,便可生成相應代碼。
其實只有一個方法啦,使用時須要注意具體的文件名是否正確。
using (var stream = new FileStream("rcc-fonticon-ribbon-v2.svg", FileMode.Open)) { using (var reader = new StreamReader(stream)) { var pattern = "unicode(\\S)*\\sglyph-name(\\S)*\""; var input = reader.ReadToEnd(); foreach (Match match in Regex.Matches(input, pattern)) { pattern = "\"\\S*\""; var list = new List<string>(); foreach (var result in Regex.Matches(match.Value, pattern)) { list.Insert(0, result.ToString()); } var name = list[0].Replace("\"", "").Replace("-","_"); var code = list[1].Replace("&#x", "\\u").Replace(";", ""); Console.WriteLine($"public static string {name} {{ get; }} = {code};"); } } }
把生成的C#字符串定義貼到具體工程的FontIcons Class(名字隨意)。
這樣一個優秀的解決方案若是僅支持WPF,那又談何遷移到MS Store呢?實際上這套機制放到UWP工程中也是能夠的。雖然UWP能夠經過SvgImageSource屬性原生支持SVG了,但咱們的這套方案在圖標的應用方面絕不遜色,甚至能夠說更爲方便。具體的例子能夠參考AppWithFontIcon工程。在這個UWP的工程中,除了放ttf文件的位置我換到了現成的Assets文件夾,幾乎沒有改變。
<TextBlock Grid.Row="0" Grid.Column="0" Text="{x:Bind local:FontIcons.airplane_mode_circ}" Foreground="Gray" FontSize="32" ></TextBlock> <TextBlock Grid.Row="0" Grid.Column="1" Text="{x:Bind local:FontIcons.airplane_mode_circ}" Foreground="{StaticResource dellBlue}" FontSize="64" ></TextBlock> <TextBlock Grid.Row="0" Grid.Column="2" Text="{x:Bind local:FontIcons.bluetooth_inactive}" Foreground="Orange" FontSize="96" ></TextBlock> <TextBlock Grid.Row="0" Grid.Column="3" Text="{x:Bind local:FontIcons.bluetooth_inactive}" Foreground="Brown" FontSize="{x:Bind DynamicFontSize(),Mode=OneWay,FallbackValue=128}" ></TextBlock>
由於UWP沒有了x:static關鍵字,因此我換成了x:Bind。換成x:Bind以後甚至能夠動態的響應值的變化。好比我在這裏把FontSize作了一個x:bind到DynamicFontSize()方法,讓字體隨着界面改變,動態的變大變小。雖然並無什麼卵用……可是Demo的時候能夠增長點噱頭……
本篇到此結束,照例貼上Github地址:
https://github.com/manupstairs/WpfAppForFontIcon感謝耐着性子看到這裏的同窗!