FormLayout通過為小窗口部件創(chuàng)建四邊的Form附加值(attachment)來進行工作,并且把這些Form附加值存儲在布局數(shù)據(jù)中。一個附加值讓一個小窗口部件指定的一邊粘貼(attach)到父Composite的一個位置或者這個布局中的另一個小窗口部件上。這為布局提供了極大的便利,并且這也允許你指定布局中單個小窗口部件的放置。
FormLayout的可配置域
MarginWidth, MarginHeight
FormLayout中的margin域類似于GridLayout中的相同域。左邊和右邊邊距(margin)被定義成marginWidth,而頂部和底部的邊距被定義成marginHeight。邊距也能根據(jù)每個小窗口部件的附加值來定義。FormLayout邊距在默認情況下為0。
為了設置這些邊距,我們創(chuàng)建一個FromLayout,然后設置它的邊距域。下面這段代碼會在父Composite的四個邊都設置5個象素的邊距。
Display display = new Display ();
Shell shell = new Shell (display);
FormLayout layout= new FormLayout ();
layout.marginHeight = 5;
layout.marginWidth = 5;
shell.setLayout (layout);
FormData對象用于指定FormLayout中的每一個小窗口部件怎么樣放置。每一個FormData對象定義了小窗口部件四個邊的附加值值。這些附加值決定在哪里定位小窗口部件的四個邊。你可以用setLayoutData方法來設定一個小窗口部件的FormData對象。例如:
Button button1 = new Button(shell, SWT.PUSH);
button1.setText("B1");
button1.setLayoutData(new FormData());
但是,這段代碼創(chuàng)建了一個沒有附加值的FormData對象。在這個例子中,定義了默認的附加值值,這沒有體現(xiàn)出FormLayout的目的和效用。默認的附加值值讓小窗口部件粘貼到父Composite的左邊和頂部。如果FormLayout中的每一個小窗口部件都使用了默認的附加值值,它們就會全部被重疊的放置在父Composite的左上角。
Left, Right, Top和Bottom
FormLayout中的left, right, top和bottom域分別指定了與小窗口部件的左邊,右邊,頂部和底部相關聯(lián)的Form附加值對象。這些域被象下面的例中一樣設定:
FormData formData = new FormData();
formData.top = new FormAttachment (0,60);
formData.bottom = new FormAttachment (100,-5);
formData.left = new FormAttachment (20,0);
formData.right = new FormAttachment (100,-3);
button1.setLayoutData(formData);
一個Form附加值對象定義了一個小窗口部件指定的邊的附加值。有很多方法能定義小窗口部件的邊的粘貼:相對于父Composite的一個位置,相對于Composite的一個邊,相對于另一個小窗口部件的相鄰邊,相對于另一個小窗口部件的相對邊,或者和另一個小窗口部件居中。相對于父Composite的一個位置粘貼放置了小窗口部件的邊,因此通常用相對于父Composite的百分比來表示。如果貼粘到父Composite的邊緣,這個百分比就是0%或者100%。
相對于另一個小窗口部件的相鄰邊粘貼保證了小窗口部件的指定邊總是與另一個小窗口部件的最近邊相鄰。相對于另一個小窗口部件的相對邊粘貼保證了小窗口部件的指定邊總是與另一個小窗口部件的最遠邊對齊。最后,相對于另一個小窗口部件居中讓小窗口部件在另一個小窗口部件中居中。這些方法都可以加或者不加偏移值來實現(xiàn)。

