JavaによるデスクトップGUI開発:AWTとSwingの基礎と実践

Java GUIプログラミングの概要

JavaにおけるデスクトップアプリケーションのGUI(グラフィカルユーザーインターフェース)開発は、歴史的にAWT(Abstract Window Toolkit)とSwingの2つの主要なライブラリによって支えられてきました。

現代のWebやモバイルアプリケーションの台頭により、これらのネイティブGUIライブラリが新規プロジェクトで選択される機会は減少しています。その主な要因として、コンポーネントのルックアンドフィールの制限や、JRE(Java Runtime Environment)の配布・インストールが必要であることなどが挙げられます。しかし、Javaのイベント駆動モデルやコンポーネント階層を理解する上で、AWTとSwingの学習は依然として重要な基礎となります。

AWT (Abstract Window Toolkit) の基礎

AWTはJavaで最も初期に導入されたGUIツールキットであり、java.awtパッケージに属しています。OSネイティブのコンポーネント(Heavyweightコンポーネント)を利用するため、プラットフォーム依存の見た目になります。

ウィンドウとパネルの構成

AWTにおける基本的なウィンドウはFrameクラスで実装されます。また、複数のコンポーネントをグループ化して配置するためにPanelが使用されます。

import java.awt.*;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;

public class BasicWindowDemo {
    public static void main(String[] args) {
        Frame mainWindow = new Frame("AWTウィンドウのデモ");
        mainWindow.setSize(450, 350);
        mainWindow.setLocation(150, 150);
        mainWindow.setBackground(new Color(60, 120, 180));
        mainWindow.setResizable(false);
        
        // ウィンドウの閉じるボタンを有効化
        mainWindow.addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent e) {
                System.exit(0);
            }
        });
        
        mainWindow.setVisible(true);
    }
}

レイアウトマネージャ

コンポーネントの配置を制御するために、AWTではレイアウトマネージャが提供されています。

  • FlowLayout: コンポーネントを左から右へ、スペースがなくなれば次の行へ折り返して配置します。
  • BorderLayout: 領域を東西南北中央(EAST, WEST, SOUTH, NORTH, CENTER)の5つに分割して配置します。
  • GridLayout: 指定された行数と列数のグリッド(格子)状に均等に配置します。
import java.awt.*;

public class LayoutDemonstration {
    public static void main(String[] args) {
        Frame gridWindow = new Frame("GridLayoutの例");
        gridWindow.setLayout(new GridLayout(3, 2, 10, 10)); // 3行2列、隙間10px

        for (int i = 1; i <= 6; i++) {
            gridWindow.add(new Button("ボタン " + i));
        }

        gridWindow.pack();
        gridWindow.setVisible(true);
    }
}

イベントリスナとテキスト入力

ユーザーの操作(クリックやキー入力など)を捉えるためにイベントリスナを登録します。

import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

public class TextFieldEventDemo {
    public static void main(String[] args) {
        Frame inputFrame = new Frame("テキスト入力イベント");
        TextField secretInput = new TextField(20);
        secretInput.setEchoChar('*'); // マスク文字の設定
        
        secretInput.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                TextField source = (TextField) e.getSource();
                System.out.println("入力された値: " + source.getText());
                source.setText(""); // 入力フィールドをクリア
            }
        });

        inputFrame.add(secretInput);
        inputFrame.pack();
        inputFrame.setVisible(true);
    }
}

面向对象设计による簡易計算機

GUIアプリケーションでは、コンポーネントとイベント処理を適切にカプセル化することが重要です。内部クラスを使用することで、外部クラスのUIコンポーネントに直接アクセスしやすくなります。

import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

public class SimpleCalculatorApp {
    public static void main(String[] args) {
        new CalculatorFrame().buildUI();
    }
}

class CalculatorFrame extends Frame {
    private TextField operandA;
    private TextField operandB;
    private TextField resultField;

    public void buildUI() {
        operandA = new TextField(8);
        operandB = new TextField(8);
        resultField = new TextField(15);
        resultField.setEditable(false);

        Button calcButton = new Button("計算");
        // 内部クラスをリスナとして登録
        calcButton.addActionListener(new CalculationHandler());

        setLayout(new FlowLayout(FlowLayout.CENTER, 10, 10));
        add(operandA);
        add(new Label("+"));
        add(operandB);
        add(calcButton);
        add(resultField);

        pack();
        setVisible(true);
    }

    // 内部クラスによるイベント処理
    private class CalculationHandler implements ActionListener {
        @Override
        public void actionPerformed(ActionEvent e) {
            try {
                int valA = Integer.parseInt(operandA.getText());
                int valB = Integer.parseInt(operandB.getText());
                resultField.setText(String.valueOf(valA + valB));
            } catch (NumberFormatException ex) {
                resultField.setText("エラー");
            }
        }
    }
}

グラフィックスとマウス・キーボード制御

Graphicsオブジェクトを使用すると、ウィンドウ上に図形を描画できます。また、マウスやキーボードのイベントをリッスンすることで、インタラクティブな描画アプリケーションが実現可能です。

import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.List;

public class InteractiveCanvas {
    public static void main(String[] args) {
        new DrawingFrame();
    }
}

class DrawingFrame extends Frame {
    private final List<Point> clickPoints = new ArrayList<>();

