首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

SWT/JFace FormLayout 佈局管理器詳解:精準控制元件位置與關聯

     編輯:Java 相關知識

前言

FormLayout 是 SWT 中最靈活、功能最強大的佈局管理器。它不依賴固定網格,而是透過 FormAttachment 讓元件相對於父容器邊緣或其他元件進行精確定位。這使其成為構建複雜、響應式介面(如對話框、屬性頁、工具欄)的首選。

核心類別

  • FormLayout:容器佈局管理器,管理邊距(marginWidth、marginHeight)。
  • FormData:每個子元件的佈局資料,持有四個 FormAttachment(left, right, top, bottom)。
  • FormAttachment:定義單一邊緣的附著規則。

FormAttachment 三種附著模式

建構子參數含義
new FormAttachment(int numerator)分子 (0~100)附著於父容器對應邊緣的百分比位置。
例:new FormAttachment(20) = 距左/上 20%。
new FormAttachment(Control control, int offset)參照元件, 偏移像素附著於指定元件的對應邊緣(left 附著 left, top 附著 top),並偏移 offset 像素。
new FormAttachment(Control control, int offset, int alignment)參照元件, 偏移, 對齊常數附著於指定元件的指定邊緣。
alignment: SWT.LEFT, RIGHT, TOP, BOTTOM, CENTER。
例:new FormAttachment(btn, 0, SWT.CENTER) 置中對齊 btn。

實戰範例 1:基礎相對定位

public class FormLayoutSample {
    public static void main(String[] args) {
        Display display = new Display();
        Shell shell = new Shell(display);
        shell.setLayout(new FormLayout());

        Button btn1 = new Button(shell, SWT.PUSH);
        btn1.setText("button1");
        FormData fd1 = new FormData();
        fd1.left = new FormAttachment(20);   // 距左 20%
        fd1.top = new FormAttachment(20);    // 距上 20%
        btn1.setLayoutData(fd1);

        Button btn2 = new Button(shell, SWT.PUSH);
        btn2.setText("button number 2");
        FormData fd2 = new FormData();
        // btn2 左邊緣 附著於 btn1 水平置中線,偏移 0
        fd2.left = new FormAttachment(btn1, 0, SWT.CENTER);
        // btn2 上邊緣 附著於 btn1 垂直置中線,偏移 0
        fd2.top = new FormAttachment(btn1, 0, SWT.CENTER);
        btn2.setLayoutData(fd2);

        shell.pack(); shell.open();
        // ... 事件循環
    }
}

結果:btn2 置中覆蓋在 btn1 上方(因兩者置中點相同)。

實戰範例 2:標準對話框佈局(表單+列表+按鈕)

public class FormLayoutDialog {
    public static void main(String[] args) {
        Display display = new Display();
        Shell shell = new Shell(display);
        shell.setText("FormLayout Demo");

        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", "Item 2", "Item 3", "Item 4"});

        Button ok = new Button(shell, SWT.PUSH);
        ok.setText("OK");
        Button cancel = new Button(shell, SWT.PUSH);
        cancel.setText("Cancel");

        FormLayout layout = new FormLayout();
        layout.marginWidth = 4;
        layout.marginHeight = 4;
        shell.setLayout(layout);

        final int inset = 4;

        // Label: 置頂,左右滿版
        FormData labelData = new FormData();
        labelData.left = new FormAttachment(0, 0);
        labelData.right = new FormAttachment(100, 0);
        label.setLayoutData(labelData);

        // 動態調整 Label 寬度(換行支援)
        shell.addListener(SWT.Resize, e -> {
            Rectangle area = shell.getClientArea();
            labelData.width = area.width - inset * 2;
            shell.layout();
        });

        // Cancel: 右下角
        FormData cancelData = new FormData();
        cancelData.right = new FormAttachment(100, -inset);
        cancelData.bottom = new FormAttachment(100, 0);
        cancel.setLayoutData(cancelData);

        // OK: Cancel 左側
        FormData okData = new FormData();
        okData.right = new FormAttachment(cancel, -inset);
        okData.bottom = new FormAttachment(100, 0);
        ok.setLayoutData(okData);

        // List: 填滿 Label 下方到按鈕上方
        FormData listData = new FormData();
        listData.left = new FormAttachment(0, 0);
        listData.right = new FormAttachment(100, 0);
        listData.top = new FormAttachment(label, inset);
        listData.bottom = new FormAttachment(cancel, -inset);
        list.setLayoutData(listData);

        shell.pack(); shell.open();
        // ... 事件循環
    }
}

FormAttachment 進階用法

1. 百分比與像素混合

// 左邊緣:父容器 30% + 10 像素
fd.left = new FormAttachment(30, 10);

// 右邊緣:父容器 70% - 5 像素
fd.right = new FormAttachment(70, -5);

2. 連接鏈:多元件串聯

// btnA -> btnB -> btnC 水平排列,等距 5px
fdA.right = new FormAttachment(btnB, -5);
fdB.left = new FormAttachment(btnA, 5);
fdB.right = new FormAttachment(btnC, -5);
fdC.left = new FormAttachment(btnB, 5);

3. 置中於另一元件

// child 置中於 parent 內部
fd.left = new FormAttachment(parent, 0, SWT.CENTER);
fd.top = new FormAttachment(parent, 0, SWT.CENTER);

4. 固定寬高(結合 left/right 或 top/bottom)

// 固定寬 200px:left 固定,right = left + 200
fd.left = new FormAttachment(10, 0);
fd.right = new FormAttachment(fd.left, 200); // 錯誤!FormAttachment 不能參照同一 FormData

// 正確做法:使用 widthHint 或 計算右邊緣百分比
// 或:在 Resize 事件中動態計算

FormLayout vs GridLayout 選擇指南

場景推薦佈局理由
表單、屬性頁、對話框FormLayout精確控制標籤-輸入框對齊、按鈕固定底部、中間區域自適應
工具列、狀態列FormLayout左/中/右區域獨立控制
規則網格、表格狀介面GridLayout行列結構清晰,span/grab 機制直覺
動態增減元件GridLayout自動換行、重新排列較簡單
複雜重疊/置中需求FormLayoutSWT.CENTER 附著唯一支援

常見陷阱與除錯技巧

  1. 循環依賴:A.left 附著 B.right,B.left 附著 A.right → 佈局失敗。檢查附著鏈向。
  2. 未設定足夠約束:元件需至少指定水平兩邊(left/right)與垂直兩邊(top/bottom)其中各一,否則尺寸為 0。
  3. Resize 時 Label 不換行:必須監聽 SWT.Resize 更新 FormData.width 並呼叫 layout()。
  4. 百分比誤區:new FormAttachment(50) 是指父容器 50% 處,而非元件中心。

總結

FormLayout 以「附著」概念提供像素級的佈局控制能力:

  • 相對父容器:百分比定位,響應式縮放基石。
  • 相對同層元件:邊緣對齊、置中、固定間距,構建穩定相對關係。
  • 動態調整:配合 ControlListener 即時重算 FormData。

掌握 FormAttachment 三種建構模式,即可駕馭最複雜的 SWT 介面佈局需求。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved