FormLayout通過爲小窗口部件創建四邊的Form附加值(attachment)來進行工作,並且把這些Form附加值存儲在佈局數據中。一個 附加值讓一個小窗口部件指定的一邊粘貼(attach)到父Composite的一個位置或者這個佈局中的另一個小窗口部件上。這爲佈局提供了極大的便 利,並且這也允許你指定佈局中單個小窗口部件的放置。
FormLayout的可配置域
MarginWidth, MarginHeight
FormLayout中的margin域類似於GridLayout中的相同域。左邊和右邊邊距(margin)被定義成marginWidth,而頂部和底部的邊距被定義成marginHeight。邊距也能根據每個小窗口部件的附加值來定義。FormLayout邊距在默認情況下爲0。
爲了設置這些邊距,我們創建一個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());
但是,這段代碼創建了一個沒有附加值的FormData對象。在這個例子中,定義了默認的附加值值,這沒有體現出FormLayout的目的和效用。默認 的附加值值讓小窗口部件粘貼到父Composite的左邊和頂部。如果FormLayout中的每一個小窗口部件都使用了默認的附加值值,它們就會全部被 重疊的放置在父Composite的左上角。
Left, Right, Top和Bottom
FormLayout中的left, right, top和bottom域分別指定了與小窗口部件的左邊,右邊,頂部和底部相關聯的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%。
相對於另一個小窗口部件的相鄰邊粘貼保證了小窗口部件的指定邊總是與另一個小窗口部件的最近邊相鄰。相對於另一個小窗口部件的相對邊粘貼保證了小窗口部件 的指定邊總是與另一個小窗口部件的最遠邊對齊。最後,相對於另一個小窗口部件居中讓小窗口部件在另一個小窗口部件中居中。這些方法都可以加或者不加偏移值 來實現。
button1.setLayoutData(new FormData());
注意到如果多於一個小窗口部件被定義爲沒有任何附加值的時候,這些小窗口部件都會被放置到窗口中相同的默認位置上,並且會發生重疊。Form附加值在Form附加值對象這一節中將進行更仔細的敘述。
Width和Height
FormData中的width和height 域用來指定小窗口部件所需的寬度和高度。如果所請求的寬度和高度與附加值設置的約束髮生衝突,那麼這些請求的高度和寬度就不能遵從了。雖然通過設置附加值 也以決定寬度和高度值,但有些情況下你不希望爲小窗口部件的所有邊都定義附加值值。在這種情況下,它們就可以很有用的象下面這樣設定小窗口部件的寬度和高 度值:
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附加值是一個用來定義小窗口部件的指定邊的附加值的對象。不需要總是爲小窗口部件的四個邊都設置附加值。通常只指定一個或者多個小窗口部件的邊就能完全指定它的放置。爲了更適當的放置你的小窗口部件,你必須至少爲FormData中left或者right之一,以及top或者bottom之 一定義附加值。如果你只希望粘貼小窗口部件的左邊而不是右邊,那麼這個小窗口部件將用它的邊來進行定位,並且這個小窗口部件會採用它的正常大小(或者當設 置了請求大小時,採用它的請求大小)。如果你沒有貼粘左邊或者右邊,默認的定義會把你的小窗口部件粘貼到窗體的左邊。對於頂部和底部也是採用了相同的邏 輯。
相對一個位置進行粘貼
存在很多種類型的附加值。第一種就是粘貼到相對於父Composite的一個位置。通過定義一個100以內的百分值就能實現,例如:
FormData formData = new FormData();
formData.top = new FormAttachment (50,0);
button1.setLayoutData(formData);
這裏設置Button的頂部到相對於父Composite(一個Shell)高度的50%的位置,偏移量爲0。當這個Shell進行縮放的時候,Button的頂部會始終在50%的位置,像這樣:
如果我們選擇設定一個偏移值,Button的上部就會被設置爲父Composite的50%加上或者減去爲偏移量設置的象素數。
我們也能定義不使用百分比定義按鈕的位置,例如:
formData.top = new FormAttachment (30,70,10);
button1.setLayoutData(formData);
如果父Composite的高度被定義爲70個單位,這裏設置了Button的頂部在父Composite頂部以下30個單位,再加上10個象素的地方。
相對於父Composite進行粘貼
第二種類型的附加值是相對於父Composite的邊緣進行粘貼。這與相對於一個位置進行粘貼的實現方法差不多,但是這個位置值只能是0%或者100%。 當處於垂直樣式的時候,0這個位置被定義爲父Composite的頂部,但處於水平樣式的時候被定義爲左邊。右邊和底部邊緣被定義爲100。因此,如果我 們要粘貼一個小窗口部件到父Composite的右邊,我們只需要簡單的創建一個附加值,然後設置它的位置值爲100:
FormData formData = new FormData();
formData.right = new FormAttachment (100,-5);
button1.setLayoutData(formData);
這裏把Button的右邊粘貼到了父Composite(一個Shell)的右邊緣,並有一個5個象素的偏移值。注意,附加值是往一個方向增加的。如果你 要讓一個小窗口部件向下或者所右偏移,偏移值必須是正的。如果要小窗口部件向上或者向左偏移,這個偏移值必須是負的。現在當這個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個象素的位置。
雖然默認的情況下是相對於一個控件的相鄰邊進行粘貼,FromAttachment也能被創建用來相對一個控件的相對邊進行粘貼。當要排列小窗口部件的時 候這就非常的有用了。在這種情況下,你可以用TOP, BOTTOM, LEFT或者RIGHT排列(alignment)創建相對於另一個控件的附加值,例如:
formData2.top = new FormAttachment (button1,0,SWT.TOP);
在接下來的例子中,按鈕1的頂部邊被定位在Shell的20%的地方。按鈕2使用了TOP排列,它的頂部邊和按鈕1的頂部邊對齊。這意味着按鈕2的頂部也 被定位在這個Shell的20%的位置。注意到當指定了頂部附加值,只有小窗口部件的垂直放置會被定義。仍然需要爲按鈕2設置左邊附加值以得Button 不會發生重疊。
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)創建相對於另一個控件的附加值,例如:
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不能解決的問題,讓它成爲定義佈局很有用的類。
重要的:不要定義循環的附 加值。例如,不要把按鈕1的右邊粘貼到按鈕2的左邊,然後又把按鈕2的左邊粘貼到按鈕1的右邊。這會過度約束佈局,導致不可預知的行爲。雖然運算會被中 斷,但結果是不可預知的。因此,確實保證你沒有過度約束你的小窗口部件。僅提供所需要的附加值來適當的放置的小窗口部件。
到目前爲止,所有使用FormLayout的例子都只是圍 繞着一兩個Button,爲了展示FormAttachment是如何工作 的。下面,我們會舉一個有更多的Button的例子來展示使用附加值如何安排佈局。我們會從畫一張描繪我們要創建的附加值的基本草圖開始。
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舉例的“狗狗展示條目”的例子。這段代碼創建一個一樣的佈局,但是用不同的觀念來實現它。
我們會從修改爲網格佈局所作的設計草圖開始。我們會畫出如何做,而不僅僅畫出我們希望做成什麼樣。這人例子會用到所有的附加值類型。
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的例子中一樣,相同的控件會被縮放。
編寫你自己的佈局類
有時候,你可能需要編寫你自己的佈局類。有可能你的佈局的需求很複雜。或者你有些地方你可能需要相同的外觀,而你希望改進代碼的重用率。或者你希望擴大領域知識來創建一個很有效率的佈局類。不管原因是什麼,在編寫一個新的類之前,有幾件事情是你要考慮的:
• 這個佈局能夠用GridLayout或者FormLayout,以及可能是一些嵌套佈局來實現嗎?
• 希望達到的效果可以更容易的用一個縮放監聽器來實現嗎?
• 你定義了一個通用的佈局算法還是隻是佈置了小窗口部件?
首先,我們會先來看看佈局是怎麼樣工作的,然後我們再創建一個新的佈局類。另一個編寫你自己的而已的例子可以在“用SWT創建你自己的小窗口部件” (Creating Your Own Widgets Using SWT)中的“複合小窗口部件示例”(Compound Widget Example)一節中找到,它顯示瞭如何用一個縮放監聽器或者一個新佈局類達到相同的外觀。
佈局是如何工作的
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的子組件被確定大小並根據佈局類中的編寫的佈局算法來放置的時候,computeSize方法計算包括所有這些子組件的矩形區域的寬度和高度。hint參數允許寬度和/或者高度被約束。例如,如果一個維被約束,一個佈局可能選擇在另一個維上增長。一個SWT.DEFAULT的hint意思是使用合適的(preffed)大小。
既然一個佈局控制着小窗口部件在一個Composite中的大小和放置,Composite中也有一些方法可以和佈局一起使用。
public void setLayout(Layout layout);
public Layout getLayout();
一個應用程序可以強制一個佈局重新計算子組件的大小,並通過調用父Composite的layout()方法重新定位這些子組件。
public void layout(boolean changed);
public void layout();
// calls layout(true);
你需要在你改變了任何子組件,可能導致子組件的大小和位置發生改變的時候做這件事情,比如改變了子組件的字體,改變了子組件的文本或者圖片,增加了新的子 組件,或者爲子組件增加了子組件。(如果子組件可以適應這個變化,佈局可能不會成功—例如,改變一個可滾動的多行的Text的字體或者文本)。既然這些改 變都是通過編程來實現的,它們不會觸發事件。從而,父構件不會知道這些變化,必須通過layout方法來告許它。這個策略導致了閃動,這是因爲應用程序可能作了好幾個改變然後才告訴父構件重新佈局,並且子組件只被重畫一次而不是每次改變都重畫一次。如果沒有調用layout()並改變是在shell打開後發生的,那麼子組件可能不會被正確的放置直到shell由於某種原因進行了縮放。注意shell.open()導致一個佈局過程的發生。
Composite的computeSize方法計算Composite的合適大小,這是它由這個佈局決定的客戶區(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是包含所有的子組件的矩形區。一個佈局在客戶區中放置子組件。
public Rectangle getClientArea ();
Composite的trim(修剪)是客戶區外的區域。對於一些Composite,修剪的大小是零。修剪可以通過傳遞客戶區的尺寸給computeTrim計算出來。
public Rectangle computeTrim (int x, int y, int width, int height);
調用Composite的pack方法將把它縮放到它的合適大小。
public void pack(boolean changed);
// calls setSize(computeSize(SWT.DEFAULT, SWT.DEFAULT, changed));
public void pack();
// calls pack(true);
layout,computeSize和pack方法的布爾型參數是changed標誌。如果爲true, 它表明Composite的內容已經被通過某些方法改變而影響了它的合適大小,所以佈局所持有的任何緩存都需要被沖洗。當一個Composite被縮放 時,它調用layout(false)來讓自己的佈局安排它的子組件;因此小窗口部件的內容緩存沒有被沖洗掉。這讓佈局只在必要的進要的時候進行代價高昂 的計算。
緩存可以改進性能,但它也可能是有欺騙性的。你可以選擇完全不緩存 — 事實上,最好不要試力用緩存,直到你的代碼已經穩定。當你考慮緩存什麼時,確保你沒有緩存任何小窗口部件狀態,例如一個標籤的文本,或者一個列表的項的數目。
如果在你的應用程序中你有一些垂直導向的Composite小窗口部件,你可能會選擇編寫ColumnLayout。我們將會展示一個簡單的佈局類版本, 它把Composite子組件放置到一個單列中。這個類有固定的修剪和間距。子組件被賦予相同的寬度,但它們能有自己自然的高度。(注意,在*2.0*中,RowLayout已經被擴展爲有ColumnLayout的功能,如果它的類型被設爲SWT.VERTICAL的話。正因爲如此,這個例子只是作爲一個例子。事實上,如果你現在需要把小窗口部件放在一列中,你可以用RowLayout來實現。)
這段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按鈕展示了在改變了其中一個子組件的寬度後調用Shell的layout()方法強制一個重新佈局的過程。調用layout()和調用layout(true)是一樣的,它告訴ColumnLayout在設定子組件的邊界的時候沖掉它的緩存。Shell在放置好子組件後也調用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創建你自己的小窗口部件”(Creating Your Own Widgets Using SWT)中描述的那樣,並且你子類化了Composite,那麼你的實現要幾點要考慮:
• 如果在你的新Composite中提供了修剪(trimming),確保你重載了computeTrim和getClientArea。
• 絕對不要重載layout(),但是你可以重載layout(boolean)。
• 重載setLayout讓它不做任何事。
• 重載layout(boolean)讓它調用你的佈局代碼。
• 重載computeSize讓它正確的計算出你的Composite的大小。
概要
SWT提供了很多不同的方法來放置小窗口部件。最簡單的方法並且是你會最常使用到的方法,就是用這些標準佈局類的其中一個:FillLayout,RowLayout,GridLayout或者FormLayout。