Design and UI > Layout
Page layout
在UWP应用中,一个Page常常有导航、命令以及内容元素。应用可以有多个Page,通过Frame组织在一起。Frame的直接上级就是跟视窗操作相关的Window。
如何在不同的页面中导航,时页面布局中首先需要考虑的事情。有两个常用的模式:左部导航或者顶部导航。Navigation design basics for UWP apps
对于超过五个页面的,建议使用左部导航nav pane。左部导航通常有个菜单键,可以用来控制导航栏的显示大小(收起还是折叠)。
顶部导航适合页面比较少的情况,和左部导航不同的是,顶部导航不可以收起。
对于每个页面,通常会有一个命令栏command bar,来放置页面常用的命令。命令栏可以放置在页面的顶部或者底部。
不同的页面,对内容的需求是不一样的。有几种常见的内容排布方式:
- 初始页:也就是应用打开时显示的页面
- 集合页:将内容以集合的方式显示,通常采用listview和gridview来呈现
- 主辅页:页面分为主辅部分,主部显示集合,辅部显示细节master/details
- 表单页:页面由多个输入控件form组成,主要用于收集数据
Sample apps列举了一些应用示例。
Screen sizes and breakpoints
从设计上UWP应用需要适应不同的屏幕尺寸,最基本的,要考虑以下屏幕尺寸(也叫breakpoints尺寸分隔)
- Small(不超过640px)
- Medium(641px到1007px)
- Large(1008px往上)
值得注意的是,因为有标题栏这些部件的存在,屏幕尺寸不直接等于应用内容尺寸。
分隔点更详细介绍:
- Small,常见的窗口大小:320x569、360x640、480x854
- 屏幕尺寸在4英寸到6英寸的手机
- 或者20英寸到65英寸的电视
- Medium,常见的窗口大小:960x540
- 7英寸到12英寸的大屏手机或者平板
- Large,常见的窗口大小:1024x640 1366x768,1920x1080
- 13英寸或者更大的计算机屏幕
虽然电视的屏幕很大,可能也有很高的物理分辨率。但是由于观看距离远,所以采用的逻辑分辨率比较低,保证App在远处操作的时候容易被看清。
关于Windows自带的DPI缩放,可以在 Settings > Display > Scale and layout中查看。
一些推荐:
- Small
- 将页面的左右外边距设置成12px,跟窗口边缘产生一定差距
- 将命令栏图标放置在页面底部
- 用单栏或者单区域显示内容
- 将搜索栏折叠成图标,采用点击触发
- 如果采用了navigation pane ,将其设置成收缩模式(只显示图标)
- 如果采用了主辅结构,建议使用stacked presentation mode
- Medium
- 将页面的左右外边距设置成24px
- 将命令栏图标放置在页面顶部
- 最多使用两栏或者双区域显示
- 显示搜索框
- 如果采用了navigation pane ,将其设置成silver mode
- 建议对TV采用特殊考虑TV experiences
- Large
- 将页面的左右外边距设置成24px
- 将命令栏图标放置在页面顶部
- 最多使用三栏或者三区域显示
- 如果采用了navigation pane ,将其展开成最大化模式
Continuum for Phones,可以把手机连到一个屏幕上,所以手机可以使用的屏幕大小会改变。
Responsive design techniques
UWP支持响应式设计以适应不同设备:
- 在屏幕尺寸吃紧的情况下,将导航部分收起
- 更好匹配设备,比如使用设备上的摄像和感知设备
- 重排UI控件,方便用户输入
很多UWP controls已经自带响应式设计。
响应式设计通常有几个技术方法:
- Reposition,也就是重排控件
- Resize,调整控件本身或者边距的大小
- Reflow,增减显示区域或者栏数
- Show/hide,显示或者隐藏控件
- Replace,将控件替换成对等的,但是对屏幕空间需求不一样的控件
- Re-architect,切换页面架构,比如从单页切换到主辅
Responsive layouts with XAML
XAML为响应式设计提供了大量的支持。
XAML支持静态设计,以及采用布局部属和面板来达成流动式设计。
布局部属可以用来控制元素的大小和位置,流动式设计的要点之一就是不要指定绝对的大小和位置,而是通过百分比或者填充的方式来动态变化。
比如说元素的Height和Width两个部属可以设置成绝对像素值,也可以设置成【自动】或者【等分】。【自动】意味着从元素的内容来推断元素的大小;【等分】意味着从父元素的尺寸来推断自身的大小。
【等分】用符号'‘表示,可以让一个元素具有更多的等分,比如将其设置为'2',意味着获得两个【等分】。
下面是一些例子:
- Column_1的尺寸是Auto,表示从子内容推断大小
- Column_2的尺寸是’*',表示它会获得一个等分的大小
- Column_3的尺寸是44,意味着它会获得44个逻辑像素
- Column_4的尺寸是'2*',表示它会获得两等分
在运行的时候,通过ActualHeight和Width可以获取具体获得的像素值。通过MinWidth/MaxWidth,以及MinHeigh/MaxHeight可以对元素的具体大小加以限制。
通过HorizontalAlignment(Left、Center、Right、Stretch)和VerticalAlignment(Top,Center,Bottom、Stretch)可以设置应用的对其。
可以将元素的Visibility设置成Visible 或者Collapsed,来控制其是否显示。这个部属的效果会扩展到子控件。
逐个设置元素的部属会比较麻烦,可以通过样式来同时给多个元素设置属性。参考Styling controls。
响应式设计是以面板为基础的。面板是一类UI元素,比如Canvas, Grid, RelativePanel或者StackPanel。面板的作用是把子内容聚合起来,以适配一种显示方式。下面是不同面板类的对比:
- Canvas,让你可以自行定位其子元素
- 元素在Canvas里面是采用绝对定位,使用Canvas.Top以及Canvas.Left这两附着部属来指定
- 元素的层叠可以使用Canvas.ZIndex来指定、
- HorizontalAlignment/VerticalAlignment如果指定为Stretch,那么就会被忽略
- 元素的大小如果没有指定,那么则采用他们内容的大小
- 超出边框的元素不会被裁剪,允许溢出
- 元素的内容不会被限制在边界内
- Grid,网格化管理子内容,可以方便重排内容
- 通过Grid.Row和Grid.Column可以指定子元素所在的网格
- 元素可以跨多个行或者列,通过Grid.RowSpan和Grid.ColumnSpan指定
- 支持HorizontalAlignment/VerticalAlignment的Stretch值
- 超出范围的子元素会被裁剪
- 元素内容被限定在区域内,超过会显示滚动条
- RelativePanel
- 子元素相对于边界,或者相对于彼此排布
- Stretch对齐会被忽略,除非设置允许Stretch (比如一个元素与左右两个边界对齐)
- 子内容超出会被裁剪,会显示滚动条
- StackPanel
- 元素排成一列
- 在指定走向的垂直方向上,支持Stretch对齐
- 元素超出显示区域会被裁剪
- 元素大小可以超过范围,也就是不显示滚动栏。如果需要显示滚动栏,需要显示设置元素大小。
- VariableSizedWrapGrid
- 和Grid一样,不过支持自动换行(列),当达到设定的最大行(列)数的时候
- 和StackPanel一样,需要设置走向
- Stretch对齐不可用,尺寸由ItemHeight 和ItemWidth决定。如果没有设置,那么由第一个子元素的大小决定。
- 裁剪子元素,超出显示滚动条
一个例子:Responsive techniques sample
面板在逻辑上将多个子元素或者控件组织在了一起。合理使用面板可以方便地自动调整UI元素的大小、位置甚至是流向。如果需要更多调整,那么可能需要用到【可视状态】。
在响应式设计里面,当你的UI窗口到达一定大小之后,或许你需要调整布局设置。【可视状态】可以用来帮你定义不同大小状态下的布局设置。
首先,要有一个AdaptiveTrigger来设置窗口大小的变化临界值,然后一个VisualState定义一个跟UI元素跟这个AdaptiveTrigger相对应的布局设置。多个VisualState可以以VisualStateGroup 的方式在一个VisualStateManager 中管理。
调用VisualStateManager.GoToState可以在代码中切换【可视状态】。可以侦听窗口的SizeChanged 事件来决定什么时候切换【可视状态】。在Set visual states in code可以看到一个具体的例子。
Windows没有给应用提供方法来检查当前正常运行的设备是什么设备。
在Win10之前,在【可视状态】修改布局设置,需要通过Storyboard。从Win10开始,可以用简化的Setter语法,并且可以在XAML中使用StateTrigger 。Set visual states in XAML markup中带有一个例子。
使用StateTrigger的时候,要保证VisualStateManager.VisualStateGroups在页面的第一个子元素上触发。
当设置附着部属的时候,需要在附着的部属名字上加上括号:
<!-- Set an attached property using ObjectAnimationUsingKeyFrames. -->
<ObjectAnimationUsingKeyFrames
Storyboard.TargetProperty="(RelativePanel.AlignHorizontalCenterWithPanel)"
Storyboard.TargetName="myTextBox">
<DiscreteObjectKeyFrame KeyTime="0" Value="True"/>
</ObjectAnimationUsingKeyFrames>
<!-- Set an attached property using Setter. -->
<Setter Target="myTextBox.(RelativePanel.AlignHorizontalCenterWithPanel)" Value="True"/>
可以从StateTrigger 扩展并创建自定义的触发器。可以定义这样一种触发器,当输入框获得焦点的时候,修改输入框的颜色。自定义触发器的例子:State triggers sample
可以在StateTrigger里面使用样式,参考Visual states and styles。
针对不同设备的UI差别可能很大,有时候为每个设备使用单独的UI定义文件更方便。比如针对不同的设备加载不同的Page。
在Visual Studio增加一个XAML VIEW,只需为项目增加新的条目,并选择条目类型为XAML template type中的XAML View。这个XAML View默认不带有背靠代码,并且名字中带有设备名称(参考 ResourceContext.QualifierValues),具体命名的格式为: [pageName].DeviceFamily-[qualifierString].xaml
,例子:
- MainPage.DeviceFamily-Desktop.xaml
- MainPage.DeviceFamily-Tablet.xaml
也可以用目录来区分:
- DeviceFamily-Desktop/MainPage.xaml
- DeviceFamily-Tablet/MainPage.xaml
当设备类型不匹配时,默认d的MainPage.xaml会启用
也可以创建带背靠代码的XAML template type中的Blank page,但是这样你的代码逻辑要选择何时加载何种页面:
if (Windows.System.Profile.AnalyticsInfo.VersionInfo.DeviceFamily == "Windows.Tablet")
{
rootFrame.Navigate(typeof(MainPage_Tablet), e.Arguments);
}
else
{
rootFrame.Navigate(typeof(MainPage), e.Arguments);
}
Tailored multiple views sample例子采用GetIntegratedDisplaySize来判断加载的Page类型。
show multiple views
略过
Alignment, margin, padding
大部分UI元素都是从FrameworkElement 继承。FrameworkElement 定义了很多跟尺寸、对齐、外边距、内边距等可以影响布局的部属。
尺寸相关:
- Height和Width指定乐元素的大小,默认值为NaN。你可以设置为绝对的值(以effective pixels衡量),或者设置为自动大小或者按比例分配。
- ActualHeigh和ActualWidth这两个只读部属提供运行时的真实大小。在SizeChanged事件中,这两个值会随着改变。注意RenderTransform 不会改变这两个值。
- MinWidth/MaxWidth和MinHeigh/MaxHeigh用来给元素大小增加约束
- FontSize和其他文字部属用来控制文字元素的显示。文字元素没有具体指定的大小,但是有ActualHeight和ActualWidth。
对齐相关:
- HorizontalAlignment和VerticalAlignment 指定横向对齐和竖向对齐的
- 默认的对齐方式时Stretch,这导致元素的上下左右边界和容器的大小对齐。如果Heigh和Width指定了数值,那么默认的Stretch就会被替换成Center。有一些元素,比如按键,其样式会覆写默认的Stretch值。
- HorizontalContentAlignment和VerticalContentAlignment 指定元素内容的对齐方式
- 对齐方式会影响裁剪效果。比如左对齐的元素,如果超出了实际容器大小(ActualWidth),那么其右边超出的部分会被裁剪。
- 文本元素使用TextAlignment来指定对齐。建议不要修改默认的左对齐,更多信息参考Typography。
外边距相关
- 外边距处于元素周围,但是其实不属于元素,并不会影响元素的ActualHeight和ActualWidth。外边距上触发的事件(比如点击)不属于对应元素。
- 外边距可以统一指定,比如Margin=“20"代表所有方向的外边距都是20;也可以单独指定,Margin=“0,10,5,25“指定了左上右下的外边距
- 元素间的外边距可以合并,如果两个元素的外边距都是10,那么这两个元素靠在一起的话,间隔是20
- 外边距可以为负值,但是会引起意想不到的想过,使用的时候要谨慎。
- 外边距属于优先级较次的约束,其他约束比如容器大小会先使用上。另外,由于外边距的存在,可能会导致元素的尺寸缩为零。
内边距
- 内边距,用于在元素内部指定内容到边框的距离。和外边距不一样,内边距不是FrameworkElement的部属。不同的类可以定义自己的内边距。
- Control.Padding,被所有控件继承,如果控件没有内容,那么内边距不会生效。
- Border.Padding,从边框(BorderThickness/BorderBrush指定)到Child元素的间距
- ItemsPresenter.Padding,
- TextBlock.Padding和RichTextBlock.Padding,应用在文本元素上。值得注意的是,文本元素没有背景,所以无法显现内边距。建议使用Block 容器的Margin部属。
一些建议:
- 使用尺寸值的时候,建议以4epx(有效像素)为单位来设置值,参考effective pixels and scaling
(本篇完)