SWT 標準佈局管理器(FillLayout、RowLayout、GridLayout、FormLayout)功能強大,但缺乏類似 AWT/Swing BorderLayout 的五區域(北、南、東、西、中)佈局模型。本文實現一個完整的 BorderLayout 移植版,支援區域約束、元件動態調整、最佳尺寸計算等核心功能。
public class BorderLayout extends Layout {
// 五大區域常數
public static final int NORTH = 0;
public static final int SOUTH = 1;
public static final int CENTER = 2;
public static final int EAST = 3;
public static final int WEST = 4;
// 佈局約束資料類別
public static class BorderData {
public int region = CENTER;
public BorderData() {}
public BorderData(int region) { this.region = region; }
}
// 內部狀態
private Control[] controls = new Control[5]; // 五區域控制項
private Point[] sizes; // 各區域最佳尺寸
private int width, height; // 整體最佳尺寸
}
@Override
protected Point computeSize(Composite composite, int wHint, int hHint, boolean flushCache) {
if (sizes == null || flushCache) refreshSizes(composite.getChildren());
int w = (wHint == SWT.DEFAULT) ? width : wHint;
int h = (hHint == SWT.DEFAULT) ? height : hHint;
return new Point(w, h);
}
@Override
protected void layout(Composite composite, boolean flushCache) {
if (flushCache || sizes == null) refreshSizes(composite.getChildren());
Rectangle area = composite.getClientArea();
// 北區:頂部橫跨全寬
if (controls[NORTH] != null) {
controls[NORTH].setBounds(area.x, area.y, area.width, sizes[NORTH].y);
}
// 南區:底部橫跨全寬
if (controls[SOUTH] != null) {
controls[SOUTH].setBounds(
area.x,
area.y + area.height - sizes[SOUTH].y,
area.width,
sizes[SOUTH].y
);
}
// 西區:左側垂直佔據中間高度
if (controls[WEST] != null) {
controls[WEST].setBounds(
area.x,
area.y + sizes[NORTH].y,
sizes[WEST].x,
area.height - sizes[NORTH].y - sizes[SOUTH].y
);
}
// 東區:右側垂直佔據中間高度
if (controls[EAST] != null) {
controls[EAST].setBounds(
area.x + area.width - sizes[EAST].x,
area.y + sizes[NORTH].y,
sizes[EAST].x,
area.height - sizes[NORTH].y - sizes[SOUTH].y
);
}
// 中區:填滿剩餘空間
if (controls[CENTER] != null) {
controls[CENTER].setBounds(
area.x + sizes[WEST].x,
area.y + sizes[NORTH].y,
area.width - sizes[WEST].x - sizes[EAST].x,
area.height - sizes[NORTH].y - sizes[SOUTH].y
);
}
}
private void refreshSizes(Control[] children) {
// 重置控制項映射
Arrays.fill(controls, null);
// 解析佈局約束
for (Control child : children) {
Object data = child.getLayoutData();
if (data instanceof BorderData) {
BorderData bd = (BorderData) data;
if (bd.region >= 0 && bd.region <= 4) {
controls[bd.region] = child;
}
}
}
// 計算各區域最佳尺寸
if (sizes == null) sizes = new Point[5];
width = height = 0;
for (int i = 0; i < 5; i++) {
if (controls[i] != null) {
sizes[i] = controls[i].computeSize(SWT.DEFAULT, SWT.DEFAULT, true);
} else {
sizes[i] = new Point(0, 0);
}
}
// 整體寬度 = max(北寬, 西寬+中寬+東寬, 南寬)
width = Math.max(width, sizes[NORTH].x);
width = Math.max(width, sizes[WEST].x + sizes[CENTER].x + sizes[EAST].x);
width = Math.max(width, sizes[SOUTH].x);
// 整體高度 = 北高 + max(西高, 中高, 東高) + 南高
height = sizes[NORTH].y + sizes[SOUTH].y
+ Math.max(sizes[WEST].y, Math.max(sizes[CENTER].y, sizes[EAST].y));
}
public class BorderLayoutDemo {
public static void main(String[] args) {
Display display = new Display();
Shell shell = new Shell(display);
shell.setText("BorderLayout 示範");
shell.setLayout(new BorderLayout());
// 建立五區域按鈕
Button north = new Button(shell, SWT.PUSH);
north.setText("北 - 頂部工具列");
north.setLayoutData(new BorderLayout.BorderData(BorderLayout.NORTH));
Button south = new Button(shell, SWT.PUSH);
south.setText("南 - 狀態列");
south.setLayoutData(new BorderLayout.BorderData(BorderLayout.SOUTH));
Button west = new Button(shell, SWT.PUSH);
west.setText("西 - 導航欄");
west.setLayoutData(new BorderLayout.BorderData(BorderLayout.WEST));
Button east = new Button(shell, SWT.PUSH);
east.setText("東 - 側邊欄");
east.setLayoutData(new BorderLayout.BorderData(BorderLayout.EAST));
Text center = new Text(shell, SWT.MULTI | SWT.BORDER | SWT.V_SCROLL | SWT.H_SCROLL);
center.setText("中 - 內容區域\n支援滾動\n自動填滿剩餘空間");
center.setLayoutData(new BorderLayout.BorderData(BorderLayout.CENTER));
shell.setSize(600, 400);
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) display.sleep();
}
display.dispose();
}
}
public class BorderLayout extends Layout {
private int hGap = 5, vGap = 5; // 水平/垂直間距
private int marginTop = 5, marginBottom = 5, marginLeft = 5, marginRight = 5;
public void setHgap(int hGap) { this.hGap = hGap; }
public void setVgap(int vGap) { this.vGap = vGap; }
public void setMargins(int top, int bottom, int left, int right) { ... }
// layout() 中調整 bounds 計算時加入間距與邊距
}
// 移除西區元件
Control west = controls[WEST];
if (west != null) {
west.dispose(); // 或 west.setVisible(false)
controls[WEST] = null;
parent.layout(true); // 觸發重新佈局
}
這個 BorderLayout 實現完整移植了 AWT/Swing 的經典五區域模型到 SWT 體系,核心優勢:
BorderLayout 高度一致,降低遷移成本。對於需要經典邊界佈局的 SWT/RCP 應用,這是一個開箱即用、生產級的解決方案。