XAML布局回顾

Grid和StackPanel是核心布局,尤其以Grid最为重要。

Grid是网格布局,XAML的设计者有可能参考了Html里的Table设计了Grid布局,但进行了改进。Html中的Table是tr套td,要想变动行列非常麻烦。XAML中的Grid使用的是指定行列序号和跨行跨列数的方式,修改起来灵活方便。

Grid里行或列的大小有三种方式,一种方式是固定大小(Double值),一种方式是由里面的内容决定(Auto),一种方式是按比例分割剩余空间(Double值加*,剩余空间由外部容器的大小决定)。

指定一个固定的Double值表示固定大小,它的单位默认为px(如果不写单位),也可以是其他单位,见下。

px (默认值)为与设备无关的单位(每个单位 1/96 英寸)

in 表示英寸;1in==96px

cm 表示厘米;1cm==(96/2.54) px

pt 表示磅;1pt==(96/72) px

这里的px不是确定的像素值,而是一种与设备无关的单位,具体的解释见后文。

指定为Auto指大小由最大的内容决定,由里面最大的内容把这个行或列撑开。

星的含义是按比例分割外界给它的剩余空间,是1的简写,把星前面的数加在一起做分母,每一个星前面的数做分子,作为这个行或列的比例。

还需结合最大值和最小值限制。

Grid.Row和Grid.Column指定在第几行和第几列,Grid.RowSpan和Grid.ColumnSpan指定跨几行和跨几列,行和列都是从0开始。

以上就是Grid布局的要点,其中的核心是分为了固定大小,内容决定和按比例分割剩余空间三种情况,这是自适应布局的基础。

Android中的线性布局也支持固定大小,内容决定和按比例分割剩余空间三种情况,所以Android也可以并且应该制作自适应布局的界面。但线性布局一次只支持在一个方向上布局,如要实现复杂的网格效果,需要多层嵌套,比较麻烦。而Android中的Grid布局不能实现按比例分割剩余空间,只能支持部分自适应的情况。还有一种相对布局,实际界面设计时需要这几种布局结合使用,使用技巧和XAML不同,但下文讨论的自适应布局的原则对Android平台也是适用的。

StackPanel相当于Grid中全是Auto,但不需要指定序号,也省去了Grid定义部分分行或分列那块。而且如果添加或移除中间部分的元素,不用改序号。所以在只需要按内容大小排列的时候使用StackPanel比Grid更简洁方便。

Canvas就是绝对定位布局,通过指定X和Y坐标布局,待布局的元素应该有固定宽高。

Border是边框,是装饰,严格来讲不能算布局,在里面只能放一个元素,它给加个边,没有Margin和Padding时对大小无影响。可以给布局容器套一层Border,给整个区域加个边框。

ScrollViewer支持里面的内容滚动,里面的内容一般会超出容器大小,只显示一部分,由滚动条控制滚动。

ViewBox对内容进行缩放,以适应容器大小。

WrapPanel是流式布局,一行一行的排,一行不够折行到下一行。里面的东西固定宽高。

UniformGrid是固定行数或列数,内容一行一行或一列一列地排。

默认的Z方向上下关系是谁在后面,谁在上面。指定Panel.ZIndex可指定上下位置关系。

自适应动态布局
自适应动态布局是指界面的控件大小和位置会随着控件中内容的变化和窗口大小的变化而自动调整,以达到较好的显示效果的布局方案。

与之相对的是传统的固定布局,固定布局不能随内容和窗口大小变化,所以一般只支持固定窗口大小,显示内容也不能随意变化。

制作固定布局界面时一般直接指定控件的大小和位置。

要想制作自适应动态布局界面需要使用支持自适应动态布局的UI平台。基于XAML的平台就是这样一个平台,Android平台也是,以前的Winform平台就不能完全支持自适应动态布局。

使用支持自适应动态布局的平台,也不是说就一定能制作出自适应动态布局的界面。如果还使用直接指定控件的大小和位置的方法,制作出的还是固定布局界面。

要想制作自适应动态布局界面,需要使用动态布局容器(Grid,StackPanel等)并遵循一定的原则。

自适应动态布局详解
上文说自适应布局会随着内容和窗口大小的变化自动调整控件大小和位置,下面来详细分析一下。

变化的方面,即需要自适应的方面包括:

  1. 窗口大小会变,对应全屏界面时就是显示器分辨率会变化,就是说外层所给的布局总空间会变化。
  2. 内容大小会变,包括文字多少会变,图片大小会变,列表条目多少会变等情况。
  3. 显示屏DPI会变。