button1.setLayoutData(new FormData());
注意到如果多于一個小窗口部件被定義為沒有任何附加值的時候,這些小窗口部件都會被放置到窗口中相同的默認位置上,并且會發(fā)生重疊。Form附加值在Form附加值對象這一節(jié)中將進行更仔細的敘述。
Width和Height
FormData中的width和height域用來指定小窗口部件所需的寬度和高度。如果所請求的寬度和高度與附加值設置的約束發(fā)生沖突,那么這些請求的高度和寬度就不能遵從了。雖然通過設置附加值也以決定寬度和高度值,但有些情況下你不希望為小窗口部件的所有邊都定義附加值值。在這種情況下,它們就可以很有用的象下面這樣設定小窗口部件的寬度和高度值:
FormData formData = new FormData(20,30);
formData.top = new FormAttachment (0,60);
formData.left = new FormAttachment (20,0);
button1.setLayoutData(formData);
如果你希望只設定寬度和高度值,你可以直接設置FromData對象的寬度和高度值:
FormData formData = new FormData ();
formData.width = 30;
formData.top = new FormAttachment (0,60);
formData.bottom = new FormAttachment (100,-5);
formData.left = new FormAttachment (20,0);
button1.setLayoutData(formData);
注意,如果一個按鈕粘貼到父Composite的兩邊,當這父Composite進行縮放時,這個按鈕會隨著父Composite增大和縮小。
FormAttachment對象
From附加值是一個用來定義小窗口部件的指定邊的附加值的對象。不需要總是為小窗口部件的四個邊都設置附加值。通常只指定一個或者多個小窗口部件的邊就能完全指定它的放置。為了更適當?shù)姆胖媚愕男〈翱诓考?,你必須至少為FormData中left或者right之一,以及top或者bottom之一定義附加值。如果你只希望粘貼小窗口部件的左邊而不是右邊,那么這個小窗口部件將用它的邊來進行定位,并且這個小窗口部件會采用它的正常大?。ɑ蛘弋斣O置了請求大小時,采用它的請求大?。?。如果你沒有貼粘左邊或者右邊,默認的定義會把你的小窗口部件粘貼到窗體的左邊。對于頂部和底部也是采用了相同的邏輯。
相對一個位置進行粘貼
存在很多種類型的附加值。第一種就是粘貼到相對于父Composite的一個位置。通過定義一個100以內(nèi)的百分值就能實現(xiàn),例如:

FormData formData = new FormData();
formData.top = new FormAttachment (50,0);
button1.setLayoutData(formData);
這里設置Button的頂部到相對于父Composite(一個Shell)高度的50%的位置,偏移量為0。當這個Shell進行縮放的時候,Button的頂部會始終在50%的位置,像這樣:

如果我們選擇設定一個偏移值,Button的上部就會被設置為父Composite的50%加上或者減去為偏移量設置的象素數(shù)。
我們也能定義不使用百分比定義按鈕的位置,例如:
formData.top = new FormAttachment (30,70,10);
button1.setLayoutData(formData);
如果父Composite的高度被定義為70個單位,這里設置了Button的頂部在父Composite頂部以下30個單位,再加上10個象素的地方。
相對于父Composite進行粘貼
第二種類型的附加值是相對于父Composite的邊緣進行粘貼。這與相對于一個位置進行粘貼的實現(xiàn)方法差不多,但是這個位置值只能是0%或者100%。當處于垂直樣式的時候,0這個位置被定義為父Composite的頂部,但處于水平樣式的時候被定義為左邊。右邊和底部邊緣被定義為100。因此,如果我們要粘貼一個小窗口部件到父Composite的右邊,我們只需要簡單的創(chuàng)建一個附加值,然后設置它的位置值為100:

FormData formData = new FormData();
formData.right = new FormAttachment (100,-5);
button1.setLayoutData(formData);
這里把Button的右邊粘貼到了父Composite(一個Shell)的右邊緣,并有一個5個象素的偏移值。注意,附加值是往一個方向增加的。如果你要讓一個小窗口部件向下或者所右偏移,偏移值必須是正的。如果要小窗口部件向上或者向左偏移,這個偏移值必須是負的?,F(xiàn)在當這個Shell被縮放時,這個Button就總是偏離右邊緣5個象素:

