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

SWT/JFace StackLayout 佈局管理器:卡片式切換與向導介面實現

     編輯:Java 相關知識

前言

StackLayout 是 SWT 中最簡單的「卡片式」佈局管理器。它將所有子元件疊放在同一位置,同一時間僅顯示一個(topControl)。適合向導嚮導、分頁介面、狀態切換(登入/主介面)、動畫過渡等場景。

核心特性

  • 單顯示:只有 topControl 可見,其餘隱藏(非銷毀)。
  • 無佈局計算:顯示的控件佔滿整個客戶區(如 FillLayout)。
  • 極速切換:僅修改 topControl 呼叫 layout(),無重新計算開銷。
  • 無內建導航:需自行實現切換邏輯(按鈕、選單、手勢)。

API 速覽

StackLayout layout = new StackLayout();
parent.setLayout(layout);

// 切換顯示
layout.topControl = someControl;
parent.layout(true); // 必須呼叫以觸發重新佈局

// 獲取當前顯示
Control current = layout.topControl;

完整範例:按鈕點擊循環切換

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

        StackLayout layout = new StackLayout();
        shell.setLayout(layout);

        final Button[] buttons = new Button[3];
        for (int i = 0; i < buttons.length; i++) {
            buttons[i] = new Button(shell, SWT.NONE);
            buttons[i].setText("Button #" + i);
            
            buttons[i].addSelectionListener(new SelectionListener() {
                public void widgetSelected(SelectionEvent e) {
                    Button source = (Button) e.widget;
                    Button next = null;
                    for (int i = 0; i < buttons.length; i++) {
                        if (buttons[i] == source) {
                            next = (i == buttons.length - 1) ? buttons[0] : buttons[i + 1];
                            break;
                        }
                    }
                    layout.topControl = next;
                    shell.layout(); // 關鍵!
                }
                public void widgetDefaultSelected(SelectionEvent e) {}
            });
        }

        layout.topControl = buttons[0]; // 初始顯示第一個
        shell.setSize(200, 100);
        shell.open();
        while (!shell.isDisposed()) {
            if (!display.readAndDispatch()) display.sleep();
        }
        display.dispose();
    }
}

實戰場景 1:登入/主介面切換

public class LoginMainSwitch {
    private StackLayout layout;
    private Composite loginPanel, mainPanel;
    
    public void createUI(Shell shell) {
        layout = new StackLayout();
        shell.setLayout(layout);
        
        // 登入面板
        loginPanel = new Composite(shell, SWT.NONE);
        loginPanel.setLayout(new GridLayout(2, false));
        // ... 添加用戶名/密碼輸入、登入按鈕 ...
        Button loginBtn = new Button(loginPanel, SWT.PUSH);
        loginBtn.setText("Login");
        loginBtn.addSelectionListener(e -> {
            if (validate()) switchToMain();
        });
        
        // 主面板
        mainPanel = new Composite(shell, SWT.NONE);
        mainPanel.setLayout(new FillLayout());
        // ... 主介面內容 ...
        
        layout.topControl = loginPanel; // 預設顯示登入
    }
    
    private void switchToMain() {
        layout.topControl = mainPanel;
        loginPanel.getParent().layout(true);
    }
    
    private void switchToLogin() {
        layout.topControl = loginPanel;
        mainPanel.getParent().layout(true);
    }
}

實戰場景 2:向導嚮導

public class WizardStack {
    private StackLayout layout;
    private List pages = new ArrayList<>();
    private int currentIndex = 0;
    
    public void createWizard(Shell shell) {
        layout = new StackLayout();
        shell.setLayout(layout);
        
        // 建立多個步驟面板
        for (int i = 0; i < 3; i++) {
            Composite page = new Composite(shell, SWT.NONE);
            page.setLayout(new GridLayout());
            // ... 每頁內容 ...
            pages.add(page);
        }
        
        // 導航按鈕(放在 StackLayout 外層容器底部)
        Composite navBar = new Composite(shell, SWT.NONE);
        navBar.setLayoutData(new GridData(SWT.FILL, SWT.BOTTOM, true, false));
        navBar.setLayout(new RowLayout());
        
        Button back = new Button(navBar, SWT.PUSH);
        back.setText("< Back");
        back.addSelectionListener(e -> goPrevious());
        
        Button next = new Button(navBar, SWT.PUSH);
        next.setText("Next >");
        next.addSelectionListener(e -> goNext());
        
        Button finish = new Button(navBar, SWT.PUSH);
        finish.setText("Finish");
        finish.addSelectionListener(e -> finish());
        
        layout.topControl = pages.get(0);
    }
    
    private void goNext() {
        if (currentIndex < pages.size() - 1) {
            layout.topControl = pages.get(++currentIndex);
            shell.layout(true);
        }
    }
    // ... goPrevious, finish ...
}

StackLayout vs 其他切換方案

方案原理優點缺點適用場景
StackLayout顯示/隱藏極簡、極快、標準 API無動畫、無內建導航向導、狀態切換、簡單分頁
CTabFolder標籤頁內建標籤、關閉、拖拽較重、預設有標籤 UI標準分頁介面
手動 setVisible控件顯隱完全控制需自行管理佈局、佔位空間特殊動畫、重疊效果
ViewPart 切換 (RCP)工作台部件生命週期管理、持久化需 RCP 框架Eclipse RCP 應用

進階技巧

1. 淡入淡出動畫(實驗性)

// 需結合 Alpha 值動畫,SWT 原生不直接支援
// 方案:兩層 Shell 疊加 + setAlpha() 漸變
// 或:使用 Canvas 繪製快照圖片動畫

2. 懶加載頁面

// 頁面多時,僅在首次切換時建立
Control getPage(int index) {
    Control page = pages.get(index);
    if (page == null) {
        page = createPage(index);
        pages.set(index, page);
    }
    return page;
}

3. 狀態保存/恢復

// 切換前保存當前頁面狀態(輸入值、滾動位置)
// 切換回來時恢復
// 可封裝為 IStackPage 接口:saveState()/restoreState()

常見問題

  1. 切換後不顯示:忘記呼叫 parent.layout(true)。
  2. 殘留舊控件:確保只有一個 topControl,未顯示的控件自動隱藏。
  3. 尺寸不正確:父容器需有確定尺寸(pack 或 固定大小),StackLayout 會讓 topControl 填滿客戶區。
  4. 事件洩漏:隱藏的控件仍持有監聽器,大量頁面時考慮移除監聽器或懶加載。

總結

StackLayout 是「單顯示、多疊放」場景的標準解決方案:

  • API 極簡:topControl + layout()。
  • 效能極佳:無佈局計算,單純顯隱切換。
  • 語義清晰:代碼直觀表達「卡片堆疊」概念。
  • 擴展性強:配合懶加載、狀態管理、導航組件,構建完整向導/狀態機介面。

當你需要「一次只看一個、快速切換」時,StackLayout 是首選。

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