第一种情况我们用比例分割剩余空间(星)去应对,第二种情况我们用内容决定大小(Auto)去应对,第三种情况系统帮我们应对。下面详细说说第三种情况。

上文提到WPF默认单位px不是像素,是与设备无关的单位,系统会处理这个单位和像素之间的关系,处理的依据是DPI。

Windows系统认为非高分辨率屏幕的DPI一般为96DPI左右,所以设备无关的单位和像素之间的比例为1比1。遇到高分辨率的屏幕(如Surface Pro 3),调节控制面板-外观和个性化-显示选项中的更改所有项目的大小选项,即更改系统DPI,见下图,

Image(1)

更改系统DPI后WPF中设备无关的单位和像素之间的比例不再是1比1,而是1比多于1的值,也就是说系统会把界面等比例放大。这个DPI设置指的是桌面DPI,会影响所有的桌面程序。也就是说Windows希望用户对桌面程序的显示方式进行统一的设置。如果想显示更多的内容就不要选择放大,如果想显示更大的内容就开启放大。这种控制是全局的,用户可以得到统一的用户体验。

但以前的桌面程序UI系统可能对这种放大机制的支持不是很好,需要应用程序自己控制。在WPF中放大机制能得到系统良好的支持,矢量的内容(文字,渐变画刷,路径等)不会有问题,但位图的内容有问题。所以要尽量使用矢量的内容,位图内容需额外处理。

Windows Store App使用的应该不是上述设置的桌面DPI,而是内部设置的DPI,但同样也能得到良好的界面等比例放大支持。

Android系统也使用类似的机制来处理DPI变化的问题,界面也会根据DPI的变化进行等比例缩放,DPI为系统内部设置,和Windows Store App一样不允许用户修改(Root后可修改)。

总之,DPI的问题系统帮我们处理了,我们只考虑第一个和第二个方面的问题。所以可用空间变化和内容变化是核心问题。

下面对核心问题给出解决方案,即核心原则。

自适应动态布局核心原则

  1. 有范围,有最优。

有一个自适应的范围,有一个最合适分辨率或窗口大小,不是无限适应。

比如,支持最小分辨率为1366768的全屏程序,最合适分辨率为19201080,最大分辨率支持到2560*1440,长宽比可以变化,不一定为16:9。但小于最小分辨率可能造成显示不全,大于最大分辨率可能造成显示效果不好。在最合适分辨率时显示效果最好,在支持的范围内的显示效果都可以接受。

  1. 定DPI,定大小。

使用默认的96DPI进行设计,根据DPI进行放大的任务交给系统。所以一般不用添加动态调整字号的功能,使用一套固定的字号大小就可以了。我们假设大家都是使用默认DPI进行设计的,所以大家的大小标准是一致的。用户想看小的不开系统放大选项,用户想看大的就开系统放大选项,选择权在用户,而且各个程序的体验是一致的。

高级的软件,可在某些特定区域支持动态调整字号,比如Visual Studio的代码编辑区。

  1. 有定有变,比例分割填剩余。

定的意思是区域大小是由内容决定的或手动指定一个固定大小。内容决定时,在内容不变的情况下,窗口大小变化了,该块区域大小不变。区域大小不依赖于窗口大小而变化。相对窗口大小来说它是定的部分,由内容本身决定的。

手动指定固定大小也是定的情况,但使用内容决定优于使用固定大小,能不写固定宽高就不写固定宽高,由Margin和Padding加上内容来控制大小。一块内容它的大小不应该是我给他强制指定一个大小,而应该是由它本身自己决定的,由内容(文字或图片,文字指字的多少字体字号),Margin和Padding来决定这一块区域的大小。如果指定了固定大小,内容(文字)变化了,就有可能存在多余空白或显示不全。

变的意思是指区域大小会随窗口大小变化而变化。由于窗口大小会发生变化,所以除去上述定的部分外的剩余空间会发生变化,剩余空间由变的部分来填充,而且可能会由多个变的部分按比例分割来填充。

变的部分有几种情况,包括:

A. 自动缩放的情况。内容本身能自动缩放,图片能自动缩放,但可能效果会不好,视频可以自动缩放,ViewBox包起来的内容亦可以自动缩放。