相對于另一個小窗口部件進行粘貼
第三種類型的附加值是相對于父Composite中的另一個控件進行粘貼。小窗口部件的邊可以被粘貼到另一個控件的相鄰邊(默認),粘貼到另一個控件的相對邊,或者小窗口部件可以相對于另一個控件居中,這些都認使用或者不使用偏移值。
相對于另一個控件進行粘貼最常用的方法是粘貼到它的相鄰邊。例如下面的代碼:
FormData formData = new FormData();
formData.top = new FormAttachment (20,0);
button1.setLayoutData(formData);
FormData formData2 = new FormData();
formData2.top = new FormAttachment (button1,10);
button2.setLayoutData(formData2);
把按鈕2的頂部粘貼到了按鈕1的底部,這是因為按鈕1的底部和按鈕2的頂部是相鄰的。

注意到當窗口被縮放時,按鈕1會移動讓它的頂部總是定位在Shell的20%的位置,而按鈕2也是會移動讓它的頂部總是在按鈕1的相鄰邊(底部)以下10個象素的位置。

雖然默認的情況下是相對于一個控件的相鄰邊進行粘貼,F(xiàn)romAttachment也能被創(chuàng)建用來相對一個控件的相對邊進行粘貼。當要排列小窗口部件的時候這就非常的有用了。在這種情況下,你可以用TOP, BOTTOM, LEFT或者RIGHT排列(alignment)創(chuàng)建相對于另一個控件的附加值,例如:
formData2.top = new FormAttachment (button1,0,SWT.TOP);
在接下來的例子中,按鈕1的頂部邊被定位在Shell的20%的地方。按鈕2使用了TOP排列,它的頂部邊和按鈕1的頂部邊對齊。這意味著按鈕2的頂部也被定位在這個Shell的20%的位置。注意到當指定了頂部附加值,只有小窗口部件的垂直放置會被定義。仍然需要為按鈕2設置左邊附加值以得Button不會發(fā)生重疊。
FormData formData = new FormData(50,50);
formData.top = new FormAttachment (20,0);
button1.setLayoutData(formData);
FormData formData2 = new FormData();
FormData2.left = new FormAttachment (button1,5);
formData2.top = new FormAttachment (button1,0,SWT.TOP);
button2.setLayoutData(formData2);
結果如下:

最后一個相對于另一個控件進行粘貼的方法是相對于另一個控件居中。當兩個控件有不同的大小的時候,這就顯得很有用了。在這種情況下,你用CENTER排列(aligmnet)創(chuàng)建相對于另一個控件的附加值,例如:
formData.top = new FormAttachment (button1,0,SWT.CENTER);
這會放置這個小窗口部件的頂部在一個允許這個小窗口部件相對于另一個控件居中的位置上,并且有一個值為0的偏移值。只設定頂部,或者底部,或者它們兩者為居中附加值會導致相同的結果。這個小窗口部件的頂部邊不會居中,但是整個小窗口部件會居中,所以這只需要指定一次。這里有一個例子:

FormData formData1 = new FormData (50,50);
button1.setLayoutData(formData1)
FormData formData2 = new FormData ();
formData2.left = new FormAttachment (button1,5);
formData2.top = new FormAttachment (button1,0,SWT.CENTER);
button2.setLayoutData (formData2);
使用不同類型的附加值允許布局以不同的方法來定義。FormLayout解決了一些FillLayout,RowLayout或者GridLayout不能解決的問題,讓它成為定義布局很有用的類。
重要的:不要定義循環(huán)的附加值。例如,不要把按鈕1的右邊粘貼到按鈕2的左邊,然后又把按鈕2的左邊粘貼到按鈕1的右邊。這會過度約束布局,導致不可預知的行為。雖然運算會被中斷,但結果是不可預知的。因此,確實保證你沒有過度約束你的小窗口部件。僅提供所需要的附加值來適當?shù)姆胖玫男〈翱诓考?/div>
到目前為止,所有使用FormLayout的例子都只是圍繞著一兩個Button,為了展示FormAttachment是如何工作 的。下面,我們會舉一個有更多的Button的例子來展示使用附加值如何安排布局。我們會從畫一張描繪我們要創(chuàng)建的附加值的基本草圖開始。