    public DrawingFrame() {
        setTitle("マウス描画デモ");
        setSize(400, 300);
        
        addMouseListener(new MouseAdapter() {
            @Override
            public void mousePressed(MouseEvent e) {
                clickPoints.add(new Point(e.getX(), e.getY()));
                repaint(); // 再描画をトリガー
            }
        });
        
        setVisible(true);
    }

    @Override
    public void paint(Graphics g) {
        g.setColor(Color.MAGENTA);
        for (Point p : clickPoints) {
            g.fillOval(p.x - 5, p.y - 5, 10, 10);
        }
    }
}

SwingによるモダンなGUI開発

Swing(javax.swingパッケージ)は、AWTのコンポーネントを拡張した軽量コンポーネント(Lightweightコンポーネント)ライブラリです。OSに依存しない描画を行うため、クロスプラットフォームで一貫したルックアンドフィールを提供します。

JFrameとコンテナの操作

SwingのトップレベルウィンドウはJFrameです。スレッドセーフリティの観点から、Swingコンポーネントの生成と更新はイベントディスパッチスレッド(EDT)上で行う必要があります。

import javax.swing.*;
import java.awt.*;

public class SwingWindowExample {
    public static void main(String[] args) {
        // EDT上でUIを初期化
        SwingUtilities.invokeLater(() -> {
            JFrame mainFrame = new JFrame("Swingアプリケーション");
            mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            mainFrame.setSize(400, 300);

            Container contentPane = mainFrame.getContentPane();
            contentPane.setBackground(Color.LIGHT_GRAY);
            contentPane.setLayout(new BorderLayout());

            JLabel infoLabel = new JLabel("Swingコンポーネントのデモ", SwingConstants.CENTER);
            infoLabel.setFont(new Font("SansSerif", Font.BOLD, 18));
            contentPane.add(infoLabel, BorderLayout.CENTER);

            mainFrame.setVisible(true);
        });
    }
}

ダイアログ、アイコン、各種ボタン

Swingでは、ポップアップウィンドウ(JDialog)、アイコン付きのラベルやボタン、ラジオボタン、チェックボックスなどが豊富に用意されています。

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;

public class ComponentShowcase extends JFrame {
    public ComponentShowcase() {
        setTitle("Swingコンポーネント集");
        setSize(350, 250);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setLayout(new FlowLayout());

        JButton dialogButton = new JButton("ダイアログ表示");
        dialogButton.addActionListener(e -> showCustomDialog());

        JRadioButton radioA = new JRadioButton("オプションA");
        JRadioButton radioB = new JRadioButton("オプションB");
        ButtonGroup radioGroup = new ButtonGroup();
        radioGroup.add(radioA);
        radioGroup.add(radioB);

        JCheckBox checkOption = new JCheckBox("同意する");

        add(dialogButton);
        add(radioA);
        add(radioB);
        add(checkOption);
    }

    private void showCustomDialog() {
        JDialog dialog = new JDialog(this, "カスタムポップアップ", true);
        dialog.setSize(200, 100);
        dialog.setLayout(new FlowLayout());
        dialog.add(new JLabel("これはモーダルダイアログです"));
        dialog.setLocationRelativeTo(this);
        dialog.setVisible(true);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> new ComponentShowcase().setVisible(true));
    }
}

リストとテキストコンポーネント

データの選択にはJComboBox(ドロップダウン)やJListが、テキストの入力にはJTextFieldJPasswordField、複数行のJTextAreaが使用されます。

import javax.swing.*;
import java.awt.*;
import java.util.Arrays;

public class TextAndListDemo extends JFrame {
    public TextAndListDemo() {
        setTitle("テキストとリスト");
        setSize(300, 200);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setLayout(new GridLayout(3, 1, 5, 5));

        JComboBox<String> statusBox = new JComboBox<>(new String[]{"進行中", "完了", "保留"});
        
        JList<String> userList = new JList<>(new String[]{"ユーザー1", "ユーザー2", "ユーザー3"});
        JScrollPane listScroll = new JScrollPane(userList);

        JTextArea logArea = new JTextArea("システムログ...\n");
        JScrollPane logScroll = new JScrollPane(logArea);

        add(statusBox);
        add(listScroll);
        add(logScroll);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> new TextAndListDemo().setVisible(true));
    }
}

GUIアニメーションとゲームループの基礎

GUIアプリケーションでアニメーションやゲーム(例:テトリスやスネークゲーム)を実装する場合、フレームレートを制御するためのタイマー機構が不可欠です。Swingではjavax.swing.Timerを使用することで、EDTをブロックせずに定期的にUIの更新(再描画)を行うことができます。

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

public class AnimationTimerDemo extends JPanel {
    private int shapeX = 0;
    private int velocity = 4;

    public AnimationTimerDemo() {
        // 30ミリ秒ごとにアクションイベントを発生させるタイマー
        Timer frameTimer = new Timer(30, new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                shapeX += velocity;
                // 画面端でのバウンド処理
                if (shapeX > getWidth() - 40 || shapeX < 0) {
                    velocity = -velocity;
                }
                repaint(); // 描画メソッドを呼び出し
            }
        });
        frameTimer.start();
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        g.setColor(Color.CYAN);
        g.fillOval(shapeX, 50, 40, 40);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> {
            JFrame frame = new JFrame("アニメーションループ");
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.add(new AnimationTimerDemo());
            frame.setSize(400, 200);
            frame.setVisible(true);
        });
    }
}

タグ: Java AWT Swing GUI EventHandling

8月10日 04:24 投稿