Skip to content
Code Blue プログラミング学習ノート
Java/Swing

【Swingアプリ開発】メモ帳アプリの解答例

前回のメモ帳アプリの解答例を紹介した内容になります。メモ帳の問題形式については前回の記事を参照してください。

以下は、Swingを使用した基本的なメモ帳アプリケーションの完全な実装例です。ファイルの新規作成、開く、保存、終了機能と基本的なテキスト編集機能を備えています。

テキストの入力や編集だけでなく、ファイルの新規作成・保存・読み込み・コピー・貼り付けなど、実際のメモ帳に近い機能を実装しています。また、メニューバーやショートカットキー、ファイル選択ダイアログなど、Swingアプリケーションでよく利用される機能も含まれてます。

import javax.swing.*;
import javax.swing.filechooser.FileNameExtensionFilter;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.io.*;

public class SimpleTextEditor {
    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> {
            // メインウィンドウの作成
            JFrame frame = new JFrame("シンプルメモ帳");
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.setSize(800, 600);

            // テキスト編集領域
            JTextArea textArea = new JTextArea();
            textArea.setFont(new Font("Meiryo", Font.PLAIN, 14));
            JScrollPane scrollPane = new JScrollPane(textArea);
            frame.add(scrollPane, BorderLayout.CENTER);

            // メニューバーの作成
            JMenuBar menuBar = new JMenuBar();

            // ファイルメニュー
            JMenu fileMenu = new JMenu("ファイル");

            JMenuItem newItem = new JMenuItem("新規作成");
            JMenuItem openItem = new JMenuItem("開く...");
            JMenuItem saveItem = new JMenuItem("保存...");
            JMenuItem saveAsItem = new JMenuItem("名前を付けて保存...");
            JMenuItem exitItem = new JMenuItem("終了");

            // 編集メニュー
            JMenu editMenu = new JMenu("編集");

            JMenuItem cutItem = new JMenuItem("切り取り");
            JMenuItem copyItem = new JMenuItem("コピー");
            JMenuItem pasteItem = new JMenuItem("貼り付け");

            // メニュー項目をメニューに追加
            fileMenu.add(newItem);
            fileMenu.add(openItem);
            fileMenu.addSeparator();
            fileMenu.add(saveItem);
            fileMenu.add(saveAsItem);
            fileMenu.addSeparator();
            fileMenu.add(exitItem);

            editMenu.add(cutItem);
            editMenu.add(copyItem);
            editMenu.add(pasteItem);

            // メニューをメニューバーに追加
            menuBar.add(fileMenu);
            menuBar.add(editMenu);

            // フレームにメニューバーを設定
            frame.setJMenuBar(menuBar);

            // 現在編集中のファイル
            File currentFile = null;

            // アクションの実装
            newItem.addActionListener(e -> {
                textArea.setText("");
                currentFile = null;
                frame.setTitle("シンプルメモ帳");
            });

            openItem.addActionListener(e -> {
                JFileChooser fileChooser = new JFileChooser();
                fileChooser.setFileFilter(new FileNameExtensionFilter("テキストファイル (*.txt)", "txt"));

                if (fileChooser.showOpenDialog(frame) == JFileChooser.APPROVE_OPTION) {
                    currentFile = fileChooser.getSelectedFile();
                    frame.setTitle("シンプルメモ帳 - " + currentFile.getName());

                    try (BufferedReader reader = new BufferedReader(new FileReader(currentFile))) {
                        StringBuilder content = new StringBuilder();
                        String line;
                        while ((line = reader.readLine()) != null) {
                            content.append(line).append("\n");
                        }
                        textArea.setText(content.toString());
                    } catch (IOException ex) {
                        JOptionPane.showMessageDialog(frame, 
                            "ファイルを開けませんでした", 
                            "エラー", 
                            JOptionPane.ERROR_MESSAGE);
                    }
                }
            });

            Action saveAction = new AbstractAction() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    if (currentFile == null) {
                        // 名前を付けて保存を実行
                        JFileChooser fileChooser = new JFileChooser();
                        fileChooser.setFileFilter(new FileNameExtensionFilter("テキストファイル (*.txt)", "txt"));

                        if (fileChooser.showSaveDialog(frame) == JFileChooser.APPROVE_OPTION) {
                            currentFile = fileChooser.getSelectedFile();
                            if (!currentFile.getName().endsWith(".txt")) {
                                currentFile = new File(currentFile.getAbsolutePath() + ".txt");
                            }
                            frame.setTitle("シンプルメモ帳 - " + currentFile.getName());
                            saveToFile();
                        }
                    } else {
                        saveToFile();
                    }
                }

                private void saveToFile() {
                    try (BufferedWriter writer = new BufferedWriter(new FileWriter(currentFile))) {
                        writer.write(textArea.getText());
                    } catch (IOException ex) {
                        JOptionPane.showMessageDialog(frame, 
                            "ファイルを保存できませんでした", 
                            "エラー", 
                            JOptionPane.ERROR_MESSAGE);
                    }
                }
            };

            saveItem.setAction(saveAction);
            saveAsItem.addActionListener(e -> {
                currentFile = null;
                saveAction.actionPerformed(e);
            });

            exitItem.addActionListener(e -> System.exit(0));

            // 編集メニューのアクション
            cutItem.addActionListener(e -> textArea.cut());
            copyItem.addActionListener(e -> textArea.copy());
            pasteItem.addActionListener(e -> textArea.paste());

            // ショートカットキーの設定
            newItem.setAccelerator(KeyStroke.getKeyStroke("control N"));
            openItem.setAccelerator(KeyStroke.getKeyStroke("control O"));
            saveItem.setAccelerator(KeyStroke.getKeyStroke("control S"));
            saveAsItem.setAccelerator(KeyStroke.getKeyStroke("control shift S"));

            cutItem.setAccelerator(KeyStroke.getKeyStroke("control X"));
            copyItem.setAccelerator(KeyStroke.getKeyStroke("control C"));
            pasteItem.setAccelerator(KeyStroke.getKeyStroke("control V"));

            frame.setVisible(true);
        });
    }
}

