在 SWT/JFace 桌面應用開發中,GridLayout 是功能最強大、最常用的佈局管理器。它將父容器劃分為網格,支援靈活的單元格合併、對齊、間距控制與動態調整。本文系統整理 GridLayout 核心屬性、GridData 進階配置,並提供四個完整範例涵蓋從基礎到複雜表單的實戰場景。
GridLayout 本身控制整體網格結構:
| 屬性 | 類型 | 說明 | 預設值 |
|---|---|---|---|
| numColumns | int | 欄位數(列數由元件數量自動決定) | 1 |
| makeColumnsEqualWidth | boolean | true 時所有欄寬相等 | false |
| marginWidth | int | 左右邊距(像素) | 5 |
| marginHeight | int | 上下邊距(像素) | 5 |
| horizontalSpacing | int | 元件水平間距 | 5 |
| verticalSpacing | int | 元件垂直間距 | 5 |
| marginLeft/Right/Top/Bottom | int | 四個方向獨立邊距(優先於 marginWidth/Height) | 0 |
每個子元件透過 setLayoutData(GridData) 定義在網格中的行為:
| 屬性 | 說明 | 常用常數 |
|---|---|---|
| horizontalAlignment | 水平對齊方式 | BEGINNING, CENTER, END, FILL |
| verticalAlignment | 垂直對齊方式 | 同上 |
| horizontalSpan | 橫向跨越欄數 | int (預設 1) |
| verticalSpan | 縱向跨越列數 | int (預設 1) |
| grabExcessHorizontalSpace | 容器寬度變化時,是否搶佔多餘水平空間 | boolean |
| grabExcessVerticalSpace | 容器高度變化時,是否搶佔多餘垂直空間 | boolean |
| widthHint / heightHint | 建議寬/高(像素),未設定其他屬性時生效 | int |
| horizontalIndent | 水平縮進(需配合 BEGINNING 對齊) | int |
// 常用建構子
new GridData() // 預設
new GridData(int horizontalAlignment) // 僅水平對齊
new GridData(int hAlign, int vAlign, boolean grabH, boolean grabV) // 完整控制
new GridData(int hAlign, int vAlign, boolean grabH, boolean grabV, int hSpan, int vSpan) // 含跨越
public class GridLayoutSample {
public static void main(String[] args) {
Display display = new Display();
Shell shell = new Shell(display);
shell.setLayout(new GridLayout(2, true)); // 2欄、等寬
// Button 1: 頂端對齊
Button b1 = new Button(shell, SWT.PUSH);
b1.setText("button1");
b1.setLayoutData(new GridData(SWT.DEFAULT, SWT.BEGINNING));
// List: 置中
List list = new List(shell, SWT.BORDER);
list.add("item 1"); list.add("item 2"); list.add("item 3");
list.setLayoutData(new GridData(SWT.DEFAULT, SWT.CENTER));
// Button 2: 底端對齊 + 縮進
Button b2 = new Button(shell, SWT.PUSH);
b2.setText("button #2");
GridData gd2 = new GridData(SWT.DEFAULT, SWT.END);
gd2.horizontalIndent = 5;
b2.setLayoutData(gd2);
// Button 3: 填滿
Button b3 = new Button(shell, SWT.PUSH);
b3.setText("3");
b3.setLayoutData(new GridData(SWT.FILL, SWT.FILL));
shell.pack(); shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) display.sleep();
}
display.dispose();
}
}
public class GridLayoutSampleGrabSpace {
public static void main(String[] args) {
Display display = new Display();
Shell shell = new Shell(display);
shell.setLayout(new GridLayout(3, false));
// Label: 固定尺寸
Label label = new Label(shell, SWT.BORDER);
label.setText("label");
GridData ld = new GridData();
ld.widthHint = 60; ld.heightHint = 20;
label.setLayoutData(ld);
// Text: 搶佔所有多餘空間,填滿
Text text = new Text(shell, SWT.SINGLE | SWT.BORDER);
text.setText("text");
GridData td = new GridData(SWT.FILL, SWT.FILL, true, true);
text.setLayoutData(td);
// Button: 同樣搶佔填滿
Button btn = new Button(shell, SWT.PUSH);
btn.setText("button");
GridData bd = new GridData(SWT.FILL, SWT.FILL, true, true);
btn.setLayoutData(bd);
shell.setSize(300, 80);
shell.open();
// ... 事件循環
}
}
關鍵:grabExcessHorizontalSpace=true 配合 horizontalAlignment=FILL 才能讓元件隨視窗縮放。
public class GridLayoutSampleSpan {
public static void main(String[] args) {
Display display = new Display();
Shell shell = new Shell(display);
shell.setLayout(new GridLayout(2, true));
Button b1 = new Button(shell, SWT.PUSH);
b1.setText("button1");
b1.setLayoutData(new GridData(SWT.DEFAULT, SWT.BEGINNING));
List list = new List(shell, SWT.BORDER);
list.add("item 1"); list.add("item 2"); list.add("item 3");
list.setLayoutData(new GridData(SWT.DEFAULT, SWT.CENTER));
// Button 2: 橫向跨越 2 欄,填滿
Button b2 = new Button(shell, SWT.PUSH);
b2.setText("button #2");
GridData gd2 = new GridData(SWT.FILL, SWT.END);
gd2.horizontalSpan = 2;
b2.setLayoutData(gd2);
// Button 3: 縱向跨越 3 列
Button b3 = new Button(shell, SWT.PUSH);
b3.setText("3");
GridData gd3 = new GridData(SWT.DEFAULT, SWT.END);
gd3.verticalSpan = 3;
b3.setLayoutData(gd3);
shell.pack(); shell.open();
// ...
}
}
public class BookEntryForm {
public static void main(String[] args) {
Display display = new Display();
Shell shell = new Shell(display);
shell.setText("Book Entry Demo");
GridLayout layout = new GridLayout(4, false);
layout.verticalSpacing = 8;
shell.setLayout(layout);
// Title: 橫跨 3 欄
createLabel(shell, "Title: ");
Text title = new Text(shell, SWT.SINGLE | SWT.BORDER);
GridData gd = new GridData(SWT.FILL, SWT.DEFAULT);
gd.horizontalSpan = 3; title.setLayoutData(gd);
// Author: 橫跨 3 欄
createLabel(shell, "Author(s): ");
Text authors = new Text(shell, SWT.SINGLE | SWT.BORDER);
gd = new GridData(SWT.FILL, SWT.DEFAULT);
gd.horizontalSpan = 3; authors.setLayoutData(gd);
// Cover: 縱跨 3 列
createLabel(shell, "Cover: ");
GridData coverLd = new GridData();
coverLd.verticalSpan = 3;
new Label(shell, SWT.NONE).setLayoutData(coverLd);
CLabel cover = new CLabel(shell, SWT.BORDER);
gd = new GridData(SWT.FILL, SWT.FILL);
gd.horizontalSpan = 1; gd.verticalSpan = 3;
gd.widthHint = 100; gd.heightHint = 100;
cover.setLayoutData(gd);
// Pages, Publisher, Rating: 各佔 1 欄
createLabel(shell, "Pages");
Text pages = new Text(shell, SWT.SINGLE | SWT.BORDER);
pages.setLayoutData(new GridData(SWT.FILL, SWT.DEFAULT));
createLabel(shell, "Publisher");
Text pub = new Text(shell, SWT.SINGLE | SWT.BORDER);
pub.setLayoutData(new GridData(SWT.FILL, SWT.DEFAULT));
createLabel(shell, "Rating");
Combo rating = new Combo(shell, SWT.READ_ONLY);
rating.setItems("5", "4", "3", "2", "1");
rating.setLayoutData(new GridData(SWT.FILL, SWT.DEFAULT));
// Abstract: 橫跨 3 欄 + 搶佔垂直空間
createLabel(shell, "Abstract:");
Text abs = new Text(shell, SWT.WRAP | SWT.MULTI | SWT.BORDER | SWT.H_SCROLL | SWT.V_SCROLL);
gd = new GridData(SWT.FILL, SWT.FILL, true, true);
gd.horizontalSpan = 3; abs.setLayoutData(gd);
// Enter Button: 橫跨 4 欄,靠右
Button enter = new Button(shell, SWT.PUSH);
enter.setText("Enter");
gd = new GridData(SWT.END, SWT.DEFAULT);
gd.horizontalSpan = 4; enter.setLayoutData(gd);
// 填入測試資料
title.setText("Professional Java Interfaces with SWT/JFace");
authors.setText("Jack Li Guojie");
pages.setText("500pp");
pub.setText("John Wiley & Sons");
// cover.setBackground(new Image(display, "cover.jpg"));
abs.setText("This book provides a comprehensive guide for\nyou to create Java user interfaces with SWT/JFace.");
shell.pack(); shell.open();
// ... 事件循環
}
private static Label createLabel(Composite parent, String text) {
Label l = new Label(parent, SWT.NONE);
l.setText(text);
return l;
}
}
// 新增元件後必須呼叫
parent.layout(true); // 重新佈局
parent.pack(); // 可選:調整父容器大小
GridLayout layout = new GridLayout(3, false);
// 第 1 欄固定,第 2 欄自適應,第 3 欄固定
// 無直接 API,需結合 grabExcessHorizontalSpace 控制
horizontalAlignment=FILL 但 grabExcessHorizontalSpace=false,元件不會隨視窗放大。pack() 依據佈局計算最佳尺寸;setSize() 強制指定,佈局管理器會在該尺寸內安排。setRedraw(false) 批次更新後再 setRedraw(true)。paint 事件中做複雜計算。GridLayout 配合 GridData 提供了極高的佈局自由度:
numColumns、makeColumnsEqualWidth、邊距/間距Alignment)、跨越 (Span)、空間搶佔 (Grab)、尺寸提示 (Hint)FILL + grab=true 實現響應式;Span 實現複雜表單;Indent 微調位置掌握這些概念,即可輕鬆構建專業級的 SWT/JFace 桌面應用介面。