FormData data1 = new FormData();
data1.left = new FormAttachment (0,5);
data1.right = new FormAttachment (25,0);
button1.setLayoutData(data1);
FormData data2 = new FormData();
data2.left = new FormAttachment (button1,5);
data2.right = new FormAttachment (100,-5);
button2.setLayoutData(data2);
FormData data3 = new FormData(60,60);
data3.top = new FormAttachment (button1,5);
data3.left = new FormAttachment (50,-30);
data3.right = new FormAttachment (50,30);
button3.setLayoutData(data3);
FormData data4 = new FormData();
data4.top = new FormAttachment (button3,5);
data4.bottom = new FormAttachment (100,-5);
data4.left = new FormAttachment (25,0);
button4.setLayoutData(data4);
FormData data5 = new FormData();
data5.bottom = new FormAttachment (100,-5);
data5.left = new FormAttachment (button4,5);
button5.setLayoutData(data5);
在這個例子中,因為沒有為按鈕1和按鈕2定義頂部附加值,它們粘貼到了布局的頂部上。鈕3在左邊和右邊使用了百分比和偏移值而在布局中居中。按鈕4和按鈕5粘貼到了布局的底部,并有5個象素的偏移量。

當我們進行縮放時,附加值就更顯得明顯了。按鈕1的左邊和右邊都相對進行粘貼,所以當窗口被縮放時它也增大了。注意到它的右邊總是在窗口的25%的位置。相同的縮放效果也被應用到按鈕2上,因為它的兩邊也都相對進行了粘貼。左邊相對按鈕1進行了粘貼,所以它總是會在窗口的25%加上5個象素的位置。按鈕3水平地保持在窗口的中間。按鈕4的頂部和底部都相對進行粘貼,因此當窗口被縮放的時候它垂直地增大了,但是它只相對于左邊進行了粘貼而對右邊則沒有,因此水平方向上它沒有增大。按鈕5不會增大或者縮小,但它的左邊會保持在離按鈕4有5個象素遠,離窗口底部5個象素遠的地方。

一個復雜的FormLayout的例子
為了舉例說明FormLayout如何能夠用于更復雜的布局,我們用FormLayout來重做上面為GridLayout舉例的“狗狗展示條目”的例子。這段代碼創(chuàng)建一個一樣的布局,但是用不同的觀念來實現(xiàn)它。
我們會從修改為網(wǎng)格布局所作的設計草圖開始。我們會畫出如何做,而不僅僅畫出我們希望做成什么樣。這人例子會用到所有的附加值類型。