このメモ帳アプリの特徴

以下はメモ帳アプリの特徴の詳細について言及します。

メインウィンドウの作成

JFrameを作成し、アプリケーションのメインウィンドウとして利用しています。中央にはJTextAreaを配置して文章を入力できるようにし、JScrollPaneで囲むことで、入力内容が画面を超えた場合でもスクロールしながら編集できるようにしています。

メニューバーの作成

JMenuBarを利用してメニューバーを作成しています。「ファイル」メニューには新規作成・開く・保存・名前を付けて保存・終了を配置し、「編集」メニューには切り取り・コピー・貼り付けを配置しています。メニューを利用することで、さまざまな操作を分かりやすく実行できるようになります。

新規作成機能

「新規作成」を選択すると、JTextAreaの内容を空にし、現在編集中のファイル情報も初期化します。また、ウィンドウタイトルを元の状態に戻すことで、新しいファイルを編集していることが分かるようにしています。

ファイルを開く機能

「開く」を選択するとJFileChooserが表示され、テキストファイルを選択できます。選択したファイルはBufferedReaderを使って1行ずつ読み込み、その内容をJTextAreaへ表示します。ファイルの読み込みに失敗した場合は、JOptionPaneでエラーメッセージを表示し、利用者に問題を知らせます。

ファイル保存機能

保存処理はAbstractActionとしてまとめられています。初めて保存する場合は保存先を選択するダイアログを表示し、すでに保存済みのファイルであれば同じ場所へ上書き保存します。保存処理にはBufferedWriterを使用し、JTextAreaに入力された内容をテキストファイルへ書き込みます。また、拡張子が入力されていない場合は、自動的に.txtを付与しています。

名前を付けて保存

「名前を付けて保存」を選択すると、現在のファイル情報を一度リセットし、保存先を新しく選択できるようにしています。同じ内容を別の名前で保存したい場合に利用できる機能です。

編集機能

「切り取り」「コピー」「貼り付け」は、JTextAreaが標準で持つcut()、copy()、paste()メソッドを利用しています。これらのメソッドを呼び出すだけで、一般的なテキストエディタと同じ編集機能を簡単に実装できます。

ショートカットキーの設定

各メニューにはsetAccelerator()を使用してショートカットキーを設定しています。例えば、Ctrl + Nで新規作成、Ctrl + Oでファイルを開く、Ctrl + Sで保存を実行できます。マウスだけでなくキーボードからも操作できるため、アプリケーションの操作性が向上します。

基本機能

  • ファイル操作: 新規作成、開く、保存、名前を付けて保存
  • テキスト編集: 切り取り、コピー、貼り付け
  • 終了機能

UI構成

  • メニューバーに各種機能を配置
  • スクロール可能なテキスト編集領域
  • ファイルダイアログを使用したファイル操作

便利な機能

  • キーボードショートカットのサポート
  • 編集中のファイル名をタイトルバーに表示
  • テキストファイルフィルタの適用

拡張可能なポイント

この基本実装をさらに発展させるためのアイデアをまとめました。

追加機能

  • 検索/置換機能
  • フォント設定の変更
  • 印刷機能
  • アンドゥ/リドゥ機能

UI改善

  • ツールバーの追加
  • ステータスバー(行数、文字数表示)
  • タブによる複数ファイル編集

コードの改良

  • コントローラクラスの導入(MVCパターン)
  • 最近使ったファイルリストの管理
  • 設定の永続化(Preferences APIの使用)

例えば、フォント変更機能を追加するには以下のコードの修正により実現します。

// メニューにフォント項目を追加
JMenu formatMenu = new JMenu("書式");
JMenuItem fontItem = new JMenuItem("フォント...");
formatMenu.add(fontItem);
menuBar.add(formatMenu);

// フォントダイアログの実装
fontItem.addActionListener(e -> {
    Font currentFont = textArea.getFont();
    FontDialog fontDialog = new FontDialog(frame, currentFont);
    fontDialog.setVisible(true);

    if (fontDialog.isApproved()) {
        textArea.setFont(fontDialog.getSelectedFont());
    }
});

// FontDialogクラスの簡易実装
class FontDialog extends JDialog {
    private boolean approved = false;
    private Font selectedFont;

    public FontDialog(Frame owner, Font initialFont) {
        super(owner, "フォントの選択", true);
        setSize(300, 200);

        // フォント選択UIの実装...
        // サンプル実装(簡略化)
        JButton okButton = new JButton("OK");
        okButton.addActionListener(e -> {
            approved = true;
            selectedFont = initialFont; // 実際にはダイアログから選択したフォントを設定
            dispose();
        });

        JButton cancelButton = new JButton("キャンセル");
        cancelButton.addActionListener(e -> dispose());

        add(okButton, BorderLayout.SOUTH);
    }

    public boolean isApproved() { return approved; }
    public Font getSelectedFont() { return selectedFont; }
}

このメモ帳アプリは、Swingの基本的なコンポーネントとファイル操作、メニューシステムを理解するのに最適なサンプルです。基本機能を理解したら、段階的に機能を追加していくことをおすすめします。