はじめに (対象読者・この記事でわかること)
この記事は、Java のデスクトップアプリ開発に興味がある初心者から中級者を対象にしています。Swing を用いた GUI プログラミングは経験が浅い方でも取り組みやすいですが、画面遷移(画面切り替え)を正しく実装するにはいくつかのコツが必要です。本記事を読むことで、以下ができるようになります。
- 複数の JFrame / JPanel を組み合わせた画面構成を設計できる
- CardLayout や自作の画面遷移マネージャを使ってスムーズに画面を切り替える方法がわかる
- 画面遷移時に起こりがちなエラーや「画面がちらつく」問題の原因と対策を理解できる
Swing での画面遷移は、Web アプリのルーティング感覚で考えるとイメージしやすく、実務でも頻繁に使用されます。ぜひ本文を参考に、実際に手を動かしながら習得してください。
前提知識
この記事を読み進める上で、以下の知識があるとスムーズです。
- Java 基本文法(クラス、メソッド、継承など)の理解
- 基本的な Swing コンポーネント(JFrame、JPanel、JButton など)の使用経験
- IDE(IntelliJ IDEA、Eclipse 等)でのプロジェクト作成とビルドの流れ
1. Swing における画面遷移の基本概念と選択肢
Swing で画面遷移を実装する際に主に選択できるパターンは大きく3つです。
-
複数の JFrame を切り替える
それぞれ独立したウィンドウとして実装し、必要に応じてsetVisible(true/false)で表示/非表示を制御します。シンプルですが、ウィンドウが増えると管理が煩雑になりがちです。 -
単一の JFrame 内に複数の JPanel を配置し、表示切替える
CardLayoutを利用すれば、カード(パネル)をスタック状に管理でき、show()メソッドで簡単に切り替えられます。最も一般的で、ウィンドウは1つだけなのでユーザー体験が統一されます。 -
独自の画面遷移マネージャを作成
アプリ規模が大きくなると、遷移ロジックや状態管理が複雑になるため、専用クラスで遷移を抽象化します。MVC/MVP パターンと組み合わせると保守性が向上します。
本稿では、実務で多用される CardLayout を中心に解説し、最後に簡易的な遷移マネージャの実装例も紹介します。
2. 実装ステップ:CardLayout と遷移マネージャで画面遷移を作る
以下のサンプルは、ログイン画面 → メイン画面 → 設定画面 の 3 つの画面を持つシンプルなアプリです。コードは Java 11 以降で動作しますが、IDE があれば 8 でも問題ありません。
2-1. プロジェクト構成と必要クラス
src/
└─ com/example/swingapp/
├─ Main.java // エントリーポイント
├─ AppFrame.java // JFrame と CardLayout の管理
├─ NavigationManager.java // 画面遷移用シングルトン
├─ view/
│ ├─ LoginPanel.java
│ ├─ MainPanel.java
│ └─ SettingsPanel.java
└─ controller/
└─ AuthController.java
- Main.java:
SwingUtilities.invokeLaterで UI スレッドを確保し、AppFrameを表示。 - AppFrame.java:
JFrameのサブクラスで、CardLayoutとコンテナcardPanelを保持。 - NavigationManager.java: 画面遷移の API(
navigateTo(String name))を提供し、どこからでも遷移を呼び出せるようにする。 - LoginPanel / MainPanel / SettingsPanel: それぞれ UI コンポーネントと簡易ロジックを実装。
2-2. AppFrame の実装
Javapackage com.example.swingapp; import javax.swing.*; import java.awt.*; public class AppFrame extends JFrame { private final JPanel cardPanel; private final CardLayout cardLayout; public AppFrame() { setTitle("Swing 画面遷移デモ"); setDefaultCloseOperation(EXIT_ON_CLOSE); setSize(600, 400); setLocationRelativeTo(null); cardLayout = new CardLayout(); cardPanel = new JPanel(cardLayout); getContentPane().add(cardPanel, BorderLayout.CENTER); // 画面を登録 cardPanel.add(new view.LoginPanel(), "login"); cardPanel.add(new view.MainPanel(), "main"); cardPanel.add(new view.SettingsPanel(), "settings"); // デフォルト画面 cardLayout.show(cardPanel, "login"); } /** NavigationManager から呼び出される内部メソッド */ void showCard(String name) { cardLayout.show(cardPanel, name); } }
ポイント:
CardLayoutのインスタンスはcardPanelにセットし、add(Component, String)で名前付きカードを登録。showCardはパッケージプライベートにし、NavigationManagerからのみアクセスさせることでカプセル化。
2-3. NavigationManager の実装
Javapackage com.example.swingapp; public class NavigationManager { private static NavigationManager instance; private AppFrame appFrame; private NavigationManager() {} public static NavigationManager getInstance() { if (instance == null) { instance = new NavigationManager(); } return instance; } /** AppFrame をセット(AppFrame のコンストラクタから呼び出す) */ void setAppFrame(AppFrame frame) { this.appFrame = frame; } /** 任意の画面へ遷移 */ public void navigateTo(String cardName) { if (appFrame != null) { appFrame.showCard(cardName); } else { throw new IllegalStateException("AppFrame が未設定です"); } } }
- シングルトンにすることで、どのクラスからでも
NavigationManager.getInstance().navigateTo("main")のように呼び出せます。 AppFrameが生成された直後にNavigationManager#setAppFrameで登録します。
2-4. 各パネルの実装例(LoginPanel)
Javapackage com.example.swingapp.view; import com.example.swingapp.NavigationManager; import com.example.swingapp.controller.AuthController; import javax.swing.*; import java.awt.*; public class LoginPanel extends JPanel { private final JTextField userField = new JTextField(15); private final JPasswordField passField = new JPasswordField(15); private final JButton loginBtn = new JButton("ログイン"); public LoginPanel() { setLayout(new GridBagLayout()); GridBagConstraints gbc = new GridBagConstraints(); gbc.insets = new Insets(5,5,5,5); gbc.gridx = 0; gbc.gridy = 0; add(new JLabel("ユーザー名:"), gbc); gbc.gridx = 1; add(userField, gbc); gbc.gridx = 0; gbc.gridy = 1; add(new JLabel("パスワード:"), gbc); gbc.gridx = 1; add(passField, gbc); gbc.gridx = 0; gbc.gridy = 2; gbc.gridwidth = 2; add(loginBtn, gbc); loginBtn.addActionListener(e -> onLogin()); } private void onLogin() { String user = userField.getText(); String pass = new String(passField.getPassword()); if (AuthController.authenticate(user, pass)) { NavigationManager.getInstance().navigateTo("main"); } else { JOptionPane.showMessageDialog(this, "認証に失敗しました", "エラー", JOptionPane.ERROR_MESSAGE); } } }
AuthController.authenticateは簡易的にuser.equals("admin") && pass.equals("1234")で判定するとします。- 認証成功で
NavigationManagerを使い「main」カードへ遷移します。
MainPanel と SettingsPanel も同様に作りますが、ボタン等で navigateTo("settings") や navigateTo("login") を呼び出すだけです。
2-5. Main クラスと起動コード
Javapackage com.example.swingapp; import javax.swing.*; public class Main { public static void main(String[] args) { SwingUtilities.invokeLater(() -> { AppFrame frame = new AppFrame(); // NavigationManager にフレームを登録 NavigationManager.getInstance().setAppFrame(frame); frame.setVisible(true); }); } }
2-6. ハマった点やエラー解決
| 発生した問題 | 原因 | 解決策 |
|---|---|---|
NullPointerException が NavigationManager.navigateTo で発生 |
AppFrame が NavigationManager に登録されていない状態で遷移呼び出しを実行した |
AppFrame の生成直後に必ず NavigationManager.getInstance().setAppFrame(this) を呼び出すようにした |
| 画面切り替え時にコンポーネントがちらつく | CardLayout.show 前に revalidate() と repaint() が呼ばれていなかった |
AppFrame.showCard に cardPanel.revalidate(); cardPanel.repaint(); を追加 |
ログイン画面で JPasswordField の文字列取得が空になる |
new String(passField.getPassword()) を忘れていた |
正しく getPassword() から文字列へ変換するコードを追加 |
IllegalStateException: AppFrame が未設定です が頻発 |
NavigationManager#setAppFrame が二重に呼ばれ、古いインスタンスが残っていた |
シングルトン方式はそのままにし、setAppFrame は最初の呼び出しだけに制限し、二度目は無視するロジックを実装した |
2-7. デバッグのコツ
- ログ出力:
System.out.printlnやjava.util.logging.Loggerを利用し、遷移前後のカード名を出力すると、期待通りに遷移しているかすぐ分かります。 - IDE のブレークポイント:
navigateToメソッド内部にブレークポイントを置き、呼び出しスタックを確認。 - UI スレッドの確認:
SwingUtilities.isEventDispatchThread()がfalseの状態で UI 更新すると例外が出るので、必ずinvokeLater/invokeAndWaitを使用。
2-8. 発展的な実装アイディア
- MVP パターン を導入し、View(JPanel)と Presenter(ロジック)を分離してテスト容易性を向上させる。
- リソース管理: 画像や設定ファイルは
ClassLoader.getResourceAsStreamでロードし、画面切り替え時に再ロードしないようキャッシュする。 - アニメーション遷移:
java.awt.AlphaCompositeやTimingFrameworkを使い、フェードイン・フェードアウト効果を付与すると、モダンな印象になる。
まとめ
本記事では、Swing の CardLayout とシングルトン方式の遷移マネージャを組み合わせた画面遷移実装 をステップバイステップで解説しました。
- カード方式でウィンドウを増やさずに画面切替が可能
- NavigationManager によりどこからでも遷移ロジックを呼び出せ、コードの散在を防止
- ハマりやすいポイント(フレーム未登録、ちらつき、認証ロジック)とその解決策を具体例と共に提示
これらをマスターすれば、シンプルなデスクトップアプリはもちろん、規模が拡大した GUI アプリでも保守しやすい構造を保てます。次回は、MVP パターンとデータバインディング を組み合わせた高度な画面遷移テクニックを紹介する予定です。
参考資料
- Swing Tutorial – Oracle Docs
- CardLayout の公式リファレンス
- 「Java GUI Programming」 (M. L. 佐藤, 2022) – Swing の設計パターン解説
- Swing アプリでの MVC/MVP パターン入門
