相關鏈接:
http://www.csdn.net/develop/article/28/28479.shtm
http://www.chinaitpower.com/A200507/2005-07-24/165936.html
當你用SWT編寫應用程序的時候,你可能需要用佈局(layout)來給你的窗口 設置特特定的外觀。佈局控制組合窗口組件(composite)中的子組件的位置和大小。佈局類都是抽象類Layout的子類。這篇文章爲你展示瞭如何使 用標準佈局,以及如何編寫定製你自己的佈局。
作者:Carolyn MacLeod, OTI
2001年3月22日
2002年5月2日
當你用標準小窗口工具箱(SWT)編寫應用程序的時候,你可能需要用佈局來爲你的窗口定製特定的外觀。佈局控制組合窗口組件(composite)中的子 組件的位置和大小。 佈局類都是抽象類Layout的子類。SWT提供了很多標準的佈局類,你可以編寫定製的佈局類。
在SWT中,定位和尺寸縮放不能自動地發生。應用程序可以初始化地確定一個Composite的子組件的大小和位置,或者可以通過一個調整大小的監聽器來 實現。另一個選擇是指定一個佈局類來定位和縮放這些子組件。如果子組件沒有給定的尺寸大小,它們將會具有零尺寸(zero size)並且是不可見的。
在SWT庫中的標準佈局類是:
· FillLayout – 在單行或者單列中放置相同大小的小窗口部件。
· RowLayout – 在一行或者多行中放置小窗口部件,應用了fill,wrap和spacing等選項。
· GridLayout – 在網格中放置小窗口部件。
· FormLayout * 2.0新特性*– 通過定義四邊的“粘貼”位置來放置小窗口部件。
shell.setLayout(new RowLayout());
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();
}
}
}
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.type = SWT.HORIZONTAL
(默認)
|
|
|
fillLayout.type = SWT.VERTICAL
|
|
|
Type * 2.0新特性*
type域控制RowLayout在水平行中還是在垂直列中放置小窗口部件。默認情況下RowLayout是水平方式的。
Wrap
warp域控制RowLayout當前行中沒有足夠的空間時是否把小窗口部件換行放放置到下一行。默認情況下RowLayout是換行的方式.
如果pack爲true,RowLayout中的小窗口部件會採用它們正常的大小,而且它們會儘可能的左對齊。如果pack爲false,小窗口部件會填充可用的空間,就象FillLayout中的小窗口部件一樣。默認情況下RowLayout的pack爲false。
如果justify域爲true,RowLayout中的小窗口部件從左到右在可用的空間中伸展開。如果父Composite變大,額外的空間均勻的分佈在小窗口部件之中。如果pack和justify都爲true,窗口小部件採用它們正常的大小,而且額外的空間被放置在這些小窗口部件之間以保持它們完全的散開(justify)。默認情況下RowLayout不進行散開。
這些域控制小窗口部件之間的象素數(spacing)以及小窗口部件和父Composite的邊界之間的象素數(margin)。默認情況下RowLayout爲空白(margin)和間隔(spacing)保留3個象素。空白和間隔在下面的圖中展示:
RowLayout的例子
下面的示例代碼中創建了一個RowLayout,設置它的所有域爲非默認的值,然後把它設定到一個Shell中。
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);
|
初始狀態
|
縮放後
|
wrap = true
pack = true
justify = false
type = SWT.HORIZONTAL
(默認)
|
|
|
wrap = false
(沒有足夠的空間則進行修剪)
|
|
|
pack = false
(所有的小窗口部件都有相同的大小)
|
|
|
justify = true
(小窗口部件在可用空間中伸展開來)
|
|
|
type = SWT.VERTICAL
(窗口小部件被垂直地安排在一列中)
|
|
|
和RowLayout一起使用RowData對象
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();
}
}
}
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列,這就是我們將得到的(注意:在沒有進一步說明的情況下,小窗口部件在列中都是左對齊的)。
MarginWidth, MarginHeight, HorizontalSpacing, 和VerticalSpacing
GridLayout中的margin和spacing域和RowLayout中的這些域是相同的。不同的地方在於左邊和右邊的空白(margin)被組合成marginWidth,而項部和底部的空白被組合成marginHeight。而且,在GridLayout中,你可以獨立地指定horizontalSpacing和verticalSpacing,然而在RowLayout中,spacing應用於水平或者垂直樣式是取決於RowLayout的類型的。
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));
事實上,爲了進一步的方便,還提供了一些常用的“類型位”的聯合。
注意到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。
horizontalAlignment = GridData.BEGINNING
(默認)
|
|
horizontalAlignment = GridData.CENTER
|
|
horizontalAlignment = GridData.END
|
|
horizontalAlignment = GridData.FILL
|
|
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,也讓它儘可能少地被使用。
至到目前爲止,爲了展示各個域是如何工作的,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();
}
}
}
如果窗口被放大,佈局調整如下所示:
注意下面這些問題:
• 這裏有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在初始狀態下顯示十二個項。