Windows10 UWP開發 - 響應式設計
本篇隨筆與大家簡單討論一下在開發適配不同分辨率、寬高比的Windows10 Universal App布局時的可行方式與小技巧。經驗均從實踐中總結,可能有諸多不完善和淺薄之處,歡迎讀者嚴格指正。另外本文也只是拋磚引玉之用,希望能收獲更多更好的實戰經驗。
自適配的必要性
說了這么多,我們首先可能會問了,為什么要做響應式設計?其原因有以下兩點:
Windows10的跨平臺性
Windows10是微軟宣稱可以統一運行于PC&平板&手機&Xbox等諸多平臺的操作系統,當然這也是universal一詞的由來。如果你希望你的應用不僅僅局限于某一個平臺,那么做一些響應式布局就非常必要了。
Win10 UWP的容器窗口可自定義大小
即使你的應用僅針對windows rt的平板用戶,win10對于uwp的全新處理方式也讓開發者不能免于分辨率適配。下圖分別是Win8.1和Win10的應用商店應用對比:


不難發現,為了增強store app的實用度,桌面環境下的uwp是可以自定義窗口尺寸的。為了讓應用別輕易被用戶玩崩壞,整理整理儀容還是相當有必要的。
廢話少說,接下來,我們來看看手中能用于做響應式設計的手段吧。
自適配布局的方法
與網頁設計類比
不知道讀者們有多少有html+css布局經歷,這一套東西盡管在布局上不算完美,但用起來也算得心應手,熟悉后能使網頁布局有基本良好的適應 性。所以這里我就先說說網頁前端布局與XAML布局的區別與聯系吧。好消息是,一言以蔽之:幾乎HTML能做的、XAML都可以。具體地說,幾種在網頁布 局上常見手法xaml實現如下:
順序布局
也就是沒有布局咯…對于結構簡單的內容呈現(比如本篇博客),用這種方式足矣。如同下面的代碼模型一樣
<!DOCTYPE html> <html> <body> <p style="font-size:70px">I am title</p> <div style="width:100px;height:100px;background:rgb(230,230,250);display:inline-block;"></div> <span style="font-size:30px">Inline text</span> <div style="font-size:32px"> The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. </div> </body> </html>
所有的元素從上到下、左到右排列,排列不下的則進入下一行。該效果使用HTML實現非常簡單,由于大部分html元素是inline的,故直接將其從上到下排列即可。
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <StackPanel Orientation="Vertical" Margin="70,70,70,10"> <TextBlock Text="I am title." FontSize="70"/> <StackPanel Orientation="Horizontal"> <Grid Background="Lavender" Width="100" Height="100"/> <TextBlock Text="Inline text." VerticalAlignment="Bottom" FontSize="30"/> </StackPanel> <TextBlock Text="The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog." TextWrapping="Wrap" FontSize="32"/> </StackPanel> </Grid>
Absolute大法
有一種說法叫以不變應萬變。
不管在什么情況下,這種布局方式是通配而普遍的:設計時固定頁面尺寸,按照固定位置對每一個元素進行布局,如果框架過小則使用滾動條瀏覽,框架過大則自動居中。反映在html中,則是無處不在的position:absolute。
這樣的優點在于方便設計,能最大程度(或者說,絲毫不差)復現designer的設計,且節省工作量(如對于電腦設計一個1366x768的布局后通用全部窗口大小)。缺點則很明顯:缺乏靈活性,用戶不友好,故只對于特定業務適用。
<div style="width:500px;height:500px;background:rgb(230,230,250);position:relative;"> <div style="width:130px;height:130px;background:#332CC7;position:absolute;left:20px;top:20px;"> </div> <div style="width:310px;height:310px;background:#4CA5FF;position:absolute;left:170px;top:170px;"> </div> <div style="width:310px;position:absolute;left:170px;top:75px;font-size:28px;"> The quick brown fox jumps over the lazy dog. </div> <div style="width:150px;position:absolute;left:20px;top:170px;font-size:28px;"> The quick brown fox jumps over the lazy dog. </div> </div>
UWP利用Canvas控件實現絕對布局,其內部元素均利用Canvas.left/Canvas.top與頂部對齊。
<Canvas Background="Lavender" Margin="70,70,70,70" Width="500" Height="500"> <Grid Background="#FF332CC7" Width="130" Height="130" Canvas.Left="20" Canvas.Top="20"> </Grid> <TextBlock Canvas.Top="75" Canvas.Left="170" Width="310" Text="The quick brown fox jumps over the lazy dog. " TextWrapping="Wrap" FontSize="28"/> <Grid Background="#FF4CA5FF" Width="310" Height="310" Canvas.Left="170" Canvas.Top="170"> </Grid> <TextBlock Canvas.Top="170" Canvas.Left="20" Width="150" Text="The quick brown fox jumps over the lazy dog. " TextWrapping="Wrap" FontSize="28"/> </Canvas>
流式布局
流式布局是網頁設計中挺老的概念了,將布局分解成區塊,區塊大小與區塊間距均按頁面總體尺寸百分比變化,從而適應不同分辨率的窗口。而對于區塊內 部細節,則可根據內容和需求進行定制。該布局方式在xaml中實現十分簡單,也并非windows10的新東西,只需要在Grid的寬高定義時用*標明百 分比即可。
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <Grid.ColumnDefinitions> <ColumnDefinition Width="5*"/> <ColumnDefinition Width="90*"/> <ColumnDefinition Width="5*"/> </Grid.ColumnDefinitions> <Grid.RowDefinitions> <RowDefinition Height="5*"/> <RowDefinition Height="90*"/> <RowDefinition Height="5*"/> </Grid.RowDefinitions> <Grid Grid.Column="1" Grid.Row="1" Background="Lavender"> <Grid.ColumnDefinitions> <ColumnDefinition Width="35*"/> <ColumnDefinition Width="10"/> <ColumnDefinition Width="64*"/> </Grid.ColumnDefinitions> <Grid Grid.Column="1" Background="White"> </Grid> <Grid Grid.Column="2"> <Grid.RowDefinitions> <RowDefinition Height="50*"/> <RowDefinition Height="10"/> <RowDefinition Height="50*"/> </Grid.RowDefinitions> <Grid Grid.Row="1" Background="White"> </Grid> </Grid> </Grid> </Grid>
實現效果如下:
XAML獨具特色的新方式
XAML自身一些具有特色的屬性/控件能進一步幫忙簡化響應式布局,力求做到不用代碼。
Viewbox
Viewbox是一個控件,會將其容納的對象渲染成指定的寬高,在布局很難做出改變且需要適應的變化并不大時可以使用該方法。關于其具體使用的tips&tricks我們會另起一篇日志介紹。
Rendertransform
為UIElement的一個屬性,會將對象在渲染層面按某種規則進行變化,如拉伸/翻轉等。需要注意的是,所謂渲染層面,表現在與該控件有關的屬性是不會察覺的:如width/height屬性,將并不會知曉作用于其上的拉神操作。如下面這段代碼將伸縮Grid的寬高:
<Canvas HorizontalAlignment="Stretch" VerticalAlignment="Stretch"> <Grid Height="500" Width="450" HorizontalAlignment="Left" VerticalAlignment="Top"> <Grid.RenderTransform> <ScaleTransform x:Name="scaler" CenterX="0.5" CenterY="0" ScaleX="1.3" ScaleY="1.3" /> </Grid.RenderTransform> </Grid> </Canvas>
配合著canvas.left/top屬性以及一些代碼,將相當容易地自制一個viewbox。
RelativePanel+VisualStateManager
二者均為Windows10引入的新特性,前者用于允許其子元素按相對位置(附著下方、附著于上方、左右對齊等方式進行排版且在布局有變時自動調 整保證其滿足給定的位置關系);后者則定義多組VisualState,每一個state對應頁面上不同控件的一些屬性值,而在對應的state條件滿足 時自動將屬性切換至目標值。此兩點結合,將可以在很多情況下不用額外的C#代碼實現響應布局。
關于此工具的具體用法,在之前發布的針對Build2015文章 UWP?UWP! - Build 2015有些啥?(2) 中已經提及,此處不再贅述。
Code動態調整
上面的方法都不好使?那就用code吧,麻煩是麻煩一點,其使用起來是最自由和可定制的,只要開發者心中有布局目標,就能滿足一切排版需求。但是在用Code調整布局的過程中,還是有一些注意事項值得嘮叨嘮叨的:
事件驅動動態調整
顯然,調整布局的code是事件驅動而不是一直運行的,那么寫在什么事件里呢?首先大家應該能想到SizeChanged。是 的,SizeChanged事件發生于對應目標的尺寸發生變化之后,也就是說,在該事件被觸發時,保證能獲取正確的寬高、從而根據獲取到的數據進行動態布 局,故將代碼寫進該事件是完全正確的選擇。需要注意的是,位置的變化(如canvas.left值改變)不會觸發該事件。另外需要注意的一點是,在事件中 寫入的代碼不能直接/間接修改自身相關的尺寸,否則引發雪崩效應,可能帶來stackoverflow。
LayoutUpdated也可以用于尺寸變化檢測,但是它是一個更普遍的事件:在布局樹上的元素發生改變就會觸發該事件,且該事件并不指定觸發 對象,如果跟蹤該事件將發現無論窗口發生怎么樣的改變,都將觸發海量的LayoutUpdated事件。故將代碼寫于其中并不明智。
ActualSize的獲取時機
在根據尺寸動態布局的的代碼中少不了獲取ActualSize,但需要明確的是在布局剛做出改變時ActualSize是不會生效的,直接以該值 作為基準勢必導致錯誤。解決方案有2:1.如上文所言,將事件寫于SizeChanged中,或將部分代碼依賴該事件觸發;2.強制調用 UpdateLayout()方法,同步更新界面,從而能保證在該調用后獲得的實際尺寸為真實尺寸。
總結
布局方法千千萬萬,本文只從最小的幾個地方入手簡要介紹,若需做出真正用戶友好、貼近業務需求的響應式布局,還需綜合應用各種布局手段,了解各手段間優劣以及其最適合的應用場景,協同作用以創造好的布局。