import org.eclipse.swt.*;
import org.eclipse.swt.events.*;
import org.eclipse.swt.graphics.*;
import org.eclipse.swt.widgets.*;
import org.eclipse.swt.layout.*;
public class ComplexFormLayoutExample {
static Display display;
static Shell shell;
static Image dogImage;
static Text dogNameText;
static Combo dogBreedCombo;
static Canvas dogPhoto;
static List categories;
static Text nameText;
static Text phoneText;
public static void main(String[] args){
display = new Display();
shell = new Shell(display);
FormLayout layout= new FormLayout();
layout.marginWidth = 5;
layout.marginHeight = 5;
shell.setLayout(layout);
shell.setText("Dog Show Entry");
Group ownerInfo = new Group(shell, SWT.NONE);
ownerInfo.setText("Owner Info");
FormLayout ownerLayout = new FormLayout();
ownerLayout.marginWidth = 5;
ownerLayout.marginHeight = 5;
ownerInfo.setLayout(ownerLayout);
Label dogName = new Label(shell, SWT.NONE);
dogName.setText("Dog's Name:");
dogNameText = new Text(shell, SWT.SINGLE | SWT.BORDER);
Label dogBreed = new Label(shell, SWT.NONE);
dogBreed.setText("Breed:");
dogBreedCombo = new Combo(shell, SWT.NONE);
dogBreedCombo.setItems(new String [] {"Collie", "Pitbull", "Poodle", "Scottie"});
Label photo = new Label(shell, SWT.NONE);
photo.setText("Photo:");
dogPhoto = new Canvas(shell, SWT.BORDER);
Button browse = new Button(shell, SWT.PUSH);
browse.setText("Browse...");
Button delete = new Button(shell, SWT.PUSH);
delete.setText("Delete");
Label cats = new Label (shell, SWT.NONE);
cats.setText("Categories");
categories = new List(shell, SWT.MULTI | SWT.BORDER | SWT.V_SCROLL | SWT.H_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"});
Button enter = new Button(shell, SWT.PUSH);
enter.setText("Enter");
FormData data = new FormData();
data.top = new FormAttachment (dogNameText,0,SWT.CENTER);
dogName.setLayoutData(data);
data = new FormData();
data.left = new FormAttachment (dogName,5);
data.right = new FormAttachment (100,0);
dogNameText.setLayoutData(data);
data = new FormData();
data.top = new FormAttachment (dogBreedCombo,0,SWT.CENTER);
dogBreed.setLayoutData(data);
data = new FormData();
data.top = new FormAttachment (dogNameText,5);
data.left = new FormAttachment (dogNameText,0,SWT.LEFT);
data.right = new FormAttachment (categories,-5);
dogBreedCombo.setLayoutData(data);
data = new FormData(80,80);
data.top = new FormAttachment (dogBreedCombo,5);
data.left = new FormAttachment (dogNameText,0,SWT.LEFT);
data.right = new FormAttachment (categories,-5);
data.bottom = new FormAttachment (ownerInfo,-5);
dogPhoto.setLayoutData(data);
dogPhoto.addPaintListener(new PaintListener() {
public void paintControl(final PaintEvent event) {
if (dogImage != null) {
event.gc.drawImage(dogImage, 0, 0);
}
}
});
data = new FormData();
data.top = new FormAttachment (dogPhoto,0,SWT.TOP);
photo.setLayoutData(data);
data = new FormData();
data.top = new FormAttachment (photo,5);
data.right = new FormAttachment (dogPhoto, -5);
browse.setLayoutData(data);
browse.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent event) {
String fileName = new FileDialog(shell).open();
if (fileName != null) {
dogImage = new Image(display, fileName);
}
}
});
data = new FormData();
data.left = new FormAttachment (browse,0,SWT.LEFT);
data.top = new FormAttachment (browse,5);
data.right = new FormAttachment (dogPhoto, -5);
delete.setLayoutData(data);
delete.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent event) {
if (dogImage != null) {
dogImage.dispose();
dogImage = null;
dogPhoto.redraw();
}
}
});
data = new FormData(90,140);
data.top = new FormAttachment (dogPhoto,0,SWT.TOP);
data.right = new FormAttachment (100,0);
data.bottom = new FormAttachment (enter,-5);
categories.setLayoutData(data);
data = new FormData();
data.bottom = new FormAttachment (categories,-5);
data.left = new FormAttachment (categories,0,SWT.CENTER);
cats.setLayoutData(data);
data = new FormData();
data.right = new FormAttachment (100,0);
data.bottom = new FormAttachment (100,0);
enter.setLayoutData(data);
enter.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent event) {
System.out.println("\nDog Name: " + dogNameText.getText());
System.out.println("Dog Breed: " + dogBreedCombo.getText());
System.out.println("Owner Name: " + nameText.getText());
System.out.println("Owner Phone: " + phoneText.getText());
System.out.println("Categories:");
String cats[] = categories.getSelection();
for (int i = 0; i < cats.length; i++) {
System.out.println("\t" + cats[i]);
}
}
});
data = new FormData();
data.bottom = new FormAttachment (enter,-5);
data.left = new FormAttachment (0,0);
data.right = new FormAttachment (categories,-5);
ownerInfo.setLayoutData(data);
Label name = new Label(ownerInfo, SWT.NULL);
name.setText("Name:");
Label phone = new Label(ownerInfo, SWT.PUSH);
phone.setText("Phone:");
nameText = new Text(ownerInfo, SWT.SINGLE | SWT.BORDER);
phoneText = new Text(ownerInfo, SWT.SINGLE | SWT.BORDER);
data = new FormData();
data.top = new FormAttachment (nameText,0,SWT.CENTER);
name.setLayoutData(data);
data = new FormData();
data.top = new FormAttachment (phoneText,0,SWT.CENTER);
phone.setLayoutData(data);
data = new FormData();
data.left = new FormAttachment (phone,5);
data.right = new FormAttachment (100,0);
nameText.setLayoutData(data);
data = new FormData();
data.left = new FormAttachment (nameText,0,SWT.LEFT);
data.right = new FormAttachment (100,0);
data.top = new FormAttachment (55,0);
phoneText.setLayoutData(data);
shell.pack();
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch())
display.sleep();
}
display.dispose();
}
}