B. 文本的情况。文本区域,如果这块区域大小变了的话,显示效果一般可以接受。文字少的话是一行,多了的话它会多折几行,也可能会截断,出…,或者出滚动条,也可不出滚动条用光标控制滚动,来通过滚动的方式显示全部文本。

C. 可滚动区域。ListView,DataGrid等数据呈现区都属于这种情况。

说到这里可以看出,其实自适应动态布局的核心问题是确定定的部分和变的部分。下面分享一下经验。

标题,工具栏等一般为定的部分,表单的标签部分也为定的部分。

空间大了的话由哪部分去撑,或者空间小了去挤哪部分,这种区域一般为变的部分。

展示用图片,视频,不定内容的文本块,输入内容的文本框,大量内容呈现的可滚动区域一般为变的部分。

自适应动态布局的优势
固定布局和动态布局,固定布局固定大小和位置,动态布局使用布局系统进行布局。动态布局可以自适应窗口大小变化,如果没有自适应窗口大小变化的需求,也应该使用动态布局,因为设计有可能会变,固定布局没有可维护性。

动态布局是符合设计师的思路的。固定布局相当于把设计思路给栅格化了,栅格化的过程应该由布局系统在呈现界面时动态完成,而不应该由开发人员静态完成。

PS做的文字和矢量图形栅格化成PNG位图,给人家了,能改吗,不能改或者很难改。如果给人家PSD原图,就好改,

用Grid这种动态的布局系统来布局实际上是表达布局思路。实际的大小和位置是由布局系统通过动态计算得到的,即时运算出来的。给布局系统指定布局的依据和参数,让布局系统自动计算大小和位置,尽量不要手动指定大小和位置。

给布局系统指定布局的依据和参数,让布局系统自动计算大小和位置,尽量不要手动指定大小和位置。
类似于矢量图工具指定图形的参数,呈现时像素是即时运算出来的,而不是像位图一样直接存储像素信息。

WPF等XAML呈现引擎是一个矢量系统,更近一步地说是矢量动画系统(类似的系统还有Flash,JavaFx,Android等),其使用两层的方式进行呈现。1.布局系统计算大小位置,2.渲染引擎栅格化成像素显示。

其他原则

  1. Margin是边距不是坐标,如果需要使用坐标用Canvas。
  2. Grid要使用Auto,由内容决定大小,不要手动设置大小。
  3. 要使用嵌套Grid替代跨行跨列,但需权衡,嵌套Grid结构好些,思路和逻辑性清楚,好维护,但损失性能,跨行跨列,性能好些。
  4. 需要按内容大小自动排列的时候优先用StackPanel,不要用全是Auto的Grid,易于维护。
  5. 比例分割剩余空间时,某些行或列,可以用最小或最大宽高进行额外控制,以达到较好的效果。

页面结构设计
下面列举出一些常见的页面结构设计思路。

  1. 页面结构是分层的,一般可分为二至三层。
  2. 每一层都可能有标题和内容区之分,最外层可包括页面标题和内容区,内容区可再分为几个子区域,每个子区域可分为标题区和内容区两部分。
  3. 如果存在第三层嵌套,和第2层类似,也可再把2级区域的内容区分为几个子区域,子区域可以有标题。
  4. 标题区可能换为工具栏区或兼有工具栏的功能。
  5. 区域之间的分割要明显。
  6. 不同的层次要易于区分。
  7. 最外层或第二层的区域可使用TabControl提供内容区切换功能。
  8. 主视图和详视图或功能切换区和实际内容区一般左右或上下排布。
  9. 如出现两层功能切换的情况,可以第一层左右排布,第二层上下排布。也可第一层上下排布,第二层左右排布。或同为上下排布,功能切换区第一层在上边,第二层在下边,避免重复感。
  10. 页面中的一个或多个核心内容区一般为可滚动区域或可缩放区域,要占据页面最大的空间,要能明显辩认出。核心内容区不宜过多,避免产生杂乱感。

布局设计模式
从实际工作经验中,总结出了几种布局设计模式,和GoF设计模式类似,模式指在某种特定的场景中可以套用的设计方法。

布局设计模式在相似的页面场景中也可以套用,能达到较好的效果。包括,

局部布局设计模式:

  1. 工具栏模式
  2. 压缩空白模式
  3. 表单模式
  4. 表格模式
  5. 工具箱模式
  6. 选择题模式

区域布局设计模式:

  1. 头尾模式
  2. 边栏模式
  3. 可变等分模式
  4. 文档模式
  5. 图片模式