摘要
当你用SWT编写应用程序的时候,你可能需要用布局(layout)来给你的窗口设置特特定的外观。布局控制组合窗口组件(composite)中的子组件的位置和大小。布局类都是抽象类Layout的子类。这篇文章为你展示了如何使用标准布局,以及如何编写定制你自己的布局。
作者:Carolyn MacLeod, OTI
2001年3月22日
当你用SWT编写应用程序的时候,你可能需要用布局(layout)来给你的窗口设置特特定的外观。布局控制组合窗口组件(composite)中的子组件的位置和大小。布局类都是抽象类Layout的子类。这篇文章为你展示了如何使用标准布局,以及如何编写定制你自己的布局。
作者:Carolyn MacLeod, OTI
2001年3月22日
修订:Shantha Ramachandran, OTI
2002年5月2日
2002年5月2日
翻译:Link
2006年5月
原文载于
Eclipse Corner
http://www.eclipse.org/articles/Understanding%20Layouts/Understanding%20Layouts.htm
--------------------------------------------------------------------------------
布局
概述
当你用标准小窗口工具箱(SWT)编写应用程序的时候,你可能需要用布局来为你的窗口定制特定的外观。布局控制组合窗口组件(composite)中的子组件的位置和大小。 布局类都是抽象类Layout的子类。SWT提供了很多标准的布局类,你可以编写定制的布局类。
在SWT中,定位和尺寸缩放不能自动地发生。应用程序可以初始化地确定一个Composite的子组件的大小和位置,或者可以通过一个调整大小的监听器来实现。另一个选择是指定一个布局类来定位和缩放这些子组件。如果子组件没有给定的尺寸大小,它们将会具有零尺寸(zero size)并且是不可见的。
当你用标准小窗口工具箱(SWT)编写应用程序的时候,你可能需要用布局来为你的窗口定制特定的外观。布局控制组合窗口组件(composite)中的子组件的位置和大小。 布局类都是抽象类Layout的子类。SWT提供了很多标准的布局类,你可以编写定制的布局类。
在SWT中,定位和尺寸缩放不能自动地发生。应用程序可以初始化地确定一个Composite的子组件的大小和位置,或者可以通过一个调整大小的监听器来实现。另一个选择是指定一个布局类来定位和缩放这些子组件。如果子组件没有给定的尺寸大小,它们将会具有零尺寸(zero size)并且是不可见的。
下面的图中显示了一些讨论布局时通常使用的术语。Composite(在这个例子中,是一个TabFolder)有一个
location,一个
clientArea和一个
trim。Composite的大小是
clientArea的大小加上
trim的大小。这个Composite有两个并排布置的子组件。布局(Layout)管理子组件的大小和位置。这个布局允许子组件之间的spacing,以及子组件和布局边缘之间的
margin。布局的大小和Composite的
clientArea的大小一样。
![](http://www.amuhouse.com/link/attachments/month_0605/n2006563532.jpg)
小窗口部件(widget)的首选大小是显示它的内容所需要的最小的大小。在这个composite的例子中,首选大小是包含所有它的子组件的最小的矩形。如果子组件已经被应用程序定位,composite会基于这些子组件的大小和位置计算它自己的首选大小。如果composite用布局类来定位它的子组件,它要求布局来计算
clientArea的大小,然后加上trim来决定它的首选大小。
标准布局
在SWT库中的标准布局类是:
· FillLayout – 在单行或者单列中放置相同大小的小窗口部件。
· RowLayout – 在一行或者多行中放置小窗口部件,应用了fill,wrap和spacing等选项。
· GridLayout – 在网格中放置小窗口部件。
· FormLayout * 2.0新特性* – 通过定义四边的“粘贴”位置来放置小窗口部件。
在SWT库中的标准布局类是:
· FillLayout – 在单行或者单列中放置相同大小的小窗口部件。
· RowLayout – 在一行或者多行中放置小窗口部件,应用了fill,wrap和spacing等选项。
· GridLayout – 在网格中放置小窗口部件。
· FormLayout * 2.0新特性* – 通过定义四边的“粘贴”位置来放置小窗口部件。
为了使用这些标准布局,你必须导入SWT的布局包:
import org.eclipse.swt.layout.*;
布局是可插件化的。为了设置composite小窗口部件的布局,你可以使用composite小窗口部件的
setLayout(Layout)方法。在接下来的代码中,一个Shell(Composite的子类)用RowLayout来定位它的子组件:
Shell shell =
new Shell();
shell.setLayout(new RowLayout());
shell.setLayout(new RowLayout());
一个布局类可能有一个对应的布局数据(layout data)类 – 包括了定制子组件的布局数据的Object的子类。习惯上,布局数据类通过取代布局类类名中的Layout为Data为标识。例如:标准布局类RowLayout具有布局数据类RowData。布局类GridLayout使用布局数据类GridData,而布局类FormLayout具有名称为FormData的布局数据类。一个窗口小部件的布局数据类的设置如下:
Button button = new Button(shell, SWT.PUSH);
button.setLayoutData(new RowData(50, 40));
Button button = new Button(shell, SWT.PUSH);
button.setLayoutData(new RowData(50, 40));
这个文档中的例子
这个文档中绝大多数的快照是通过运行下面的示例代码的变更版本来得到的。我们将会改变布局的种类,使用的选项,或者是子组件的数目。
import org.eclipse.swt.*;
import org.eclipse.swt.widgets.*;
import org.eclipse.swt.layout.*;
public class LayoutExample {
public static void main(String[] args) {
Display display = new Display();
Shell shell = new Shell(display);
// Create the layout.
RowLayout layout = new RowLayout();
// Optionally set layout fields.
layout.wrap = true;
// Set the layout into the composite.
shell.setLayout(layout);
// Create the children of the composite.
new Button(shell, SWT.PUSH).setText("B1");
new Button(shell, SWT.PUSH).setText("Wide Button 2");
new Button(shell, SWT.PUSH).setText("Button 3");
shell.pack();
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) display.sleep();
}
}
}
import org.eclipse.swt.*;
import org.eclipse.swt.widgets.*;
import org.eclipse.swt.layout.*;
public class LayoutExample {
public static void main(String[] args) {
Display display = new Display();
Shell shell = new Shell(display);
// Create the layout.
RowLayout layout = new RowLayout();
// Optionally set layout fields.
layout.wrap = true;
// Set the layout into the composite.
shell.setLayout(layout);
// Create the children of the composite.
new Button(shell, SWT.PUSH).setText("B1");
new Button(shell, SWT.PUSH).setText("Wide Button 2");
new Button(shell, SWT.PUSH).setText("Button 3");
shell.pack();
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) display.sleep();
}
}
}
运行以上代码得到如下的结果:
![](http://www.amuhouse.com/link/attachments/month_0605/m20065635432.gif)
如果用户调整了shell的大小使得右边的Button 3没有足够的空间,RowLayout将Button 3换行放置到下一行,如下所示:
![](http://www.amuhouse.com/link/attachments/month_0605/c2006563555.gif)
我们将会看到,使用布局和调整大小有紧密的关系。因此,这个文档中大多数的例子展示了当Composite变大或者变小时将会发生什么,以举例说明布局是如何工作的。
FillLayout
FillLayout是最简单的布局类。它在单行或者单列中放置小窗口部件,强制它们为
同一大小。开始,窗口小部件都和最高的容器小部件一样高,和最宽的窗口小部件一样宽。FillLayout不会换行,而且你不能定制空白(margin)和间隔(spacing)。当Composite只有一个子组件时FillLayout也能被使用。例如,如果一个Shell有一个Group子组件,FillLayout会使Group完全填充这个Shell。
这里是示例代码相关的一部分。首先我们创建了一个FillLayout,然后(如果我们需要垂直的样式)我们设定它的
type域为SWT.VERTICAL,并且把它设定到Composite(一个Shell)中。这个Shell有三个按扭子组件,B1,B2和B3。注意在FillLayout中,子组件总是有相同的大小,而且充满可用空间。
FillLayout fillLayout = new FillLayout();
llLayout.type = SWT.VERTICAL;
shell.setLayout(fillLayout);
new Button(shell, SWT.PUSH).setText("B1");
new Button(shell, SWT.PUSH).setText("B2");
new Button(shell, SWT.PUSH).setText("Button 3");
FillLayout fillLayout = new FillLayout();
llLayout.type = SWT.VERTICAL;
shell.setLayout(fillLayout);
new Button(shell, SWT.PUSH).setText("B1");
new Button(shell, SWT.PUSH).setText("B2");
new Button(shell, SWT.PUSH).setText("Button 3");
下面的表格显示了水平和垂直样式的FillLayout,初始状态以及当父组件增大时的不同点
|
初始状态
|
缩放后
|
fillLayout.type = SWT.HORIZONTAL
(默认)
|
![]() |
![]() |
fillLayout.type = SWT.VERTICAL
|
![]() |
![]() |
RowLayout
RowLayout比FillLayout的使用更为普遍,因为它能够
换行(warp)和提供了可配置的空白(margin)和间隔(spacing)。RowLayout有很多可配置的域。而且,RowLayout中的每个小窗口部件的高度和宽度都可以通过使用
setLayoutData设置小窗口部件的RowData对象来指定。
RowLayout的可配置域
Type * 2.0新特性*
type域控制RowLayout在水平行中还是在垂直列中放置小窗口部件。默认情况下RowLayout是水平方式的。
Wrap
warp域控制RowLayout当前行中没有足够的空间时是否把小窗口部件换行放放置到下一行。默认情况下RowLayout是换行的方式.
pack
如果 pack为true,RowLayout中的小窗口部件会采用它们正常的大小,而且它们会尽可能的左对齐。如果pack为false,小窗口部件会填充可用的空间,就象FillLayout中的小窗口部件一样。默认情况下RowLayout的pack为false。
如果 pack为true,RowLayout中的小窗口部件会采用它们正常的大小,而且它们会尽可能的左对齐。如果pack为false,小窗口部件会填充可用的空间,就象FillLayout中的小窗口部件一样。默认情况下RowLayout的pack为false。
justify
如果 justify域为true,RowLayout中的小窗口部件从左到右在可用的空间中伸展开。如果父Composite变大,额外的空间均匀的分布在小窗口部件之中。如果 pack和 justify都为true,窗口小部件采用它们正常的大小,而且额外的空间被放置在这些小窗口部件之间以保持它们完全的散开(justify)。默认情况下RowLayout不进行散开。
如果 justify域为true,RowLayout中的小窗口部件从左到右在可用的空间中伸展开。如果父Composite变大,额外的空间均匀的分布在小窗口部件之中。如果 pack和 justify都为true,窗口小部件采用它们正常的大小,而且额外的空间被放置在这些小窗口部件之间以保持它们完全的散开(justify)。默认情况下RowLayout不进行散开。
MarginLeft, MarginTop, MarginRight, MarginBottom和Spacing
这些域控制小窗口部件之间的象素数( spacing)以及小窗口部件和父Composite的边界之间的象素数( margin)。默认情况下RowLayout为空白(margin)和间隔(spacing)保留3个象素。空白和间隔在下面的图中展示:
这些域控制小窗口部件之间的象素数( spacing)以及小窗口部件和父Composite的边界之间的象素数( margin)。默认情况下RowLayout为空白(margin)和间隔(spacing)保留3个象素。空白和间隔在下面的图中展示:
![](http://www.amuhouse.com/link/attachments/month_0605/j2006564112.gif)
RowLayout的例子
下面的示例代码中创建了一个RowLayout,设置它的所有域为非默认的值,然后把它设定到一个Shell中。
RowLayout rowLayout = new RowLayout();
rowLayout.wrap = false;
rowLayout.pack = false;
rowLayout.justify = true;
rowLayout.type = SWT.VERTICAL;
rowLayout.marginLeft = 5;
rowLayout.marginTop = 5;
rowLayout.marginRight = 5;
rowLayout.marginBottom = 5;
rowLayout.spacing = 0;
shell.setLayout(rowLayout);
rowLayout.wrap = false;
rowLayout.pack = false;
rowLayout.justify = true;
rowLayout.type = SWT.VERTICAL;
rowLayout.marginLeft = 5;
rowLayout.marginTop = 5;
rowLayout.marginRight = 5;
rowLayout.marginBottom = 5;
rowLayout.spacing = 0;
shell.setLayout(rowLayout);
如果你仅使用默认的域值,你只需要一行代码:
shell.setLayout(new RowLayout());
在下面的表格中,展示了设置指定的域的结果:
|
初始状态
|
缩放后
|
wrap = true
pack = true
justify = false
type = SWT.HORIZONTAL
(
默认
)
|
![]() |
![]() |
wrap = false
(
没有足够的空间则进行修剪
)
|
![]() |
![]() |
pack = false
(
所有的小窗口部件都有相同的大小
)
|
![]() |
![]() |
justify = true
(
小窗口部件在可用空间中伸展开来
)
|
![]() |
![]() |
type = SWT.VERTICAL
(窗口小部件被垂直地安排在一列中
)
|
![]() |
![]() |
和RowLayout一起使用RowData对象
每由
RowLaout
控制的一个小窗口部件都能通过设置它的
RowData
对象指定它初始的宽度和高度。以下代码用
RowData
对象来改变一个
Shell
中按钮(
Button
)的初始大小。
import org.eclipse.swt.*;
import org.eclipse.swt.widgets.*;
import org.eclipse.swt.layout.*;
public class RowDataExample
public static void main(String[] args) {
Display display = new Display();
Shell shell = new Shell(display);
shell.setLayout(new RowLayout());
Button button1 = new Button(shell, SWT.PUSH);
button1.setText("Button 1");
button1.setLayoutData(new RowData(50, 40));
Button button2 = new Button(shell, SWT.PUSH);
button2.setText("Button 2");
button2.setLayoutData(new RowData(50, 30));
Button button3 = new Button(shell, SWT.PUSH);
button3.setText("Button 3");
button3.setLayoutData(new RowData(50, 20));
shell.pack();
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) display.sleep();
}
}
}
import org.eclipse.swt.widgets.*;
import org.eclipse.swt.layout.*;
public class RowDataExample
public static void main(String[] args) {
Display display = new Display();
Shell shell = new Shell(display);
shell.setLayout(new RowLayout());
Button button1 = new Button(shell, SWT.PUSH);
button1.setText("Button 1");
button1.setLayoutData(new RowData(50, 40));
Button button2 = new Button(shell, SWT.PUSH);
button2.setText("Button 2");
button2.setLayoutData(new RowData(50, 30));
Button button3 = new Button(shell, SWT.PUSH);
button3.setText("Button 3");
button3.setLayoutData(new RowData(50, 20));
shell.pack();
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) display.sleep();
}
}
}
这是你运行这些代码后看到的结果。
![](http://www.amuhouse.com/link/attachments/month_0605/c20065641036.gif)
GridLayout
GridLayout可能是最有用最强大的标准布局,但它也是最为复杂的。在一个GridLayout中,一个Composite的小窗口部件子组件被放置在网格中。GridLayout有很多可配置的域,而且,和RowLayout一样,它所放置的小窗口部件可以有一个关联的布局数据对象,叫做GridData。GridLayout的强大在于它具有为GridLayout所控制的每一个小窗口部件配置GirdData的能力。
GridLayout的可配置域
NumColumns
numColumns域是GridLayout中最重要的域,而且它通常也是应用程序会设定的第一个域。小窗口部件被从左到右放置在不同的列中,当有numColumns + 1个小窗口部件被加到Composite中时,一个新的行就会被创建。默认情况下只有1列。接下来的代码创建了一个有着五个不同宽度的按钮(Button)的Shell,并用GridLayout来管理。以下的表格显示了当numColumns被设为1,2或者3时网格的情况。
Display display = new Display();
Shell shell = new Shell(display);
GridLayout gridLayout = new GridLayout();
gridLayout.numColumns = 3;
shell.setLayout(gridLayout);
new Button(shell, SWT.PUSH).setText("B1");
new Button(shell, SWT.PUSH).setText("Wide Button 2");
new Button(shell, SWT.PUSH).setText("Button 3");
new Button(shell, SWT.PUSH).setText("B4");
new Button(shell, SWT.PUSH).setText("Button 5");
shell.pack();
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) display.sleep();
}
MakeColumnsEqualWidth
makeColumnsEqualWidth域强制让列都具有相同的宽度。默认值为false。如果我们改变上面的代码让它具有相同宽度的3列,这就是我们将得到的(注意:在没有进一步说明的情况下,小窗口部件在列中都是左对齐的)。
MarginWidth, MarginHeight, HorizontalSpacing, 和VerticalSpacing
GridLayout中的 margin和 spacing域和RowLayout中的这些域是相同的。不同的地方在于左边和右边的空白(margin)被组合成 marginWidth,而项部和底部的空白被组合成 marginHeight。而且,在GridLayout中,你可以独立地指定 horizontalSpacing和 verticalSpacing,然而在RowLayout中, spacing应用于水平或者垂直样式是取决于RowLayout的类型的。
numColumns域是GridLayout中最重要的域,而且它通常也是应用程序会设定的第一个域。小窗口部件被从左到右放置在不同的列中,当有numColumns + 1个小窗口部件被加到Composite中时,一个新的行就会被创建。默认情况下只有1列。接下来的代码创建了一个有着五个不同宽度的按钮(Button)的Shell,并用GridLayout来管理。以下的表格显示了当numColumns被设为1,2或者3时网格的情况。
Display display = new Display();
Shell shell = new Shell(display);
GridLayout gridLayout = new GridLayout();
gridLayout.numColumns = 3;
shell.setLayout(gridLayout);
new Button(shell, SWT.PUSH).setText("B1");
new Button(shell, SWT.PUSH).setText("Wide Button 2");
new Button(shell, SWT.PUSH).setText("Button 3");
new Button(shell, SWT.PUSH).setText("B4");
new Button(shell, SWT.PUSH).setText("Button 5");
shell.pack();
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) display.sleep();
}
numColumns = 1
|
numColumns = 2
|
numColumns = 3
|
![]() |
![]() |
![]() |
MakeColumnsEqualWidth
makeColumnsEqualWidth域强制让列都具有相同的宽度。默认值为false。如果我们改变上面的代码让它具有相同宽度的3列,这就是我们将得到的(注意:在没有进一步说明的情况下,小窗口部件在列中都是左对齐的)。
![](http://www.amuhouse.com/link/attachments/month_0605/4200656234059.jpg)
MarginWidth, MarginHeight, HorizontalSpacing, 和VerticalSpacing
GridLayout中的 margin和 spacing域和RowLayout中的这些域是相同的。不同的地方在于左边和右边的空白(margin)被组合成 marginWidth,而项部和底部的空白被组合成 marginHeight。而且,在GridLayout中,你可以独立地指定 horizontalSpacing和 verticalSpacing,然而在RowLayout中, spacing应用于水平或者垂直样式是取决于RowLayout的类型的。
GridData 对象域
GridData是关联于GridLayout的布局数据对象。为了设置一个小窗口部件的GridData对象,你可以使用setLayoutData方法。例如:为了给一个按钮(Button)设定GridData,我们可以象下面这样做:
Button button1 = new Button(shell, SWT.PUSH);
button1.setText("B1");
button1.setLayoutData(new GridData());
当然,这段代码只是用所有它的域的默认值创建了一个GridData对象,这各没有完全设置布局数据是一样的。有两种创建带有某些已设置域的GridData对象的方法。第一种方法是直接对域进行赋值,象这样:
GridData gridData = new GridData();
gridData.horizontalAlignment = GridData.FILL;
gridData.grabExcessHorizontalSpace = true;
button1.setLayoutData(gridData);
第二种方法是是利用一些有用的API - GridData定义的“类型位”(style bits)。
button1.setLayoutData(new GridData(GridData.HORIZONTAL_ALIGN_FILL | GridData.GRAB_HORIZONTAL));
事实上,为了进一步的方便,还提供了一些常用的“类型位”的联合。
GridData是关联于GridLayout的布局数据对象。为了设置一个小窗口部件的GridData对象,你可以使用setLayoutData方法。例如:为了给一个按钮(Button)设定GridData,我们可以象下面这样做:
Button button1 = new Button(shell, SWT.PUSH);
button1.setText("B1");
button1.setLayoutData(new GridData());
当然,这段代码只是用所有它的域的默认值创建了一个GridData对象,这各没有完全设置布局数据是一样的。有两种创建带有某些已设置域的GridData对象的方法。第一种方法是直接对域进行赋值,象这样:
GridData gridData = new GridData();
gridData.horizontalAlignment = GridData.FILL;
gridData.grabExcessHorizontalSpace = true;
button1.setLayoutData(gridData);
第二种方法是是利用一些有用的API - GridData定义的“类型位”(style bits)。
button1.setLayoutData(new GridData(GridData.HORIZONTAL_ALIGN_FILL | GridData.GRAB_HORIZONTAL));
事实上,为了进一步的方便,还提供了一些常用的“类型位”的联合。
button1.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
注意到FILL_这些方便的类型设置了填充式排列(fill alignment)和抡占式(grab)填充。GridData的“类型位”只能被布尔型或者枚举型域使用。数据域必须直接进行设置。
(Swing程序员注意)在我们讲述它们的域之前关于GridData最后要注意的是:不要重用GridData对象。每一个Composite中被GridLayout管理的小窗口部件必须有一个唯一的GridData对象。如果在布局的时候一个GridLayout中的小窗口部件的布局数据为null,就会为它创建一个唯一的GridData对象。
注意到FILL_这些方便的类型设置了填充式排列(fill alignment)和抡占式(grab)填充。GridData的“类型位”只能被布尔型或者枚举型域使用。数据域必须直接进行设置。
(Swing程序员注意)在我们讲述它们的域之前关于GridData最后要注意的是:不要重用GridData对象。每一个Composite中被GridLayout管理的小窗口部件必须有一个唯一的GridData对象。如果在布局的时候一个GridLayout中的小窗口部件的布局数据为null,就会为它创建一个唯一的GridData对象。
HorizontalAlignment和VerticalAlignment
Alignment域用于指定在一个网格单元中的哪个位置水平的和/或者垂直的放置一个小窗口部件。每一个alignment域可以有下列的其中的一个值:
• BEGINNING
• CENTER
• END
• FILL
默认的horizontalAlignment值是BEGINNING(或者左对齐)。默认的verticalAlignment值是CENTER。
让我们回到我们那个在三个列中有五个按钮的例子,我们会改变Button 5的horizontalAlignment值。
HorizontalIndent
horizontalIndent域允许你通过指定一个特定数目的象素数来把一个小窗口部件右移。这个域典型的只在 horizontalAlignment值为BEGINNING时有用。我们不能用一个“类型位”来设置缩进,所以我们象下面这样把我们的例子中的Button 5缩进4个象素:
GridData gridData = new GridData();
gridData.horizontalIndent = 4;
button5.setLayoutData(gridData);
HorizontalSpan and VerticalSpan
Span域让小窗口部件占有多于一个网格单元的空间。它们通常和FILL排列(aligment)一起使用。我们象下面这样让我们例子中的的Button 5跨越最后两个网格单元:
GridData gridData = new GridData();
gridData.horizontalAlignment = GridData.FILL;
gridData.horizontalSpan = 2;
button5.setLayoutData(gridData);
如果我们决定让我们的Wide Button 2跨越两个网格单元,我们可以以这样的方式结束:
GridData gridData = new GridData();
gridData.horizontalAlignment = GridData.FILL;
gridData.horizontalSpan = 2;
button2.setLayoutData(gridData);
或者我们可以让Button 3垂直地跨越两个网格单元:
GridData gridData = new GridData();
gridData.verticalAlignment = GridData.FILL;
gridData.verticalSpan = 2;
button3.setLayoutData(gridData);
GrabExcessHorizontalSpace和GrabExcessVerticalSpace
grabExcessHorizontalSpace和 grabExcessVerticalSpace域典型的被较大的小窗口部件如Text,List或者Canvas使用,以允许当它们包含的Composite变大时它们也相应的增大。如果一个Text抢占(grab)了额外的水平空间并用用户缩放了Shell的宽度,那么这个Text会占有所有这些新的水平空间而同一行中的其他的小窗口部件会维持原来的宽度。当然,当Shell缩小时抢占了额外空间的小窗口部件也是最先收缩的。在缩放的上下文中总是考虑 grabExcessSpace域是最容易的。作为一个简单的例子,我们重新使用前面Button 3垂直地跨越两个网格单元的例子。这是这个例子的又一次展现:
如果我们缩放这个窗口,唯一发生的事情只是窗口变大了。
现在,我们让Button 3抢占额外的水平和垂直的空间,并且让B1和B4垂直地填充(不使用抢占),然后我们再一次对窗口进行缩放:
这一次,Button 3再两个方向上都增大了,而B4垂直的增大。其他的按钮维持原来的大小。因为Button 3是垂直地抢占的并且它跨越了两行,它所跨越的最后一行变高了。注意到B1并没有增大-虽然它是垂直填充的-因为它的行并没有增大。因为Button 3是水平地抢占的,它的列变宽了,并且因为它是水平的填充的,它的宽度变大了以填充这一列。这里是所有五个按钮的代码:
Button button1 = new Button(shell, SWT.PUSH);
button1.setText("B1");
GridData gridData = new GridData();
gridData.verticalAlignment = GridData.FILL;
button1.setLayoutData(gridData);
new Button(shell, SWT.PUSH).setText("Wide Button 2");
Button button3 = new Button(shell, SWT.PUSH);
button3.setText("Button 3");
gridData = new GridData();
gridData.verticalAlignment = GridData.FILL;
gridData.verticalSpan = 2;
gridData.grabExcessVerticalSpace = true;
gridData.horizontalAlignment = GridData.FILL;
gridData.grabExcessHorizontalSpace = true;
button3.setLayoutData(gridData);
Button button4 = new Button(shell, SWT.PUSH);
button4.setText("B4");
gridData = new GridData();
gridData.verticalAlignment = GridData.FILL;
button4.setLayoutData(gridData);
new Button(shell, SWT.PUSH).setText("Button 5");
在一个典型的应用程序窗口中,你通常需要有至少一个抢占的小窗口部件。如果多于一个窗口试图抢占同样的空间,那么这些额外的空间会被均匀的分配到这些抢占式的小窗口部件中:
最后一个关于抢占需要注意的地方。如果一个小窗口部件抢占了额外的水平空间并且它的父Composite变宽了,那么包含这个小窗口部件的整个列都变宽了。如果一个小窗口部件抢占了额外的垂直空间并且它的父Composite变高了,那么包含这个小窗口的整个行都变高了。这其中的含义是说如果任何在同一受影响的列或者行中的小窗口部件有具有填充排列(fill alignment)的属性,那么它也会伸展。具有开始(beginning),居中(center) 或结束(end)的排列属性不会伸展-他们会维持在变宽的这一列或者变宽的这一行的开始,中间或者结束处。
WidthHint和HeightHint
如果不和GridLayout的约束系统中的其他需求发生冲突, widthHint和 heightHint域指出了你要让一个小窗口部件具有的宽或者高的象素数。回到五个按钮,三列的例子,假如我们要Button 5有70个象素宽40个象素高。我们象下面这样编码:
GridData gridData = new GridData();
gridData.widthHint = 70;
gridData.heightHint = 40;
button5.setLayoutData(gridData);
Button 5在窗口中正常的大小如下左图所示,而70个象素宽40个象素高的Button 5如下右图所示:
注意到,虽然如此,如果Button 5的 horizontalAlignment是FILL,那么GridLayout就不能满足70个象素宽的需求。
使用宽度和高度hint的最后一个意见。在一个平台上看起来很好的事情不一定在另一个平台也看起来很好。跨平台的字体大小和正常小窗口部件大小间的变化意味着硬编码的象素值通常不是布局窗口的最好办法。因此,如果你要使用大小hint,也让它尽可能少地被使用。
horizontalAlignment = GridData.BEGINNING
(
默认
)
|
![]() |
horizontalAlignment = GridData.CENTER
|
![]() |
horizontalAlignment = GridData.END
|
![]() |
horizontalAlignment = GridData.FILL
|
![]() |
HorizontalIndent
horizontalIndent域允许你通过指定一个特定数目的象素数来把一个小窗口部件右移。这个域典型的只在 horizontalAlignment值为BEGINNING时有用。我们不能用一个“类型位”来设置缩进,所以我们象下面这样把我们的例子中的Button 5缩进4个象素:
![](http://www.amuhouse.com/link/attachments/month_0605/1200656234755.jpg)
GridData gridData = new GridData();
gridData.horizontalIndent = 4;
button5.setLayoutData(gridData);
HorizontalSpan and VerticalSpan
Span域让小窗口部件占有多于一个网格单元的空间。它们通常和FILL排列(aligment)一起使用。我们象下面这样让我们例子中的的Button 5跨越最后两个网格单元:
![](http://www.amuhouse.com/link/attachments/month_0605/m20065623496.jpg)
GridData gridData = new GridData();
gridData.horizontalAlignment = GridData.FILL;
gridData.horizontalSpan = 2;
button5.setLayoutData(gridData);
如果我们决定让我们的Wide Button 2跨越两个网格单元,我们可以以这样的方式结束:
![](http://www.amuhouse.com/link/attachments/month_0605/3200656234934.jpg)
GridData gridData = new GridData();
gridData.horizontalAlignment = GridData.FILL;
gridData.horizontalSpan = 2;
button2.setLayoutData(gridData);
或者我们可以让Button 3垂直地跨越两个网格单元:
![](http://www.amuhouse.com/link/attachments/month_0605/a20065623509.jpg)
GridData gridData = new GridData();
gridData.verticalAlignment = GridData.FILL;
gridData.verticalSpan = 2;
button3.setLayoutData(gridData);
GrabExcessHorizontalSpace和GrabExcessVerticalSpace
grabExcessHorizontalSpace和 grabExcessVerticalSpace域典型的被较大的小窗口部件如Text,List或者Canvas使用,以允许当它们包含的Composite变大时它们也相应的增大。如果一个Text抢占(grab)了额外的水平空间并用用户缩放了Shell的宽度,那么这个Text会占有所有这些新的水平空间而同一行中的其他的小窗口部件会维持原来的宽度。当然,当Shell缩小时抢占了额外空间的小窗口部件也是最先收缩的。在缩放的上下文中总是考虑 grabExcessSpace域是最容易的。作为一个简单的例子,我们重新使用前面Button 3垂直地跨越两个网格单元的例子。这是这个例子的又一次展现:
![](http://www.amuhouse.com/link/attachments/month_0605/f200656235218.jpg)
如果我们缩放这个窗口,唯一发生的事情只是窗口变大了。
![](http://www.amuhouse.com/link/attachments/month_0605/w200656235238.jpg)
现在,我们让Button 3抢占额外的水平和垂直的空间,并且让B1和B4垂直地填充(不使用抢占),然后我们再一次对窗口进行缩放:
![](http://www.amuhouse.com/link/attachments/month_0605/d200656235313.jpg)
这一次,Button 3再两个方向上都增大了,而B4垂直的增大。其他的按钮维持原来的大小。因为Button 3是垂直地抢占的并且它跨越了两行,它所跨越的最后一行变高了。注意到B1并没有增大-虽然它是垂直填充的-因为它的行并没有增大。因为Button 3是水平地抢占的,它的列变宽了,并且因为它是水平的填充的,它的宽度变大了以填充这一列。这里是所有五个按钮的代码:
Button button1 = new Button(shell, SWT.PUSH);
button1.setText("B1");
GridData gridData = new GridData();
gridData.verticalAlignment = GridData.FILL;
button1.setLayoutData(gridData);
new Button(shell, SWT.PUSH).setText("Wide Button 2");
Button button3 = new Button(shell, SWT.PUSH);
button3.setText("Button 3");
gridData = new GridData();
gridData.verticalAlignment = GridData.FILL;
gridData.verticalSpan = 2;
gridData.grabExcessVerticalSpace = true;
gridData.horizontalAlignment = GridData.FILL;
gridData.grabExcessHorizontalSpace = true;
button3.setLayoutData(gridData);
Button button4 = new Button(shell, SWT.PUSH);
button4.setText("B4");
gridData = new GridData();
gridData.verticalAlignment = GridData.FILL;
button4.setLayoutData(gridData);
new Button(shell, SWT.PUSH).setText("Button 5");
在一个典型的应用程序窗口中,你通常需要有至少一个抢占的小窗口部件。如果多于一个窗口试图抢占同样的空间,那么这些额外的空间会被均匀的分配到这些抢占式的小窗口部件中:
![](http://www.amuhouse.com/link/attachments/month_0605/b200656235548.gif)
![](http://www.amuhouse.com/link/attachments/month_0605/420065623563.gif)
最后一个关于抢占需要注意的地方。如果一个小窗口部件抢占了额外的水平空间并且它的父Composite变宽了,那么包含这个小窗口部件的整个列都变宽了。如果一个小窗口部件抢占了额外的垂直空间并且它的父Composite变高了,那么包含这个小窗口的整个行都变高了。这其中的含义是说如果任何在同一受影响的列或者行中的小窗口部件有具有填充排列(fill alignment)的属性,那么它也会伸展。具有开始(beginning),居中(center) 或结束(end)的排列属性不会伸展-他们会维持在变宽的这一列或者变宽的这一行的开始,中间或者结束处。
WidthHint和HeightHint
如果不和GridLayout的约束系统中的其他需求发生冲突, widthHint和 heightHint域指出了你要让一个小窗口部件具有的宽或者高的象素数。回到五个按钮,三列的例子,假如我们要Button 5有70个象素宽40个象素高。我们象下面这样编码:
GridData gridData = new GridData();
gridData.widthHint = 70;
gridData.heightHint = 40;
button5.setLayoutData(gridData);
Button 5在窗口中正常的大小如下左图所示,而70个象素宽40个象素高的Button 5如下右图所示:
![](http://www.amuhouse.com/link/attachments/month_0605/6200656235744.gif)
![](http://www.amuhouse.com/link/attachments/month_0605/n200656235753.gif)
注意到,虽然如此,如果Button 5的 horizontalAlignment是FILL,那么GridLayout就不能满足70个象素宽的需求。
使用宽度和高度hint的最后一个意见。在一个平台上看起来很好的事情不一定在另一个平台也看起来很好。跨平台的字体大小和正常小窗口部件大小间的变化意味着硬编码的象素值通常不是布局窗口的最好办法。因此,如果你要使用大小hint,也让它尽可能少地被使用。
一个复杂的GridLayout的例子
至到目前为止,为了展示各个域是如何工作的,GridLayout的例子都相当的简单。现在,我们把它们放在一起创建一个比较复杂的例子。我们由手画一个粗糙的我们将要创建的窗口的草图开始,用以决定例如网格将含有多少个列,一些小窗口部件是否需要被伸展等问题。
然后,我们开始从图中编写这个例子。代码如下。注意到我们增加了一些逻辑使得这段代码更有趣,例如,Browse…打开一个文件对话框(FileDialog)来读入一张图片,Canvas在一个重绘监听器中显示这张图片,Delete删除这张图片,Enter打印当前的狗以及它的主人的信息。这个例子在一个单独的 main函数中编码,这让我们集中于布局的代码编写而不用为编程风格而感到烦乱。
import org.eclipse.swt.*;
import org.eclipse.swt.widgets.*;
import org.eclipse.swt.layout.*;
import org.eclipse.swt.events.*;
import org.eclipse.swt.graphics.*;
public class ComplexGridLayoutExample {
static Display display;
static Shell shell;
static Text dogName;
static Combo dogBreed;
static Canvas dogPhoto;
static Image dogImage;
static List categories;
static Text ownerName;
static Text ownerPhone;
public static void main(String[] args) {
display = new Display();
shell = new Shell(display);
shell.setText("Dog Show Entry");
GridLayout gridLayout = new GridLayout();
gridLayout.numColumns = 3;
shell.setLayout(gridLayout);
new Label(shell, SWT.NONE).setText("Dog's Name:");
dogName = new Text(shell, SWT.SINGLE | SWT.BORDER);
GridData gridData = new GridData(GridData.HORIZONTAL_ALIGN_FILL);
gridData.horizontalSpan = 2;
dogName.setLayoutData(gridData);
new Label(shell, SWT.NONE).setText("Breed:");
dogBreed = new Combo(shell, SWT.NONE);
dogBreed.setItems(new String [] {"Collie", "Pitbull", "Poodle", "Scottie"});
dogBreed.setLayoutData(new GridData(GridData.HORIZONTAL_ALIGN_FILL));
Label label = new Label(shell, SWT.NONE);
label.setText("Categories");
label.setLayoutData(new GridData(GridData.HORIZONTAL_ALIGN_CENTER));
new Label(shell, SWT.NONE).setText("Photo:");
dogPhoto = new Canvas(shell, SWT.BORDER);
gridData = new GridData(GridData.FILL_BOTH);
gridData.widthHint = 80;
gridData.heightHint = 80;
gridData.verticalSpan = 3;
dogPhoto.setLayoutData(gridData);
dogPhoto.addPaintListener(new PaintListener() {
public void paintControl(final PaintEvent event) {
if (dogImage != null) {
event.gc.drawImage(dogImage, 0, 0);
}
}
});
categories = new List(shell, SWT.MULTI | SWT.BORDER | SWT.V_SCROLL);
categories.setItems(new String [] {
"Best of Breed", "Prettiest Female", "Handsomest Male",
"Best Dressed", "Fluffiest Ears", "Most Colors",
"Best Performer", "Loudest Bark", "Best Behaved",
"Prettiest Eyes", "Most Hair", "Longest Tail",
"Cutest Trick"});
gridData = new GridData(GridData.HORIZONTAL_ALIGN_FILL | GridData.VERTICAL_ALIGN_FILL);
gridData.verticalSpan = 4;
int listHeight = categories.getItemHeight() * 12;
Rectangle trim = categories.computeTrim(0, 0, 0, listHeight);
gridData.heightHint = trim.height;
categories.setLayoutData(gridData);
Button browse = new Button(shell, SWT.PUSH);
browse.setText("Browse...");
gridData = new GridData(GridData.HORIZONTAL_ALIGN_FILL);
gridData.horizontalIndent = 5;
browse.setLayoutData(gridData);
browse.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent event) {
String fileName = new FileDialog(shell).open();
if (fileName != null) {
dogImage = new Image(display, fileName);
}
}
});
Button delete = new Button(shell, SWT.PUSH);
delete.setText("Delete");
gridData = new GridData(GridData.HORIZONTAL_ALIGN_FILL | GridData.VERTICAL_ALIGN_BEGINNING);
gridData.horizontalIndent = 5;
delete.setLayoutData(gridData);
delete.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent event) {
if (dogImage != null) {
dogImage.dispose();
dogImage = null;
dogPhoto.redraw();
}
}
});
Group ownerInfo = new Group(shell, SWT.NONE);
ownerInfo.setText("Owner Info");
gridLayout = new GridLayout();
gridLayout.numColumns = 2;
ownerInfo.setLayout(gridLayout);
gridData = new GridData(GridData.HORIZONTAL_ALIGN_FILL);
gridData.horizontalSpan = 2;
ownerInfo.setLayoutData(gridData);
new Label(ownerInfo, SWT.NONE).setText("Name:");
ownerName = new Text(ownerInfo, SWT.SINGLE | SWT.BORDER);
ownerName.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
new Label(ownerInfo, SWT.NONE).setText("Phone:");
ownerPhone = new Text(ownerInfo, SWT.SINGLE | SWT.BORDER);
ownerPhone.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
Button enter = new Button(shell, SWT.PUSH);
enter.setText("Enter");
gridData = new GridData(GridData.HORIZONTAL_ALIGN_END);
gridData.horizontalSpan = 3;
enter.setLayoutData(gridData);
enter.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent event) {
System.out.println("\nDog Name: " + dogName.getText());
System.out.println("Dog Breed: " + dogBreed.getText());
System.out.println("Owner Name: " + ownerName.getText());
System.out.println("Owner Phone: " + ownerPhone.getText());
System.out.println("Categories:");
String cats[] = categories.getSelection();
for (int i = 0; i < cats.length; i++) {
System.out.println("\t" + cats[i]);
}
}
});
shell.pack();
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) display.sleep();
}
if (dogImage != null) {
dogImage.dispose();
}
}
}
至到目前为止,为了展示各个域是如何工作的,GridLayout的例子都相当的简单。现在,我们把它们放在一起创建一个比较复杂的例子。我们由手画一个粗糙的我们将要创建的窗口的草图开始,用以决定例如网格将含有多少个列,一些小窗口部件是否需要被伸展等问题。
![](http://www.amuhouse.com/link/attachments/month_0605/e20065703933.gif)
然后,我们开始从图中编写这个例子。代码如下。注意到我们增加了一些逻辑使得这段代码更有趣,例如,Browse…打开一个文件对话框(FileDialog)来读入一张图片,Canvas在一个重绘监听器中显示这张图片,Delete删除这张图片,Enter打印当前的狗以及它的主人的信息。这个例子在一个单独的 main函数中编码,这让我们集中于布局的代码编写而不用为编程风格而感到烦乱。
import org.eclipse.swt.*;
import org.eclipse.swt.widgets.*;
import org.eclipse.swt.layout.*;
import org.eclipse.swt.events.*;
import org.eclipse.swt.graphics.*;
public class ComplexGridLayoutExample {
static Display display;
static Shell shell;
static Text dogName;
static Combo dogBreed;
static Canvas dogPhoto;
static Image dogImage;
static List categories;
static Text ownerName;
static Text ownerPhone;
public static void main(String[] args) {
display = new Display();
shell = new Shell(display);
shell.setText("Dog Show Entry");
GridLayout gridLayout = new GridLayout();
gridLayout.numColumns = 3;
shell.setLayout(gridLayout);
new Label(shell, SWT.NONE).setText("Dog's Name:");
dogName = new Text(shell, SWT.SINGLE | SWT.BORDER);
GridData gridData = new GridData(GridData.HORIZONTAL_ALIGN_FILL);
gridData.horizontalSpan = 2;
dogName.setLayoutData(gridData);
new Label(shell, SWT.NONE).setText("Breed:");
dogBreed = new Combo(shell, SWT.NONE);
dogBreed.setItems(new String [] {"Collie", "Pitbull", "Poodle", "Scottie"});
dogBreed.setLayoutData(new GridData(GridData.HORIZONTAL_ALIGN_FILL));
Label label = new Label(shell, SWT.NONE);
label.setText("Categories");
label.setLayoutData(new GridData(GridData.HORIZONTAL_ALIGN_CENTER));
new Label(shell, SWT.NONE).setText("Photo:");
dogPhoto = new Canvas(shell, SWT.BORDER);
gridData = new GridData(GridData.FILL_BOTH);
gridData.widthHint = 80;
gridData.heightHint = 80;
gridData.verticalSpan = 3;
dogPhoto.setLayoutData(gridData);
dogPhoto.addPaintListener(new PaintListener() {
public void paintControl(final PaintEvent event) {
if (dogImage != null) {
event.gc.drawImage(dogImage, 0, 0);
}
}
});
categories = new List(shell, SWT.MULTI | SWT.BORDER | SWT.V_SCROLL);
categories.setItems(new String [] {
"Best of Breed", "Prettiest Female", "Handsomest Male",
"Best Dressed", "Fluffiest Ears", "Most Colors",
"Best Performer", "Loudest Bark", "Best Behaved",
"Prettiest Eyes", "Most Hair", "Longest Tail",
"Cutest Trick"});
gridData = new GridData(GridData.HORIZONTAL_ALIGN_FILL | GridData.VERTICAL_ALIGN_FILL);
gridData.verticalSpan = 4;
int listHeight = categories.getItemHeight() * 12;
Rectangle trim = categories.computeTrim(0, 0, 0, listHeight);
gridData.heightHint = trim.height;
categories.setLayoutData(gridData);
Button browse = new Button(shell, SWT.PUSH);
browse.setText("Browse...");
gridData = new GridData(GridData.HORIZONTAL_ALIGN_FILL);
gridData.horizontalIndent = 5;
browse.setLayoutData(gridData);
browse.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent event) {
String fileName = new FileDialog(shell).open();
if (fileName != null) {
dogImage = new Image(display, fileName);
}
}
});
Button delete = new Button(shell, SWT.PUSH);
delete.setText("Delete");
gridData = new GridData(GridData.HORIZONTAL_ALIGN_FILL | GridData.VERTICAL_ALIGN_BEGINNING);
gridData.horizontalIndent = 5;
delete.setLayoutData(gridData);
delete.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent event) {
if (dogImage != null) {
dogImage.dispose();
dogImage = null;
dogPhoto.redraw();
}
}
});
Group ownerInfo = new Group(shell, SWT.NONE);
ownerInfo.setText("Owner Info");
gridLayout = new GridLayout();
gridLayout.numColumns = 2;
ownerInfo.setLayout(gridLayout);
gridData = new GridData(GridData.HORIZONTAL_ALIGN_FILL);
gridData.horizontalSpan = 2;
ownerInfo.setLayoutData(gridData);
new Label(ownerInfo, SWT.NONE).setText("Name:");
ownerName = new Text(ownerInfo, SWT.SINGLE | SWT.BORDER);
ownerName.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
new Label(ownerInfo, SWT.NONE).setText("Phone:");
ownerPhone = new Text(ownerInfo, SWT.SINGLE | SWT.BORDER);
ownerPhone.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
Button enter = new Button(shell, SWT.PUSH);
enter.setText("Enter");
gridData = new GridData(GridData.HORIZONTAL_ALIGN_END);
gridData.horizontalSpan = 3;
enter.setLayoutData(gridData);
enter.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent event) {
System.out.println("\nDog Name: " + dogName.getText());
System.out.println("Dog Breed: " + dogBreed.getText());
System.out.println("Owner Name: " + ownerName.getText());
System.out.println("Owner Phone: " + ownerPhone.getText());
System.out.println("Categories:");
String cats[] = categories.getSelection();
for (int i = 0; i < cats.length; i++) {
System.out.println("\t" + cats[i]);
}
}
});
shell.pack();
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) display.sleep();
}
if (dogImage != null) {
dogImage.dispose();
}
}
}
这是当Mary Smith输入Fifi后窗口看起来的样子:
如果窗口被放大,布局调整如下所示:
注意下面这些问题:
• 这里有3列7行。
• dogPhoto这个Canvas会变宽和变高同,这是因为它是填充的并且水平地和垂直的抢占的(虽然我们本应该缩放图片,但我们没有对图片进行缩放)。
• dogBreed这个Combo会变宽,因为它是水平的填充的,并且它和Canvans在同一列。
• dogName这个Text会变宽,那是因为它是水平填充的,并且它所跨越的其中一列包含有Canvas。
• Categories这个List会变高,这是因为它是垂直填充的,并且它和Canvas跨越了相同的行。
• 因为 categories这个List变高了,它的垂直滚动条消失了(它并没有变宽)。
• ownerInfo这个Group会变宽,这是因为它是水平的填充的,并且它所跨越的其中一列包含有Canvas。
• 作为Composite的子类, ownerInfo这个Group有它自己的2列2行的GridLayout。
• ownerName和 ownerPhone这两个Text会变宽,这是因为Group会变宽,并且它们在Group的GridLayout中是填充的和水平抢占的。
• browse和 delete这两个Button会稍微的缩进,而且因为它们都是水平填充的,它们有相同的宽度。
• delete这个Button在它的行的顶部垂直的对齐。
• “Categories”这个Label在 categories这个List的顶端居中。
• enter这个Button是水平地对齐到它所跨越的3列的最右边。
• dogPhoto这个Canvas用宽度和高度hint来创建,这是因为如果可能的话,我们要让Image有80象素×80象素的大小。
• categories这个List用List的字体的12倍的高度hint值来创建,这是因为我们要让List在初始状态下显示十二个项。
![](http://www.amuhouse.com/link/attachments/month_0605/320065705230.gif)
如果窗口被放大,布局调整如下所示:
![](http://www.amuhouse.com/link/attachments/month_0605/v20065705251.gif)
注意下面这些问题:
• 这里有3列7行。
• dogPhoto这个Canvas会变宽和变高同,这是因为它是填充的并且水平地和垂直的抢占的(虽然我们本应该缩放图片,但我们没有对图片进行缩放)。
• dogBreed这个Combo会变宽,因为它是水平的填充的,并且它和Canvans在同一列。
• dogName这个Text会变宽,那是因为它是水平填充的,并且它所跨越的其中一列包含有Canvas。
• Categories这个List会变高,这是因为它是垂直填充的,并且它和Canvas跨越了相同的行。
• 因为 categories这个List变高了,它的垂直滚动条消失了(它并没有变宽)。
• ownerInfo这个Group会变宽,这是因为它是水平的填充的,并且它所跨越的其中一列包含有Canvas。
• 作为Composite的子类, ownerInfo这个Group有它自己的2列2行的GridLayout。
• ownerName和 ownerPhone这两个Text会变宽,这是因为Group会变宽,并且它们在Group的GridLayout中是填充的和水平抢占的。
• browse和 delete这两个Button会稍微的缩进,而且因为它们都是水平填充的,它们有相同的宽度。
• delete这个Button在它的行的顶部垂直的对齐。
• “Categories”这个Label在 categories这个List的顶端居中。
• enter这个Button是水平地对齐到它所跨越的3列的最右边。
• dogPhoto这个Canvas用宽度和高度hint来创建,这是因为如果可能的话,我们要让Image有80象素×80象素的大小。
• categories这个List用List的字体的12倍的高度hint值来创建,这是因为我们要让List在初始状态下显示十二个项。