當這個窗口被縮放時,象GridLayout的例子中一樣,相同的控件會被縮放。

編寫你自己的布局類
有時候,你可能需要編寫你自己的布局類。有可能你的布局的需求很復雜?;蛘吣阌行┑胤侥憧赡苄枰嗤耐庥^,而你希望改進代碼的重用率。或者你希望擴大領域知識來創(chuàng)建一個很有效率的布局類。不管原因是什么,在編寫一個新的類之前,有幾件事情是你要考慮的:
這個布局能夠用GridLayout或者FormLayout,以及可能是一些嵌套布局來實現(xiàn)嗎?
希望達到的效果可以更容易的用一個縮放監(jiān)聽器來實現(xiàn)嗎?
你定義了一個通用的布局算法還是只是布置了小窗口部件?
首先,我們會先來看看布局是怎么樣工作的,然后我們再創(chuàng)建一個新的布局類。另一個編寫你自己的而已的例子可以在“用SWT創(chuàng)建你自己的小窗口部件”(Creating Your Own Widgets Using SWT)中的“復合小窗口部件示例”(Compound Widget Example)一節(jié)中找到,它顯示了如何用一個縮放監(jiān)聽器或者一個新布局類達到相同的外觀。
布局是如何工作的
Layout是所有布局的超類。它只有兩個方法computeSize和layout。這個類的定義如下:
public abstract class Layout {
protected abstract Point computeSize(Composite composite, int widthHint, int heightHint, boolean flushCache);
protected abstract void layout(Composite composite, boolean flushCache);
}
一旦這個Composite的子組件被確定大小并根據(jù)布局類中的編寫的布局算法來放置的時候,computeSize方法計算包括所有這些子組件的矩形區(qū)域的寬度和高度。hint參數(shù)允許寬度和/或者高度被約束。例如,如果一個維被約束,一個布局可能選擇在另一個維上增長。一個SWT.DEFAULT的hint意思是使用合適的(preffed)大小。
既然一個布局控制著小窗口部件在一個Composite中的大小和放置,Composite中也有一些方法可以和布局一起使用。
public void setLayout(Layout layout);
public Layout getLayout();
一個應用程序可以強制一個布局重新計算子組件的大小,并通過調(diào)用父Composite的layout()方法重新定位這些子組件。
public void layout(boolean changed);
public void layout();
// calls layout(true);
你需要在你改變了任何子組件,可能導致子組件的大小和位置發(fā)生改變的時候做這件事情,比如改變了子組件的字體,改變了子組件的文本或者圖片,增加了新的子組件,或者為子組件增加了子組件。(如果子組件可以適應這個變化,布局可能不會成功—例如,改變一個可滾動的多行的Text的字體或者文本)。既然這些改變都是通過編程來實現(xiàn)的,它們不會觸發(fā)事件。從而,父構件不會知道這些變化,必須通過layout方法來告許它。這個策略導致了閃動,這是因為應用程序可能作了好幾個改變?nèi)缓蟛鸥嬖V父構件重新布局,并且子組件只被重畫一次而不是每次改變都重畫一次。如果沒有調(diào)用layout()并改變是在shell打開后發(fā)生的,那么子組件可能不會被正確的放置直到shell由于某種原因進行了縮放。注意shell.open()導致一個布局過程的發(fā)生。
Composite的computeSize方法計算Composite的合適大小,這是它由這個布局決定的客戶區(qū)(client)的大小加上它的修剪(trim)。
public Point computeSize(int widthHint, int heightHint, boolean changed);
public Point computeSize(int widthHint, int heightHint);
// calls computeSize(widthHint, heightHint, true);
Composite的clientArea是包含所有的子組件的矩形區(qū)。一個布局在客戶區(qū)中放置子組件。
public Rectangle getClientArea ();
Composite的trim(修剪)是客戶區(qū)外的區(qū)域。對于一些Composite,修剪的大小是零。修剪可以通過傳遞客戶區(qū)的尺寸給computeTrim計算出來。
public Rectangle computeTrim (int x, int y, int width, int height);
調(diào)用Composite的pack方法將把它縮放到它的合適大小。
public void pack(boolean changed);
// calls setSize(computeSize(SWT.DEFAULT, SWT.DEFAULT, changed));
public void pack();
// calls pack(true);
layout,computeSize和pack方法的布爾型參數(shù)是changed標志。如果為true,它表明Composite的內(nèi)容已經(jīng)被通過某些方法改變而影響了它的合適大小,所以布局所持有的任何緩存都需要被沖洗。當一個Composite被縮放時,它調(diào)用layout(false)來讓自己的布局安排它的子組件;因此小窗口部件的內(nèi)容緩存沒有被沖洗掉。這讓布局只在必要的進要的時候進行代價高昂的計算。
緩存可以改進性能,但它也可能是有欺騙性的。你可以選擇完全不緩存 — 事實上,最好不要試力用緩存,直到你的代碼已經(jīng)穩(wěn)定。當你考慮緩存什么時,確保你沒有緩存任何小窗口部件狀態(tài),例如一個標簽的文本,或者一個列表的項的數(shù)目。
如果在你的應用程序中你有一些垂直導向的Composite小窗口部件,你可能會選擇編寫ColumnLayout。我們將會展示一個簡單的布局類版本,它把Composite子組件放置到一個單列中。這個類有固定的修剪和間距。子組件被賦予相同的寬度,但它們能有自己自然的高度。(注意,在*2.0*中,RowLayout已經(jīng)被擴展為有ColumnLayout的功能,如果它的類型被設為SWT.VERTICAL的話。正因為如此,這個例子只是作為一個例子。事實上,如果你現(xiàn)在需要把小窗口部件放在一列中,你可以用RowLayout來實現(xiàn)。)
這段ColumnLayout類的例子就在下面。注意,我們緩存了小窗口部件子組件的寬度,子組件的高度和(加上了間距),而且這些值被用來計算大小和放置子組件。如果flushCache是true,它們會被重新計算。
import org.eclipse.swt.graphics.*;
import org.eclipse.swt.widgets.*;
import org.eclipse.swt.layout.*;
public class ColumnLayout extends Layout {
// fixed margin and spacing
public static final int MARGIN = 4;
public static final int SPACING = 2;
// cache
Point [] sizes;
int maxWidth, totalHeight;
protected Point computeSize(Composite composite, int wHint, int hHint, boolean flushCache) {
Control children[] = composite.getChildren();
if (flushCache || sizes == null || sizes.length != children.length) {
initialize(children);
}
int width = wHint, height = hHint;
if (wHint == SWT.DEFAULT) width = maxWidth;
if (hHint == SWT.DEFAULT) height = totalHeight;
return new Point(width + 2 * MARGIN, height + 2 * MARGIN);
}
protected void layout(Composite composite, boolean flushCache) {
Control children[] = composite.getChildren();
if (flushCache || sizes == null || sizes.length != children.length) {
initialize(children);
}
Rectangle rect = composite.getClientArea();
int x = MARGIN, y = MARGIN;
int width = Math.max(rect.width - 2 * MARGIN, maxWidth);
for (int i = 0; i < children.length; i++) {
int height = sizes[i].y;
children[i].setBounds(x, y, width, height);
y += height + SPACING;
}
void initialize(Control children[]) {
maxWidth = 0;
totalHeight = 0;
sizes = new Point [children.length];
for (int i = 0; i < children.length; i++) {
sizes[i] = children[i].computeSize(SWT.DEFAULT, SWT.DEFAULT, true);
maxWidth = Math.max(maxWidth, sizes[i].x);
totalHeight += sizes[i].y;
}
totalHeight += (children.length - 1) * SPACING;
}
}
這里有一些簡單的測試代碼來測試ColumnLayout。Grow和shrink按鈕展示了在改變了其中一個子組件的寬度后調(diào)用Shell的layout()方法強制一個重新布局的過程。調(diào)用layout()和調(diào)用layout(true)是一樣的,它告訴ColumnLayout在設定子組件的邊界的時候沖掉它的緩存。Shell在放置好子組件后也調(diào)用pack()方法。這強制讓Shell采用新的大小。
import org.eclipse.swt.*;
import org.eclipse.swt.widgets.*;
import org.eclipse.swt.layout.*;
import org.eclipse.swt.events.*;
public class ColumnLayoutTest {
static Shell shell;
static Button button3;
public static void main(String[] args) {
Display display = new Display();
shell = new Shell(display);
shell.setLayout(new ColumnLayout());
new Button(shell, SWT.PUSH).setText("B1");
new Button(shell, SWT.PUSH).setText("Very Wide Button 2");
(button3 = new Button(shell, SWT.PUSH)).setText("Button 3");
Button grow = new Button(shell, SWT.PUSH);
grow.setText("Grow Button 3");
grow.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
button3.setText("Extreemely Wide Button 3");
shell.layout();
shell.pack();
}
});
Button shrink = new Button(shell, SWT.PUSH);
shrink.setText("Shrink Button 3");
shrink.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
button3.setText("Button 3");
shell.layout();
shell.pack();
}
});
shell.pack();
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) display.sleep();
}
}
}
如果我們運行這段測試代碼,窗口會像左圖看起來那樣。按下Grow Button 3按鈕會導致窗口如右圖所示。用鼠標縮放窗口的大小也能使按鈕變寬(或者變窄)但是它們不會變高。

重載Composite
如果你正在編寫自己的小窗口部件,就像“用SWT創(chuàng)建你自己的小窗口部件”(Creating Your Own Widgets Using SWT)中描述的那樣,并且你子類化了Composite,那么你的實現(xiàn)要幾點要考慮:
如果在你的新Composite中提供了修剪(trimming),確保你重載了computeTrim和getClientArea。
絕對不要重載layout(),但是你可以重載layout(boolean)。
重載setLayout讓它不做任何事。
重載layout(boolean)讓它調(diào)用你的布局代碼。
重載computeSize讓它正確的計算出你的Composite的大小。
概要
SWT提供了很多不同的方法來放置小窗口部件。最簡單的方法并且是你會最常使用到的方法,就是用這些標準布局類的其中一個:FillLayout,RowLayout,GridLayout或者FormLayout。





