org.eclipse.swt.layout.FormLayout 是 SWT 中最強大、彈性最高的佈局管理器。它不依賴固定網格或流向,而是透過 FormAttachment 定義控件邊緣之間的「相對關係」:
每個子控件需設定 FormData 作為 LayoutData,包含 left, right, top, bottom 四個 FormAttachment。
Button1 固定在 (20%, 20%)。Button2 的左/上邊緣依附於 Button1 的中心點,實現「Button2 中心對齊 Button1 中心」。
package swt_jface.demo2;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.FormAttachment;
import org.eclipse.swt.layout.FormData;
import org.eclipse.swt.layout.FormLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
public class FormLayoutSample {
Display display = new Display();
Shell shell = new Shell(display);
public FormLayoutSample() {
shell.setLayout(new FormLayout());
Button button1 = new Button(shell, SWT.PUSH);
button1.setText("button1");
FormData fd1 = new FormData();
fd1.left = new FormAttachment(20); // 父容器左邊 20%
fd1.top = new FormAttachment(20); // 父容器上邊 20%
button1.setLayoutData(fd1);
Button button2 = new Button(shell, SWT.PUSH);
button2.setText("button number 2");
FormData fd2 = new FormData();
// 關鍵:依附於 button1,offset=0, alignment=SWT.CENTER
fd2.left = new FormAttachment(button1, 0, SWT.CENTER);
fd2.top = new FormAttachment(button1, 0, SWT.CENTER);
button2.setLayoutData(fd2);
shell.pack();
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) display.sleep();
}
display.dispose();
}
public static void main(String[] args) {
new FormLayoutSample();
}
}
展示 FormLayout 處理動態調整大小的經典模式:
SWT.WRAP 自動換行。package swt_jface.demo2;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.FormAttachment;
import org.eclipse.swt.layout.FormData;
import org.eclipse.swt.layout.FormLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.List;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Shell;
public class Main {
public static void main(String[] args) {
Display display = new Display();
final Shell shell = new Shell(display);
Label label = new Label(shell, SWT.WRAP);
label.setText("This is a long text string that will wrap when the dialog is resized.");
List list = new List(shell, SWT.BORDER | SWT.H_SCROLL | SWT.V_SCROLL);
list.setItems(new String[] { "Item 1", "Item2" });
Button button1 = new Button(shell, SWT.PUSH);
button1.setText("Ok");
Button button2 = new Button(shell, SWT.PUSH);
button2.setText("Cancel");
final int insetX = 4, insetY = 4;
FormLayout formLayout = new FormLayout();
formLayout.marginWidth = insetX;
formLayout.marginHeight = insetY;
shell.setLayout(formLayout);
// Label: 左右滿版,高度自適應
Point lblSize = label.computeSize(SWT.DEFAULT, SWT.DEFAULT);
final FormData labelData = new FormData(lblSize.x, SWT.DEFAULT);
labelData.left = new FormAttachment(0, 0);
labelData.right = new FormAttachment(100, 0);
label.setLayoutData(labelData);
// 監聽 Resize 更新 Label 寬度 (確保 Wrap 正確)
shell.addListener(SWT.Resize, e -> {
Rectangle rect = shell.getClientArea();
labelData.width = rect.width - insetX * 2;
shell.layout();
});
// Cancel: 右下角
FormData btn2Data = new FormData();
btn2Data.right = new FormAttachment(100, -insetX);
btn2Data.bottom = new FormAttachment(100, 0);
button2.setLayoutData(btn2Data);
// Ok: Cancel 左側
FormData btn1Data = new FormData();
btn1Data.right = new FormAttachment(button2, -insetX);
btn1Data.bottom = new FormAttachment(100, 0);
button1.setLayoutData(btn1Data);
// List: 填滿中間區域
FormData listData = new FormData();
listData.left = new FormAttachment(0, 0);
listData.right = new FormAttachment(100, 0);
listData.top = new FormAttachment(label, insetY);
listData.bottom = new FormAttachment(button2, -insetY);
list.setLayoutData(listData);
shell.pack();
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) display.sleep();
}
display.dispose();
}
}
new FormAttachment(control, offset, alignment)
| 參數 | 說明 |
|---|---|
| control | 依附目標:null 或百分比 (0-100) 代表父容器邊緣;Control 實例代表同層控件邊緣。 |
| offset | 像素偏移量。正數:向右/向下;負數:向左/向上。 |
| alignment (僅當 control 為 Control 時) | SWT.LEFT (預設,依附目標左邊)、SWT.RIGHT (依附目標右邊)、SWT.CENTER (依附目標中心)、SWT.TOP, SWT.BOTTOM。 |
left = new FormAttachment(50, -width/2) 或 left = new FormAttachment(other, 0, SWT.CENTER)。left = new FormAttachment(other, 5, SWT.RIGHT) (間距 5px)。left 依附 A 右側,right 依附 B 左側。FormLayout 不會強制子控件最小尺寸。若內容過多導致控件擠壓,需在 FormData 建構子指定 width/height,或呼叫 control.computeSize(w, h) 取得建議尺寸。
A.left 依附 B.right,B.left 依附 A.right 會造成佈局死循環。設計時建立清晰的「錨點」層級 (如:按鈕錨定右下 -> List 錨定按鈕上方 -> Label 錨定 List 上方)。
新增控件後呼叫 parent.layout()。移除控件前需 control.dispose() 並清理其 FormData 參考 (若有被其他控件依附),最後 parent.layout()。
FormLayout 計算複雜度較高 (約束求解)。控件數量超過 50+ 且頻繁 Resize 時,可能感覺延遲。此時考慮簡化依附關係或改用 GridLayout。
FormLayout 以「相對依附」機制提供極致彈性,是實現複雜響應式對話框、精確對齊介面的利器。掌握 FormAttachment(control, offset, alignment) 三參數語意,配合 Resize 事件微調關鍵控件尺寸,即可駕馭各種動態佈局需求。