每种类型操作系统对屏幕的定义不一样,在开发跨平台应用软件时,我们一般都会使用布局(Layout)来管理复合控件(Composite)中子控件的位置和大小。通过布局,程序员可以充分展示那些埋没已久的艺术细菌,哦不,是艺术细胞!Composite及其子类都可以设置布局,常用的有Shell,Group,TabFolder等,本文将像Composite等这类可以放置布局的控件称为布局容器,布局中的子控件称为布局子控件。
默认情况下,在WindowBuilder添加一个布局容器并没有设置它的布局(Layout),也就是WindowBuilder里的绝对像素坐标布局(Abstract Layout)。这种布局不能使用窗口大小的变化,也不利于跨平台,一般不推荐使用。
用于布局的类都扩展自抽象基类Layout。SWT提供了几种常用的布局类,在开发中基本够用,本文称之为SWT标准布局。当然,你也可以很容易的写出自己的布局类。
下面是SWT的标准布局:
WindowBuilder Layout图
FillLayout是最简单的布局类,将子控件按一行或者一列摆列,每个子控件具有相同的尺寸,即最高子控件的高度和最宽子控件的高度。当布局容器缩小时,所有子控件也等比例缩小,而不会自动换行(列)。
天生我材必有用,再简单的FillLayout主要用于:
(1). 摆放任务栏或工具栏按钮;
(2). 堆叠中的复选框组。
(3). 布局容器的只有一个子控件。
布局容器的FillLayout在WindowBuilder中的设置图,
Type属性:可以设置水平摆放还是竖直摆放。
关于其它的布局属性意义,千言万语不如下图一目了然:
WindowBuilder切换到Source页可以浏览和编辑生成的代码,如下:
FillLayout fl_shlFilllayout = new FillLayout(SWT.HORIZONTAL);
fl_shlFilllayout.marginWidth = 60;
fl_shlFilllayout.spacing = 40;
fl_shlFilllayout.marginHeight = 50;
shlFilllayout.setLayout(fl_shlFilllayout);
RowLayout也是一行或者一列摆放,但比FillLayout更常用的,因为它有更多设置的属性,而且每个子控件可以有不同的尺寸,可以通过布局数据(RowData)独立设置。这里有两个概念需要搞清楚,布局(Layout)由布局容器(如Composite)来设置,而布局数据(如RowData)由布局子控件(如Button)来设置。
布局容器的RowLayout在WindowBuilder的设置图。
下图由WindowBuilder画出,RowLayout为默认设置,但每个子控件的尺寸经过调整。
布局属性意义见下:
Center:居中
Fill:填充,true时类似于FillLayout,false是子控件的尺寸由子控件的属性设置。
Justify:根据布局容器尺寸调整子控件之间的间距,
Pack:默认为true,子控件会使用最恰当的尺寸。当设置为false时,所有子控件等尺寸,为所有子控件的最大尺寸。
Wrap:默认为true,子控件会根据布局容器的大小自动换行。
若设置为false,子控件不会自动换行。
子控件(如按钮)的布局数据(RowData)属性如下图:
Exclude:当false时,子控件隐藏。下图隐藏了“Eclipse RCP”按钮:
生成的关于Eclipse RCP按钮的代码如下:
Button btnEclipseRcp = new Button(shlRowlayouth, SWT.NONE);
RowData rd_btnEclipseRcp = new RowData(SWT.DEFAULT, 74);
rd_btnEclipseRcp.exclude = true;
btnEclipseRcp.setLayoutData(rd_btnEclipseRcp);
btnEclipseRcp.setText("Eclipse RCP");
前面都是一维的布局,但用户图形界面(GUI)是二维的。一维的布局难以适应更复杂的情况。网格布局(GridLayout)是来自二维世界的布局,它将子控件放置在网格单元中。这里,我们把网格中每个单元格称为网格单元(Cell)。
布局容器的布局(GridLayout)属性如下图:
从属性的名字上,大家应该很容易明白,这里不再详细说明。下图为以上属性图所对应的界面,网格共3列,子控件间的距离在水平、竖直方向都为5个像素,边距宽度高度为5个像素。
跟RowLayout一样,单单设置布局容器的GridLayout是不够的,每个子控件都各有特色,这需要关联各自的布局数据(GridData)。下图为文本编辑框的GridData
grabExcessHorizontalSpace: 可伸缩位设置。当设置为true时,该子控件所在的网格单元填充额外的空白。本例中,文本编辑框所在的网格单元随着窗口在水平方向伸缩而伸缩。注意,这里说的是网格单元会伸缩,而不是子控件(文本编辑框)会伸缩。好绕口是吧,要知道网格单元(Cell)不等于子控件,而是放置子控件的容器。若要想让子控件(文本编辑框)随着窗口伸缩而伸缩,请往下读。
horizontalAlignment:指的是子控件相对于网格单元的水平对齐方式,确定子控件在网格单元中的位置。可以左对齐(LEFT),右对齐(RIGHT),居中(CENTER)和填充(FILL)。本例中,标签New Label为右对齐,文本编辑框则填充(FILL)网格单元。你可以试试把文本编辑框改成其它的对齐方式(如左对齐),如下图所示:
当窗口在水平方向伸缩时,文本编辑框不会伸缩。这多么不美观啊,你的审美第六感会告诉你:文本编辑框应该填充网格单元(horizontalAlignment = SWT.FILL),并且随着窗口伸缩而伸缩(grabExcessHorizontalSpace = true)。
horizontalIndent:网格单元左侧缩进值,以像素为单位,用于控制子控件在网格单元中的位置。
widthHint:布局容器刚打开时子控件的宽度,缺省值为-1,表示SWT提供的默认宽度。当水平填充(horizontalAlignment = SWT.FILL)时无效。
minimumWidth:子控件的最小宽度,缺省值为0,表示SWT提供的默认最小宽度(非0)。只有在可伸缩(grabExcessHorizontalSpace = true)时有效。
horizontalSpan:指定子控件在水平方向上占据多少个网格单元,默认为1。本例中第二行的组合框(Combo)就占据了2个网格单元(horizontalSpan = 2)。
竖直方向的属性类似于水平属性,读者可以触类旁通。省点墨水,为共建节约型社会的努力!
GridLayout很好很强大,既容易学习,又相当实用。根据我们团队多年的开发经验,除非遇到特殊的要求,总是把GridLayout作为GUI设计的首选布局,即使只有一行或者一列。
布局的目的是为了确定子控件在布局容器中的位置,从另一个角度,我们也可以这样理解布局:如果把布局容器看成一块磁铁,每个子控件也比作磁铁,那么布局容器的每一边与子控件之间必然存在磁场,子控件与子控件之间也必然存在磁场。距离不同,磁场强度也不同。不同的布局对应于不同的磁场分布,不同的磁场分布对应不同的布局。因此,只要确定了磁场分布的各种磁场强度,就可以确定子控件在布局容器中的位置,也就是布局。我们把这种磁场强度就是某子控件的粘附值(FormAttachment),表示某子控件与布局容器、相邻子控件的距离,以像素为单位。这就是窗体布局,如下图所示。
窗体布局是另外一种二维布局,它的原理是通过为子控件的上下左右每一边创建窗体粘附值(FormAttachment),并存储于该子控件的布局数据(FormData)中。有兴趣的读者可以玩一下,这里就不多介绍了。
上图布局的设计好不好,元芳,你怎么看?
回大人,此图必有蹊跷。虽然表面上看去很正常,普通人是看不出的,但根据属下多年来对马克思主义哲学的深刻领悟,从“运动”的角度,可以透过现象看本质。运行一下程序,调整窗口大小,很容易出现控件交叉的现象(如下图)。
可见,在设计FormLayout时很容易产生潜在的问题。就个人而言,我不推荐使用这种布局,因为我不能保证开发团队里的每个人都是元芳。
(待续...)
---------------------- 本博客所有内容均为原创,转载请注明作者和出处 -----------------------
作者:刘文哲
联系方式:[email protected]
博客:http://blog.csdn.net/liuwenzhe2008
-------------------------------------------------------------------------------------------------------------