理解SWT布局(第一部分)

目录:

Part1:学习理解FillLayout,RowLayout与GridLayout
Part2:学习理解FormLayout与布局的基础知识

概述:

当你用标准小窗口工具箱(SWT)编写应用程序的时候,你可能需要用布局来为你的窗口定制特定的外观。布局控制组合窗口组件(composite)中的子组件的位置和大小。
布局类都是抽象类Layout的子类。SWT提供了很多标准的布局类,你可以编写定制的布局类。
在SWT中,定位和尺寸缩放不能自动地发生。应用程序可以初始化地确定一个Composite的子组件的大小和位置,或者可以通过一个调整大小的监听器来实现。另一个选择是指定一个布局类来定位和缩放这些子组件。如果子组件没有给定的尺寸大小,它们将会具有零尺寸(zero size)并且是不可见的。

下面的图中显示了一些讨论布局时通常使用的术语。Composite(在这个例子中,是一个TabFolder)有一个location,一个clientArea和一个trim。Composite的大小是clientArea的大小加上trim的大小。这个Composite有两个并排布置的子组件。布局(Layout)管理子组件的大小和位置。这个布局允许子组件之间的spacing,以及子组件和布局边缘之间的margin。布局的大小和Composite的clientArea的大小一样。

小窗口部件(widget)的首选大小是显示它的内容所需要的最小的大小。在这个composite的例子中,首选大小是包含所有它的子组件的最小的矩形。如果子组件已经被应用程序定位,composite会基于这些子组件的大小和位置计算它自己的首选大小。如果composite用布局类来定位它的子组件,它要求布局来计算clientArea的大小,然后加上trim来决定它的首选大小。

标准布局:
在SWT库中的标准布局类是:

1、 FillLayout – 在单行或者单列中放置相同大小的Widget。
2、 RowLayout – 在一行或者多行中放置Widget,应用了fill,wrap和spacing等选项。
3、 GridLayout – 在网格中放置Widget
4、 FormLayout 2.0新特性 – 通过定义四边的“粘贴”位置来放置Widget。

为了使用这些标准布局,你必须导入SWT的布局包:
import org.eclipse.swt.layout.;*
布局是可插件化的。为了设置composite小窗口部件的布局,你可以使用composite小窗口部件的setLayout(Layout)方法。在接下来的代码中,一个Shell(Composite的子类)用RowLayout来定位它的子组件:

Shell shell = new Shell();     
shell.setLayout(new RowLayout());

一个布局类可能有一个对应的布局数据(layout data)类 – 包括了定制子组件的布局数据的Object的子类。习惯上,布局数据类通常取 Layout名+Data 为标识。例如:标准布局类RowLayout具有布局数据类RowData。布局类GridLayout使用布局数据类GridData,而布局类FormLayout具有名称为FormData的布局数据类。一个Widget的布局数据类的设置如下:

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();            
  }      
}

}
运行以上代码得到如下的结果:

如果用户调整了shell的大小使得右边的Button 3没有足够的空间,RowLayout将Button 3换行放置到下一行,如下所示:

我们将会看到,使用布局和调整大小有紧密的关系。因此,这个文档中大多数的例子展示了当Composite变大或者变小时将会发生什么,以举例说明布局是如何工作的。

**FillLayout
**
FillLayout是最简单的布局类。它在单行或者单列中放置Widget,强制它们为同一大小。开始,所有的Widget都和最高的Widget一样高,和最宽的Widget一样宽。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.type = SWT.HORIZONTAL(默认)
fillLayout.type = SWT.VERTICAL
RowLayout

RowLayout比FillLayout的使用更为普遍,因为它能够换行(warp)和提供了可配置的空白(margin)和间隔(spacing)。RowLayout有很多可配置的域。而且,RowLayout中的每个Widget的高度和宽度都可以通过使用setLayoutData设置Widget的RowData对象来指定。

RowLayout的可配置域
Type-2.0新特性
  type域控制RowLayout在水平行中还是在垂直列中放置Widget。默认情况下RowLayout是水平方式的。
Wrap **
  
warp域控制RowLayout当前行中没有足够的空间时是否把Widget换行放放置到下一行。默认情况下RowLayout是换行的方式.
pack
  如果
