主視窗 (`MainFrame`) 點擊「登入」按鈕 → 顯示登入視窗 (`LoginDialog`) → 登入成功 → 將用戶資訊回傳主視窗並顯示 → 關閉登入視窗。
登入視窗不直接操作主視窗元件,而是定義介面,主視窗實作介面並傳遞自身引用(`this`)。
public interface LoginListener {
void onLoginSuccess(User user); // 登入成功回調
void onLoginCancel(); // 取消/關閉回調
}
public class LoginDialog extends JDialog {
private LoginListener listener;
private JTextField tfUser;
private JPasswordField pfPass;
public LoginDialog(Frame owner, LoginListener listener) {
super(owner, "用戶登入", true); // true = 模態對話框,阻塞主視窗
this.listener = listener;
initUI();
pack();
setLocationRelativeTo(owner);
}
private void initUI() {
// 佈局代碼省略...
btnLogin.addActionListener(e -> attemptLogin());
btnCancel.addActionListener(e -> { if (listener != null) listener.onLoginCancel(); dispose(); });
}
private void attemptLogin() {
String user = tfUser.getText();
String pass = new String(pfPass.getPassword());
// 這裡應呼叫 Service 驗證,示例寫死
if ("admin".equals(user) && "123456".equals(pass)) {
User u = new User(user, "管理員");
if (listener != null) listener.onLoginSuccess(u);
dispose();
} else {
JOptionPane.showMessageDialog(this, "帳號或密碼錯誤");
}
}
}
public class MainFrame extends JFrame implements LoginListener {
private JLabel lblStatus;
public MainFrame() {
initUI();
}
private void initUI() {
lblStatus = new JLabel("未登入");
JButton btnLogin = new JButton("登入");
btnLogin.addActionListener(e -> new LoginDialog(this, this).setVisible(true));
// 佈局...
}
@Override
public void onLoginSuccess(User user) {
lblStatus.setText("歡迎:" + user.getName() + " (" + user.getRole() + ")");
// 可在此啟用需登入的功能按鈕等
}
@Override
public void onLoginCancel() {
// 使用者取消,可記錄日誌或不處理
}
}
public class User {
private String username;
private String role;
// 建構子、getter/setter 省略
}
| 要點 | 說明 |
|---|---|
| 模態對話框 | `super(owner, title, true)` 阻塞主視窗互動,登入流程結束前無法操作主視窗 |
| 介面解耦 | `LoginDialog` 僅依賴 `LoginListener` 介面,不依賴 `MainFrame` 具體類,可復用於其他視窗 |
| 傳遞 this | `new LoginDialog(this, this)` 第一個 `this` 是 owner(父視窗),第二個是 listener(回調實作) |
| 線程安全 | Swing 事件派發執行緒 (EDT) 中執行,UI 更新安全;若驗證耗時,需 `SwingWorker` 異步處理 |