pack为true,RowLayout中的Widget会采用它们正常的大小,而且它们会尽可能的左对齐。如果pack为false,Widget会填充可用的空间,就象FillLayout中的Widget一样。默认情况下RowLayout的pack为false。
justify
  如果
justify域为true,RowLayout中的Widget从左到右在可用的空间中伸展开。如果父Composite变大,额外的空间均匀的分布在Widget之中。如果packjustify都为true,Widget采用它们正常的大小,而且额外的空间被放置在这些Widget之间以保持它们完全的散开(justify)。默认情况下RowLayout不进行散开。
MarginLeft, MarginTop, MarginRight, MarginBottom和Spacing **
  这些域控制Widget之间的象素数(
spacing
)以及Widget和父Composite的边界之间的象素数(margin)。默认情况下RowLayout为空白(margin)和间隔(spacing)保留3个象素。空白和间隔在下面的图中展示:

** 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);

如果你仅使用默认的域值,你只需要一行代码:

 shell.setLayout(new RowLayout());  

在下面的表格中,展示了设置指定的域的结果:

初始状态 缩放后
wrap = true
pack = true
justify = false
type = SWT.HORIZONTAL (默认)

wrap = false(没有足够的空间则进行修剪)
pack = false(所有的Widget都有相同的大小)
justify = true(Widget在可用空间中伸展开来)
type = SWT.VERTICAL(Widget被垂直地安排在一列中)

和****RowLayout****一起使用****RowData****对象
每由RowLaout控制的一个Widget都能通过设置它的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();  
      }     
    }   
  }
}

这是你运行这些代码后看到的结果。

GridLayout

GridLayout可能是最有用最强大的标准布局,但它也是最为复杂的。在一个GridLayout中,一个Composite的Widget子组件被放置在网格中。GridLayout有很多可配置的域,而且,和RowLayout一样,它所放置的Widget可以有一个关联的布局数据对象,叫做GridData。GridLayout的强大在于它具有为GridLayout所控制的每一个Widget配置GirdData的能力。

GridLayout的可配置域
NumColumns
numColumns域是GridLayout中最重要的域,而且它通常也是应用程序会设定的第一个域。Widget被从左到右放置在不同的列中,当有numColumns + 1个Widget被加到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列,这就是我们将得到的(注意:在没有进一步说明的情况下,Widget在列中都是左对齐的)。

MarginWidth, MarginHeight, HorizontalSpacing, 和VerticalSpacing
GridLayout中的marginspacing域和RowLayout中的这些域是相同的。不同的地方在于左边和右边的空白(margin)被组合成marginWidth,而项部和底部的空白被组合成marginHeight。而且,在GridLayout中,你可以独立地指定horizontalSpacingverticalSpacing,然而在RowLayout中,spacing应用于水平或者垂直样式是取决于RowLayout的类型的。

GridData 对象域
GridData是关联于GridLayout的布局数据对象。为了设置一个Widget的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管理的Widget必须有一个唯一的GridData对象。如果在布局的时候一个GridLayout中的Widget的布局数据为null,就会为它创建一个唯一的GridData对象。

HorizontalAlignment和VerticalAlignment
Alignment域用于指定在一个网格单元中的哪个位置水平的和/或者垂直的放置一个Widget。每一个alignment域可以有下列的其中的一个值:
• BEGINNING
• CENTER
• END
• FILL
默认的horizontalAlignment值是BEGINNING(或者左对齐)。默认的verticalAlignment值是CENTER。
让我们回到我们那个在三个列中有五个按钮的例子,我们会改变Button 5的horizontalAlignment值。

horizontalAlignment = GridData.BEGINNING(默认)
horizontalAlignment = GridData.CENTER
horizontalAlignment = GridData.END
horizontalAlignment = GridData.FILL

HorizontalIndent
horizontalIndent域允许你通过指定一个特定数目的象素数来把一个Widget右移。这个域典型的只在horizontalAlignment值为BEGINNING时有用。我们不能用一个“类型位”来设置缩进,所以我们象下面这样把我们的例子中的Button 5缩进4个象素:

GridData gridData = new GridData();
gridData.horizontalIndent = 4;
button5.setLayoutData(gridData); 

HorizontalSpan and VerticalSpan
Span域让Widget占有多于一个网格单元的空间。它们通常和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
grabExcessHorizontalSpacegrabExcessVerticalSpace域典型的被较大的Widget如Text,List或者Canvas使用,以允许当它们包含的Composite变大时它们也相应的增大。如果一个Text抢占(grab)了额外的水平空间并用用户缩放了Shell的宽度,那么这个Text会占有所有这些新的水平空间而同一行中的其他的Widget会维持原来的宽度。当然,当Shell缩小时抢占了额外空间的Widget也是最先收缩的。在缩放的上下文中总是考虑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");   

在一个典型的应用程序窗口中,你通常需要有至少一个抢占的Widget。如果多于一个窗口试图抢占同样的空间,那么这些额外的空间会被均匀的分配到这些抢占式的Widget中:

最后一个关于抢占需要注意的地方。如果一个Widget抢占了额外的水平空间并且它的父Composite变宽了,那么包含这个Widget的整个列都变宽了。如果一个Widget抢占了额外的垂直空间并且它的父Composite变高了,那么包含这个小窗口的整个行都变高了。这其中的含义是说如果任何在同一受影响的列或者行中的Widget有具有填充排列(fill alignment)的属性,那么它也会伸展。具有开始(beginning),居中(center) 或结束(end)的排列属性不会伸展-他们会维持在变宽的这一列或者变宽的这一行的开始,中间或者结束处。

WidthHint和HeightHint
如果不和GridLayout的约束系统中的其他需求发生冲突,widthHintheightHint域指出了你要让一个Widget具有的宽或者高的象素数。回到五个按钮,三列的例子,假如我们要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的最后一个意见。在一个平台上看起来很好的事情不一定在另一个平台也看起来很好。跨平台的字体大小和正常Widget大小间的变化意味着硬编码的象素值通常不是布局窗口的最好办法。因此,如果你要使用大小hint,也让它尽可能少地被使用。

一个复杂的GridLayout的例子
至到目前为止,为了展示各个域是如何工作的,GridLayout的例子都相当的简单。现在,我们把它们放在一起创建一个比较复杂的例子。我们由手画一个粗糙的我们将要创建的窗口的草图开始,用以决定例如网格将含有多少个列,一些Widget是否需要被伸展等问题。

Paste_Image.png

然后,我们开始从图中编写这个例子。代码如下。注意到我们增加了一些逻辑使得这段代码更有趣,例如,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输入Bifford后窗口看起来的样子:


如果窗口被放大,布局调整如下所示:

注意下面这些问题:
• 这里有3列7行。
dogPhoto这个Canvas会变宽和变高同,这是因为它是填充的并且水平地和垂直的抢占的(虽然我们本应该缩放图片,但我们没有对图片进行缩放)。
dogBreed这个Combo会变宽,因为它是水平的填充的,并且它和Canvans在同一列。
dogName这个Text会变宽,那是因为它是水平填充的,并且它所跨越的其中一列包含有Canvas。
Categories这个List会变高,这是因为它是垂直填充的,并且它和Canvas跨越了相同的行。
• 因为categories这个List变高了,它的垂直滚动条消失了(它并没有变宽)。
ownerInfo这个Group会变宽,这是因为它是水平的填充的,并且它所跨越的其中一列包含有Canvas。
• 作为Composite的子类,ownerInfo这个Group有它自己的2列2行的GridLayout。
ownerNameownerPhone这两个Text会变宽,这是因为Group会变宽,并且它们在Group的GridLayout中是填充的和水平抢占的。 • browsedelete这两个Button会稍微的缩进,而且因为它们都是水平填充的,它们有相同的宽度。
delete这个Button在它的行的顶部垂直的对齐。
• “Categories”这个Label在categories这个List的顶端居中。
enter这个Button是水平地对齐到它所跨越的3列的最右边。 • dogPhoto这个Canvas用宽度和高度hint来创建,这是因为如果可能的话,我们要让Image有80象素×80象素的大小。
categories这个List用List的字体的12倍的高度hint值来创建,这是因为我们要让List在初始状态下显示十二个项。

第二部分我们将学习理解FormLayout 与布局的相关基础性的问题。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,736评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,167评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,442评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,902评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,302评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,573评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,847评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,562评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,260评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,531评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,021评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,367评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,016评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,068评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,827评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,610评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,514评论 2 269

推荐阅读更多精彩内容