
JavaFXを使って、シンプルなエディタアプリを作成する方法を解説します。本記事では、基本的なUI設計からイベント処理の実装まで、実践的なステップを詳しく説明します。初心者でも理解しやすいように、コード付きで解説しているので、ぜひ参考にしてください。
エディタアプリの概要
本アプリは、開発者やライターを対象とした高機能テキストエディタです。シンプルな操作性を保ちつつ、フォントやテーマ機能を提供します。
本アプリの目的
本アプリは、サンプルとして作成したテキストエディタであるため、最低限の機能のみ実装することとします。基本的なテキスト編集機能にフォーカスしています。
ユーザーごとのカスタマイズや拡張機能については、今後のアップデートにて追加される可能性がありますが、現時点では最小限の機能を提供しています。
エディタとして実現する機能一覧
本アプリで提供する主要な機能を以下の表にまとめます。これらの機能により、快適なテキスト編集環境を実現します。
機能 | 概要 |
---|---|
テキスト入力・編集機能 | 基本的なテキストの入力や編集が可能 |
ファイルの読み込み・保存 | 外部ファイルの読み込みおよび編集後の保存 |
フォント・テーマの変更 | フォントの種類やサイズ、エディタのテーマを変更可能 |
自動保存機能 | 一定間隔で自動的に編集中のファイルを保存 |
操作性とUIの要件
- ダークモード対応
- リサイズ可能なレイアウト
- ステータスバーの表示(テーマ変更やフォントサイズ指定など)
エディタアプリの全体設計書の構成と設計要素
本アプリは、シンプルなテキスト編集機能だけを備えた最小限のサンプルエディタです。拡張性やメンテナンス性は考慮していないため、機能追加や修正は予定していません。
設計は極力シンプルにまとめ、最小限のUI構成と機能のみを実装しています。アーキテクチャやデータ管理については特にこだわらず、基本的な動作のみを実現しています。
このアプリは、ユーザー設定やシンタックスハイライトなどの高度な機能には対応しておらず、シンプルなテキスト編集ができるだけのものです。
設計方針
具体的には、MVC(Model-View-Controller)アーキテクチャを採用し、UI(View)、制御(Controller)、データ管理(Model)の役割を明確に分離することで、コードの見通しを良くし、機能の追加や修正を容易にします。また、ユーザーの好みに応じた設定変更を可能にするため、カスタマイズしやすい設定管理を組み込み、フォントやテーマの変更、ショートカットキーのカスタマイズなどを柔軟に対応できるようにします。
これらの設計方針により、開発のしやすさと拡張性を両立させ、実用的かつ持続的に改善可能なエディタアプリを構築します。
設計方針
- シンプルかつ拡張性の高い設計
- MVC(Model-View-Controller)アーキテクチャ
- カスタマイズしやすい設定管理
アプリの主要コンポーネント
エディタアプリの開発においては、機能の拡張や保守性を考慮し、アーキテクチャを適切に設計することが重要です。本アプリでは、View(UI)、Controller(制御)、Model(データ管理)の3つの主要コンポーネントに分割し、それぞれの役割を明確にします。
View(UI) では、ユーザーが直感的に操作できるインターフェースを構築し、エディタエリア、ステータスバーなどの主要なUI要素を配置します。Controller(制御部) では、ユーザーの操作を受け取り、ファイルの読み書きなどの処理を担います。Model(データ管理) では、現在開いているファイルの情報や、ユーザー設定の保存・読み込みを管理します。
このように各コンポーネントを明確に分割することで、コードの保守性を高め、機能の追加や改修をスムーズに行える設計を実現します。
View(UI)
- メニューバー
- エディタエリア(テキスト入力部分)
- ステータスバー
Controller(制御部)
- ユーザー操作の管理(ボタンやショートカット)
- ファイルの読み書き制御
Model(データ管理)
- 現在開いているファイルの情報
- 設定データの保存・読み込み
ファイル管理とユーザー設定
エディタアプリにおいて、ファイルの読み込みや保存は最も基本的でありながら、柔軟な設計が求められる重要な機能の一つです。ユーザーがスムーズにファイルを操作できるよう、本アプリでは直感的なUIと効率的なファイル入出力処理を両立させた設計を行います。
具体的には、ファイルの選択や保存を簡単に行えるように FileChooser を利用したファイルダイアログ を実装し、ユーザーが自由にファイルを開閉できるようにします。また、ファイルの読み書き処理には BufferedWriter / BufferedReader を使用し、大容量のテキストファイルでも高速に処理できる仕組みを採用します。
- ファイル選択ダイアログの実装
- FileChooser を使ったファイルの入出力
- BufferedWriter / BufferedReader<によるファイル管理
エディタアプリをより使いやすくするためには、ユーザーごとにカスタマイズ可能な設定機能が不可欠です。本アプリでは、フォントの種類やサイズ、テーマ(ダークモード対応)に対応する仕組みを設計します。
エディタアプリの使用する全クラス一覧
本アプリでは、各クラスを役割ごとに分割し、必要に応じて実行構成の設定を適用する設計を採用しています。以下の表では、各クラスの役割と実行構成の設定の有無を示します。なお、実行構成の設定が「あり」となるのは、main() メソッドを持つクラスのみです。
クラス名 | 役割 | 実行構成の設定 |
---|---|---|
TextEditor | JavaFXアプリケーションのエントリーポイント | あり |
EditorView | エディタのメインUI(レイアウト管理) | なし |
AutoSaveManager | 定期的な自動保存を制御 | なし |
MenuController | メニューバーの操作制御 | なし |
FileManager | ファイルの読み込み・保存処理 | なし |
EditorController | エディタのUI操作を制御(フォント、テーマなど) | なし |
EditorSettings | エディタの設定(フォント、テーマ)の保存と管理 | なし |
エディタアプリの実装手順
本アプリの実装手順を順を追って説明します。各機能を段階的に構築しながら、エディタとして最低限必要な機能を追加していきます。まずはメインアプリの作成から始め、UIの構築、テキスト編集機能、ファイル管理、設定管理などを実装していきます。開発環境のセットアップについては、前回の記事を参照してください。
本アプリの実装手順を以下に示します。開発環境のセットアップに関しては、下記の記事を参照してください。
メインアプリの実装
エディタアプリの起点となるメインアプリを実装します。JavaFXの Application クラスを継承し、start() メソッド内でウィンドウの初期設定を行います。このクラスが全体のエントリーポイントとなり、UIのロードや制御の基盤を担います。
TextEditor.java の実装
JavaFXアプリのエントリーポイントとなるMainApp.javaの実装を行います。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | package application; import javafx.application.Application; import javafx.scene.Group; import javafx.scene.Scene; import javafx.stage.Stage; /** * TextEditorクラスは、シンプルなテキストエディタアプリケーションを作成するためのエントリーポイントです。 * JavaFXを使用して、基本的なウィンドウとシーンを表示します。 */ public class TextEditor extends Application { /** * アプリケーションのエントリーポイント。シンプルなウィンドウとシーンを作成し、表示します。 * @param primaryStage アプリケーションのメインステージ */ @Override public void start(Stage primaryStage) { // ウィンドウのタイトルを設定 primaryStage.setTitle("エディタアプリ"); // シンプルな空のシーンを設定 // Groupを使って、コンテンツを配置するための親ノードを作成 primaryStage.setScene(new Scene(new Group(), 300, 200)); // アプリケーションのウィンドウを表示 primaryStage.show(); } /** * アプリケーションの開始メソッド。JavaFXアプリケーションを起動します。 * @param args コマンドライン引数 */ public static void main(String[] args) { // JavaFXアプリケーションの起動 launch(args); } } |

UI(エディタ画面)の構築
エディタアプリのユーザーインターフェースを作成します。直感的な操作性を考慮し、シンプルかつ拡張性の高いレイアウトを設計します。メインの編集エリアに加え、メニューバーやツールバーを配置し、快適な編集環境を提供します。
EditorView.java の実装
エディタのメインUIを構築し、レイアウトを設定します。JavaFXのレイアウトマネージャーを活用し、メインウィンドウの構成を定義します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | package application; import javafx.scene.control.TextArea; import javafx.scene.layout.BorderPane; /** * EditorViewクラスは、テキストエディタのユーザーインターフェース(UI)部分を定義します。 * ここでは、エディタのレイアウトとテキストエリアの作成を行います。 */ public class EditorView { private TextArea textArea; // テキスト入力エリア private BorderPane layout; // レイアウトのための親ノード /** * EditorViewのコンストラクタ。テキストエリアとレイアウトを初期化します。 * テキストエリアは`BorderPane`の中央に配置されます。 */ public EditorView() { // `TextArea` を作成。ユーザーがテキストを入力するための領域。 textArea = new TextArea(); // `BorderPane` レイアウトを作成。中央に`TextArea`を配置します。 layout = new BorderPane(); layout.setCenter(textArea); // `TextArea` を中央に配置 } /** * 現在のレイアウト(`BorderPane`)を取得します。 * @return layout `BorderPane` レイアウト */ public BorderPane getLayout() { return layout; } /** * テキストエリアを取得します。テキストの編集を行うために使用します。 * @return textArea テキスト入力エリア */ public TextArea getTextArea() { return textArea; } } |
TextEditor.javaの修正
EditorView.java の実装を反映させるために「MainApp.java」へ下記コードを追加します。
// EditorViewのインスタンスを作成し、ウィンドウにセット
new EditorView(primaryStage);
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | package application; import javafx.application.Application; import javafx.stage.Stage; /** * TextEditorクラスは、JavaFXを使用してエディタアプリケーションを起動するエントリーポイントです。 * メインウィンドウの作成と、エディタビューを初期化します。 */ public class TextEditor extends Application { /** * アプリケーションのメインメソッド。アプリケーションのウィンドウ(Stage)を作成し、 * エディタビューを表示します。 * @param primaryStage アプリケーションのメインステージ */ @Override public void start(Stage primaryStage) { // ウィンドウのタイトルを設定 primaryStage.setTitle("エディタアプリ"); // EditorView を作成し、primaryStage(ウィンドウ)を渡す // EditorViewは、エディタのUIレイアウトやインターフェースを担当 new EditorView(primaryStage); } /** * アプリケーションを起動します。JavaFXアプリケーションを開始するためのエントリーポイントです。 * @param args コマンドライン引数 */ public static void main(String[] args) { // JavaFXアプリケーションを起動 launch(args); } } |

メニューバーの作成
エディタの操作性を向上させるために、メニューバーを実装します。メニューバーでは、ファイルの開閉や保存などの基本操作を提供ます。
MenuController.java の実装
エディタアプリに「メニューバー」を追加し、基本的な操作(開く・保存など)を管理できるようにします。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 | package application; import javafx.scene.control.Menu; import javafx.scene.control.MenuBar; import javafx.scene.control.MenuItem; /** * MenuControllerクラスは、エディタのメニューを制御するクラスです。 * 「ファイル」メニューを作成し、その中に「開く」と「保存」のメニューアイテムを追加します。 */ public class MenuController { private MenuBar menuBar; // メニューのバー private MenuItem openItem; // 「開く」メニューアイテム private MenuItem saveItem; // 「保存」メニューアイテム /** * MenuControllerのコンストラクタ。 * メニューの初期化処理を行います。 */ public MenuController() { initialize(); } /** * メニューを初期化するメソッド。 * 「ファイル」メニューを作成し、その中に「開く」および「保存」のメニューアイテムを追加します。 */ private void initialize() { // メニューのバーを作成 menuBar = new MenuBar(); // 「ファイル」メニューを作成 Menu fileMenu = new Menu("ファイル"); // 「開く」「保存」メニューアイテムを作成 openItem = new MenuItem("開く"); saveItem = new MenuItem("保存"); // メニューアイテムを「ファイル」メニューに追加 fileMenu.getItems().addAll(openItem, saveItem); // メニューをメニューバーに追加 menuBar.getMenus().add(fileMenu); } /** * メニューのバー(MenuBar)を取得します。 * @return menuBar メニューのバー */ public MenuBar getMenuBar() { return menuBar; } /** * 「開く」メニューアイテムを取得します。 * @return openItem 「開く」メニューアイテム */ public MenuItem getOpenItem() { return openItem; } /** * 「保存」メニューアイテムを取得します。 * @return saveItem 「保存」メニューアイテム */ public MenuItem getSaveItem() { return saveItem; } } |
TextEditor.javaの修正
MenuController.java及び、ToolbarController.java の実装を反映させるために「TextEditor.java」へ下記コードを追加します。
// メニューバーをトップに追加
MenuController menuController = new MenuController();
root.setTop(menuController.getMenuBar());
// ツールバーをメニューバーの下に配置
ToolbarController toolbarController = new ToolbarController();
root.setCenter(toolbarController.getToolBar());
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | package application; import javafx.application.Application; import javafx.scene.Scene; import javafx.scene.layout.BorderPane; import javafx.stage.Stage; /** * TextEditorクラスは、エディタアプリケーションのエントリーポイントであり、アプリケーションのウィンドウ(Stage)を作成します。 * メニュー、ツールバー、エディタビューを組み合わせて、基本的なテキストエディタのUIを構築します。 */ public class TextEditor extends Application { private EditorView editorView; // エディタビュー private MenuController menuController; // メニューを制御するコントローラー private ToolbarController toolbarController; // ツールバーを制御するコントローラー /** * アプリケーションのメインメソッド。ウィンドウを作成し、エディタのレイアウトを構築して表示します。 * @param primaryStage アプリケーションのメインステージ */ @Override public void start(Stage primaryStage) { // アプリケーションのタイトルを設定 primaryStage.setTitle("エディタアプリ"); // EditorView を作成(エディタのレイアウトを担当) editorView = new EditorView(); // MenuController と ToolbarController を作成 // メニューとツールバーを管理するコントローラーをインスタンス化 menuController = new MenuController(); toolbarController = new ToolbarController(); // レイアウトを設定(BorderPane を使用) BorderPane root = new BorderPane(); // メニューバーを上部に配置 root.setTop(menuController.getMenuBar()); // エディタビューを中央に配置 root.setCenter(editorView.getLayout()); // ツールバーを下部に配置 root.setBottom(toolbarController.getToolBar()); // シーンを作成し、ウィンドウに設定 Scene scene = new Scene(root, 800, 600); primaryStage.setScene(scene); primaryStage.show(); // ウィンドウを表示 } /** * JavaFXアプリケーションを起動します。 * @param args コマンドライン引数 */ public static void main(String[] args) { launch(args); // アプリケーションの起動 } } |

ファイル管理の実装
エディタアプリでは、作成したテキストをファイルに保存し、後で再編集できるようにする必要があります。ここでは、ファイルの読み込み・保存処理と、作業内容を定期的に自動保存する機能を実装します。
FileManager.java の実装
ファイルの読み込みと保存を管理するFileManager.javaを実装します。このクラスは、ユーザーが手動でファイルを開いたり、編集内容を保存できるようにします。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 | package application; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.File; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import javafx.stage.FileChooser; import javafx.stage.Stage; /** * FileManagerクラスは、ファイルの読み書きを管理するクラスです。 * ユーザーがファイルを開いたり、保存したりする機能を提供します。 */ public class FileManager { private Stage stage; // アプリケーションのウィンドウ /** * コンストラクタ。`FileManager`オブジェクトを作成します。 * @param stage ファイル選択ダイアログを表示するために必要なステージ */ public FileManager(Stage stage) { this.stage = stage; } /** * ユーザーが選択したファイルを開き、その内容を読み込んで返します。 * @return ファイルの内容(テキスト) */ public String openFile() { // ファイル選択ダイアログを表示 FileChooser fileChooser = new FileChooser(); fileChooser.setTitle("ファイルを開く"); File file = fileChooser.showOpenDialog(stage); if (file != null) { // ファイルが選択された場合、その内容を読み込む try (BufferedReader reader = new BufferedReader(new FileReader(file))) { StringBuilder content = new StringBuilder(); String line; while ((line = reader.readLine()) != null) { content.append(line).append("\n"); } return content.toString(); } catch (IOException e) { // エラーが発生した場合、スタックトレースを出力 e.printStackTrace(); } } return ""; // ファイルが選択されなかった場合、空文字を返す } /** * ユーザーが選択した場所にファイルを保存します。 * @param content 保存するテキスト内容 */ public void saveFile(String content) { // ファイル選択ダイアログを表示 FileChooser fileChooser = new FileChooser(); fileChooser.setTitle("ファイルを保存"); File file = fileChooser.showSaveDialog(stage); if (file != null) { // ファイルが選択された場合、その内容を保存する try (BufferedWriter writer = new BufferedWriter(new FileWriter(file))) { writer.write(content); } catch (IOException e) { // エラーが発生した場合、スタックトレースを出力 e.printStackTrace(); } } } } |
AutoSaveManager.javaの実装
エディタの編集内容を定期的に保存するため、自動保存機能を追加します。一定時間ごとに現在のテキストをバックアップファイルとして保存し、万が一のデータ消失を防ぎます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 | package application; import java.io.BufferedWriter; import java.io.FileWriter; import java.io.IOException; import java.util.Timer; import java.util.TimerTask; import javafx.scene.control.TextArea; /** * AutoSaveManagerクラスは、テキストエリアの内容を定期的に自動保存する機能を提供します。 * 変更があった場合、指定したファイルに自動で内容を保存し、ユーザーのデータ損失を防ぎます。 */ public class AutoSaveManager { private String backupFilePath = "backup.txt"; // 自動保存するバックアップファイルのパス private Timer timer; // タイマーオブジェクト private TextArea textArea; // テキストエリア private String lastSavedContent = ""; // 最後に保存した内容 /** * コンストラクタ。AutoSaveManagerを初期化し、指定されたTextAreaに対して自動保存を開始します。 * @param textArea 自動保存対象となるテキストエリア */ public AutoSaveManager(TextArea textArea) { this.textArea = textArea; timer = new Timer(true); // バックグラウンドで実行されるタイマー startAutoSave(); // 自動保存を開始 } /** * 自動保存を開始するメソッド。指定した間隔でテキストエリアの内容を保存します。 * 変更があった場合にのみ保存が行われます。 */ private void startAutoSave() { // 5秒ごとに自動保存を実行するタスクをスケジュール timer.schedule(new TimerTask() { @Override public void run() { // テキストエリアの現在の内容を取得 String content = textArea.getText(); // 最後に保存した内容と異なる場合のみ保存 if (!content.equals(lastSavedContent)) { try (BufferedWriter writer = new BufferedWriter(new FileWriter(backupFilePath))) { writer.write(content); // バックアップファイルに内容を保存 lastSavedContent = content; // 最後に保存した内容を更新 } catch (IOException e) { // ファイル書き込みエラーが発生した場合、スタックトレースを表示 e.printStackTrace(); } } } }, 0, 5000); // 0ms後に開始し、5000ms(5秒)ごとに繰り返し実行 } /** * 自動保存を停止するメソッド。タイマーをキャンセルして自動保存を停止します。 */ public void stopAutoSave() { timer.cancel(); // タイマーをキャンセルして自動保存を停止 } } |
TextEditor.javaの修正
FileManager.java(開く・保存)と AutoSaveManager.java(自動保存)をTextEditor.javaへ組み込みます
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 | package application; import javafx.application.Application; import javafx.scene.Scene; import javafx.scene.layout.BorderPane; import javafx.stage.Stage; /** * TextEditorクラスは、エディタアプリケーションのエントリーポイントです。 * メインウィンドウを作成し、エディタビュー、メニュー、ツールバー、ファイル操作、自動保存などの機能を統合します。 */ public class TextEditor extends Application { private EditorView editorView; // エディタのUIを管理するビュー private MenuController menuController; // メニューの操作を管理 private ToolbarController toolbarController; // ツールバーの操作を管理 private EditorController editorController; // エディタのロジックを制御 private FileManager fileManager; // ファイルの読み書きを管理 private AutoSaveManager autoSaveManager; // 自動保存を管理 /** * アプリケーションのメインメソッド。ウィンドウを作成し、レイアウトを構築して表示します。 * @param primaryStage アプリケーションのメインステージ */ @Override public void start(Stage primaryStage) { primaryStage.setTitle("エディタアプリ"); // エディタビューを作成 editorView = new EditorView(); // エディタコントローラを作成し、TextAreaを管理 editorController = new EditorController(editorView.getTextArea()); // メニューコントローラを作成 menuController = new MenuController(); // ツールバーコントローラを作成 toolbarController = new ToolbarController(editorController); // ファイルマネージャを作成(ウィンドウを渡す) fileManager = new FileManager(primaryStage); // 自動保存マネージャを作成(TextAreaを渡す) autoSaveManager = new AutoSaveManager(editorView.getTextArea()); // メニューの「開く」と「保存」項目にアクションを設定 menuController.getOpenItem().setOnAction(event -> { // ファイルを開き、内容をTextAreaにセット String content = fileManager.openFile(); if (!content.isEmpty()) { editorView.getTextArea().setText(content); } }); menuController.getSaveItem().setOnAction(event -> { // 現在のテキスト内容をファイルに保存 fileManager.saveFile(editorView.getTextArea().getText()); }); // レイアウトを設定 BorderPane root = new BorderPane(); root.setTop(menuController.getMenuBar()); // メニューバーを上部に配置 root.setCenter(editorView.getLayout()); // エディタビューを中央に配置 root.setBottom(toolbarController.getToolBar()); // ツールバーを下部に配置 // シーンを作成し、ステージに設定 Scene scene = new Scene(root, 800, 600); primaryStage.setScene(scene); primaryStage.show(); // アプリケーションを表示 } /** * アプリケーションのエントリーポイント。 * @param args コマンドライン引数 */ public static void main(String[] args) { launch(args); // アプリケーションを起動 } } |



フォント・テーマの変更機能の実装
エディタのフォントやテーマを変更することで、ユーザーがより快適に作業できる環境を提供できます。このセクションでは、フォントの種類やサイズの変更、ダークテーマ・ライトテーマの切り替え方法について解説します。
エディタのフォントを変更することで、ユーザーが見やすい環境をカスタマイズできます。特に、プログラミング向けのエディタでは、可読性の高いフォントの選択が重要です。
クラスの配置場所
この辺りからクラスの数が増え始めるので現在までのクラス配置イメージを記載しておきます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | project-root/ ├── src/ │ ├── main/ │ │ ├── java/ │ │ │ ├── controllers/ │ │ │ │ ├── TextEditor.java │ │ │ │ ├── EditorView.java │ │ │ │ ├── MenuController.java │ │ │ │ ├── FileManager.java │ │ │ │ ├── EditorController.java │ │ │ │ ├── EditorSettings.java │ │ ├── resources/ │ │ │ ├── dark-theme.css │ │ │ ├── editor.fxml |
フォントの種類とサイズ変更機能の実装
JavaFXでは、 TextArea のフォントを変更することで、ユーザーが好みのフォントやサイズを設定できます。以下のコードを適用することで、フォントをリアルタイムに変更できます。
import javafx.scene.text.Font;
textArea.setFont(Font.font("Courier New", 14));
テキストエディタのテーマ編集仕様
テキストエディタのテーマを下記へまとめます。下記のテーブルは、ダークテーマとライトテーマの色分けに関する情報を簡潔に表示するためのものです。
コンポーネント | 背景色 | 文字色 | 備考 |
---|---|---|---|
ダークテーマ(全体背景) | #333333 | white | ダークグレー背景、白文字 |
テキストエリア | #222222 | white | 背景暗いグレー、白文字 |
選択範囲 | #555555 | white | 背景色、選択テキストの色 |
プレースホルダー文字 | #888888 | none | プレースホルダーの文字色 |
ComboBox, Spinner | #444444 | white | 背景色、テキストの色(アイテム) |
ComboBoxリストアイテム | #444444 | white | リストアイテム背景、文字色 |
Spinnerテキストフィールド | #555555 | white | スピナーの文字色 |
ボタン | #555555 | white | ボタン背景、白文字 |
設定パネル | #333333 | white | 設定パネル背景、白文字 |
ダークテーマの詳細設定 | none | white | 各コンポーネントの文字色を白に設定 |
ライトテーマ(全体背景) | #ffffff | #000000 | 白背景、黒文字 |
テキストエリア | #ffffff | #000000 | 白背景、黒文字 |
選択範囲 | #cccccc | #000000 | 選択範囲の背景色、選択テキストの色 |
プレースホルダー文字 | #666666 | none | プレースホルダー文字の色 |
ComboBox, Spinner | #ffffff | #000000 | 背景色、テキストの色 |
ComboBoxリストアイテム | #ffffff | #000000 | リストアイテム背景、文字色 |
Spinnerテキストフィールド | #ffffff | #000000 | スピナーの文字色 |
ボタン | #eeeeee | #000000 | ボタン背景、黒文字 |
テキストエリア(文字色) | #ffffff | #000000 | 黒文字 |
ComboBoxアイテム(マウスオーバー) | #4CAF50 | white | アイテムにマウスオーバーしたとき |
Spinnerアイテム(マウスオーバー) | #4CAF50 | white | スピナーにマウスオーバーしたとき |
ダークテーマ時のSpinner | #555555 | white | 背景色を暗く、文字色を白に |
ダークテーマ時のSpinnerボタン | #555555 | white | ボタン背景色、ボタン内の文字色を白に |
ダークテーマとライトテーマの切り替え
ダークテーマやライトテーマを切り替えることで、目の負担を軽減し、作業環境を改善できます。JavaFXでは、CSSのスタイルを変更することで簡単にテーマを切り替えられます。
以下のコードを適用することで、ボタンを押すとテーマが切り替わるようになります。
root.getStylesheets().add(getClass().getResource("dark-theme.css").toExternalForm());
ダークテーマのスタイルは、以下のCSSで設定できます。
.root {
-fx-background-color: #333;
-fx-text-fill: #fff;
}
.text-area {
-fx-background-color: #222;
-fx-text-fill: #ddd;
}
dark-theme.cssの実装
本プロジェクトのフォルダ構成は以下の通りです。
CSSファイルは src/main/resources/ に保存されています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 | /* ダークテーマ */ .dark-mode { -fx-background-color: #333333; /* ダークグレーの背景 */ -fx-text-fill: white; /* 白文字 */ } /* テキストエリアのスタイル */ .dark-mode .text-area { -fx-background-color: #222222; /* 背景を暗いグレーに */ -fx-control-inner-background: #222222; /* TextArea の内部背景 */ -fx-text-fill: white; /* テキスト色は白 */ -fx-highlight-fill: #555555; /* 選択範囲の背景色 */ -fx-highlight-text-fill: white; /* 選択テキストの色 */ -fx-prompt-text-fill: #888888; /* プレースホルダー文字の色 */ } /* ComboBox, Spinner のテキストカラー設定 */ .dark-mode .combo-box, .dark-mode .spinner { -fx-background-color: #444444; /* 背景を暗いグレーに */ -fx-text-fill: white; /* テキストの色を白に */ -fx-prompt-text-fill: white; /* プレースホルダー文字色も白 */ } /* ComboBox のリストアイテム */ .dark-mode .combo-box .list-cell { -fx-text-fill: white; /* リストアイテムのテキストカラー */ -fx-background-color: #444444; /* 背景色 */ } /* Spinner のテキストフィールド */ .dark-mode .spinner .text-field { -fx-text-fill: white; /* スピナーの文字色を白に */ } /* ボタンのスタイル */ .dark-mode .button { -fx-background-color: #555555; /* ボタンの背景 */ -fx-text-fill: white; /* ボタンのテキスト色 */ -fx-border-color: #777777; /* ボーダー色 */ } /* ダークモードの設定パネル (設定項目) */ #settings-box { -fx-background-color: #333333; /* ダークモードの背景 */ } /* ダークテーマでの詳細設定 */ .dark-mode .combo-box, .dark-mode .spinner, .dark-mode .button { -fx-text-fill: white; /* 各コンポーネントの文字色を白に設定 */ } /* Lightテーマに適用される設定 */ .root { -fx-background-color: #ffffff; /* ライトモードの背景 */ -fx-text-fill: black; /* テキスト色は黒 */ } .text-area { -fx-background-color: #ffffff; /* 白背景 */ -fx-control-inner-background: #ffffff; /* TextArea の背景 */ -fx-text-fill: black; /* 黒文字 */ -fx-highlight-fill: #cccccc; /* 選択範囲の背景色 */ -fx-highlight-text-fill: black; /* 選択テキストの色 */ -fx-prompt-text-fill: #666666; /* プレースホルダー文字色 */ } .combo-box, .spinner { -fx-background-color: #ffffff; /* ライトモードの背景 */ -fx-text-fill: black; /* テキストの色 */ -fx-prompt-text-fill: black; /* プレースホルダー文字色 */ } .combo-box .list-cell { -fx-text-fill: black; /* リスト内のテキストカラー */ -fx-background-color: #ffffff; /* リストアイテムの背景 */ } .spinner .text-field { -fx-text-fill: black; /* スピナーの文字色 */ } .button { -fx-background-color: #eeeeee; /* ボタン背景 */ -fx-text-fill: black; /* ボタン文字色 */ -fx-border-color: #cccccc; /* ボーダー色 */ } /* テキストエリアの文字色 */ .text-area { -fx-text-fill: black; /* 黒文字 */ } /* ComboBoxアイテムのマウスオーバー時の色を変更 */ .combo-box .list-cell:hover { -fx-background-color: #4CAF50; /* 緑色の背景 */ -fx-text-fill: white; /* 白文字に */ } /* Spinnerのアイテムのマウスオーバー時の色を変更 */ .spinner .text-field:hover { -fx-background-color: #4CAF50; /* 緑色の背景 */ -fx-text-fill: white; /* 白文字に */ } /* ダークテーマ時のSpinnerのスタイル */ .dark-mode .spinner .text-field { -fx-background-color: #555555; /* 背景色を暗く */ -fx-text-fill: white; /* 文字色を白に */ } /* ダークテーマ時のSpinnerのボタン部分(上下の矢印) */ .dark-mode .spinner .increment-button, .dark-mode .spinner .decrement-button { -fx-background-color: #555555; /* ボタン背景 */ -fx-text-fill: white; /* ボタン内の文字色を白に */ } |
EditorController.javaの実装
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 | package application; import javafx.fxml.FXML; import javafx.scene.control.Button; import javafx.scene.control.ComboBox; import javafx.scene.control.Spinner; import javafx.scene.control.TextArea; import javafx.scene.layout.BorderPane; import javafx.scene.layout.VBox; import javafx.scene.text.Font; /** * EditorControllerクラスは、エディタのUI操作を管理します。 * フォント選択、フォントサイズ変更、テーマ切替などの機能を提供します。 */ public class EditorController { @FXML private ComboBox<String> fontSelector; // フォント選択用コンボボックス @FXML private Spinner<Integer> fontSizeSpinner; // フォントサイズ選択用スピナー @FXML private TextArea textArea; // テキストエリア @FXML private Button themeToggleButton; // テーマ切り替えボタン @FXML private VBox root; // レイアウトのルート(VBox) /** * initializeメソッドは、FXMLファイルで指定されたUIコンポーネントを初期化し、イベントリスナーを設定します。 */ @FXML public void initialize() { // フォントリストを設定 fontSelector.getItems().addAll("Arial", "Verdana", "Courier New", "Times New Roman"); fontSelector.setValue(TextEditor.getFont().getFamily()); // 初期フォント設定 // フォントサイズ設定 fontSizeSpinner.setValueFactory( new javafx.scene.control.SpinnerValueFactory.IntegerSpinnerValueFactory(10, 32, (int) TextEditor.getFont().getSize()) ); // テキストエリアに初期フォントを適用 textArea.setFont(TextEditor.getFont()); // フォント選択時のアクション設定 fontSelector.setOnAction(e -> applyFont()); fontSizeSpinner.valueProperty().addListener((obs, oldVal, newVal) -> applyFont()); // フォントサイズ変更リスナー // テーマ切替ボタンのアクション設定 themeToggleButton.setOnAction(e -> { toggleTheme(); TextEditor.getInstance().applyTheme((BorderPane) root.getParent()); // テーマを適用 }); } /** * フォント選択とサイズ変更の後にテキストエリアにフォントを適用するメソッド。 */ private void applyFont() { String selectedFont = fontSelector.getValue(); // 選択されたフォントを取得 int fontSize = fontSizeSpinner.getValue(); // 選択されたフォントサイズを取得 TextEditor.getInstance().setFont(selectedFont, fontSize); // インスタンス経由でフォント設定 // テキストエリアに新しいフォントを適用 textArea.setFont(Font.font(selectedFont, fontSize)); } /** * 現在のテーマを切り替えるメソッド。 * テーマが変更されると、適用されているテーマが画面に反映されます。 */ private void toggleTheme() { TextEditor.toggleTheme(); // テーマを切り替え TextEditor.getInstance().applyTheme((BorderPane) root.getParent()); // 新しいテーマを適用 } } |
EditorSettings.javaの実装
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 | package application; import java.util.prefs.Preferences; import javafx.scene.text.Font; /** * EditorSettingsクラスは、エディタの設定を管理するクラスです。 * フォント、フォントサイズ、テーマの設定を保存および取得します。 */ public class EditorSettings { private static final String PREF_FONT = "font"; // 保存するフォントのキー private static final String PREF_FONT_SIZE = "fontSize"; // 保存するフォントサイズのキー private static final String PREF_THEME = "theme"; // 保存するテーマのキー private String currentFont; // 現在のフォント private int currentFontSize; // 現在のフォントサイズ private String currentTheme; // 現在のテーマ private Preferences prefs; // 設定を保存するためのPreferencesオブジェクト /** * コンストラクタ。エディタの設定をPreferencesから読み込みます。 */ public EditorSettings() { // Preferencesを使用して設定を取得または初期値を設定 prefs = Preferences.userNodeForPackage(EditorSettings.class); currentFont = prefs.get(PREF_FONT, "Arial"); // デフォルトはArial currentFontSize = prefs.getInt(PREF_FONT_SIZE, 14); // デフォルトは14 currentTheme = prefs.get(PREF_THEME, "light"); // デフォルトはlightテーマ } /** * 現在のフォント(フォントファミリとサイズ)を取得します。 * @return 現在のフォント */ public Font getCurrentFont() { return Font.font(currentFont, currentFontSize); } /** * 現在のテーマを取得します。 * @return 現在のテーマ("light" または "dark") */ public String getCurrentTheme() { return currentTheme; } /** * フォントとフォントサイズを設定します。 * 設定はPreferencesに保存されます。 * @param font フォントファミリ名 * @param size フォントサイズ */ public void setFont(String font, int size) { this.currentFont = font; this.currentFontSize = size; // 設定を保存 prefs.put(PREF_FONT, font); prefs.putInt(PREF_FONT_SIZE, size); } /** * テーマを切り替えます。 * 現在のテーマが"light"の場合、"dark"に変更し、逆も同様です。 * 設定はPreferencesに保存されます。 */ public void toggleTheme() { // 現在のテーマがlightの場合はdarkに、darkの場合はlightに切り替え if (currentTheme.equals("light")) { currentTheme = "dark"; } else { currentTheme = "light"; } // 設定を保存 prefs.put(PREF_THEME, currentTheme); } } |
TextEditor.javaの修正
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 | package application; import java.util.prefs.Preferences; import javafx.application.Application; import javafx.scene.Node; import javafx.scene.Scene; import javafx.scene.control.Button; import javafx.scene.control.ComboBox; import javafx.scene.control.Label; import javafx.scene.control.Spinner; import javafx.scene.layout.BorderPane; import javafx.scene.layout.HBox; import javafx.scene.text.Font; import javafx.stage.Stage; /** * TextEditorクラスは、エディタアプリケーションのメインウィンドウを構成するクラスです。 * ユーザーのフォント選択、フォントサイズ、テーマ切り替えを管理し、アプリケーションのUIを構築します。 */ public class TextEditor extends Application { private static TextEditor instance; // インスタンス private EditorView editorView; // エディタのUI private MenuController menuController; // メニューの管理 private FileManager fileManager; // ファイル操作を管理 private AutoSaveManager autoSaveManager; // 自動保存を管理 private HBox settingsBox; // 設定用のUIボックス private static Preferences prefs = Preferences.userNodeForPackage(TextEditor.class); private static final String PREF_FONT = "font"; // フォントの設定キー private static final String PREF_FONT_SIZE = "fontSize"; // フォントサイズの設定キー private static final String PREF_THEME = "theme"; // テーマの設定キー private static String currentFont = prefs.get(PREF_FONT, "Arial"); // 現在のフォント private static int currentFontSize = prefs.getInt(PREF_FONT_SIZE, 14); // 現在のフォントサイズ private static String currentTheme = prefs.get(PREF_THEME, "light"); // 現在のテーマ /** * アプリケーションのメインメソッド。ウィンドウを作成し、レイアウトを設定します。 * @param primaryStage アプリケーションのメインステージ */ @Override public void start(Stage primaryStage) { instance = this; primaryStage.setTitle("エディタアプリ"); // 初期テーマを「light」に設定 currentTheme = "light"; prefs.put(PREF_THEME, currentTheme); // UIの各コンポーネントを作成 editorView = new EditorView(); menuController = new MenuController(); fileManager = new FileManager(primaryStage); autoSaveManager = new AutoSaveManager(editorView.getTextArea()); // フォント選択とテーマ切り替え用のUIを設定 ComboBox<String> fontSelector = new ComboBox<>(); fontSelector.getItems().addAll("Arial", "Verdana", "Courier New", "Times New Roman"); fontSelector.setValue(currentFont); Spinner<Integer> fontSizeSpinner = new Spinner<>(10, 32, currentFontSize); Button themeToggleButton = new Button("テーマ切替"); // レイアウトのためのBorderPaneを作成 BorderPane root = new BorderPane(); // フォント選択時にフォントを適用 fontSelector.setOnAction(e -> { String selectedFont = fontSelector.getValue(); int selectedSize = fontSizeSpinner.getValue(); setFont(selectedFont, selectedSize); editorView.getTextArea().setFont(Font.font(selectedFont, selectedSize)); }); // フォントサイズ変更時にフォントを適用 fontSizeSpinner.valueProperty().addListener((obs, oldVal, newVal) -> setFont(fontSelector.getValue(), newVal)); // テーマ切り替えボタンのアクション themeToggleButton.setOnAction(e -> { toggleTheme(); applyTheme(root); }); // 設定用のボックスにフォントとサイズの選択UIを追加 settingsBox = new HBox(10, new Label("フォント:"), fontSelector, new Label("サイズ:"), fontSizeSpinner, themeToggleButton); settingsBox.setId("settings-box"); // 画面レイアウトを設定 root.setTop(menuController.getMenuBar()); root.setCenter(editorView.getLayout()); root.setBottom(settingsBox); // 初期のフォントとテーマ設定 editorView.getTextArea().setFont(Font.font(currentFont, currentFontSize)); applyTheme(root); // シーンを設定し、表示 Scene scene = new Scene(root, 800, 600); primaryStage.setScene(scene); primaryStage.show(); } /** * シングルトンインスタンスを取得します。 * @return TextEditorインスタンス */ public static TextEditor getInstance() { return instance; } /** * フォントとフォントサイズを設定します。設定はPreferencesに保存されます。 * @param font フォントファミリ * @param size フォントサイズ */ public void setFont(String font, int size) { currentFont = font; currentFontSize = size; prefs.put(PREF_FONT, font); prefs.putInt(PREF_FONT_SIZE, size); if (editorView != null) { editorView.getTextArea().setFont(Font.font(currentFont, currentFontSize)); } else { System.err.println("Error: editorView is null!"); } } /** * 現在のフォント設定を取得します。 * @return 現在のフォント */ public static Font getFont() { return Font.font(currentFont, currentFontSize); } /** * テーマを切り替えます。「light」から「dark」またはその逆に切り替え、設定を保存します。 */ public static void toggleTheme() { currentTheme = currentTheme.equals("light") ? "dark" : "light"; prefs.put(PREF_THEME, currentTheme); } /** * 現在のテーマを適用します。ダークモードかライトモードかを切り替えます。 * @param root レイアウトのルート要素(BorderPane) */ public void applyTheme(BorderPane root) { String themePath = "dark-theme.css"; java.net.URL resource = TextEditor.class.getResource(themePath); if (resource == null) { System.err.println("Error: Theme file not found: " + themePath); return; } root.getStylesheets().clear(); root.getStylesheets().add(resource.toExternalForm()); // ダークモードの場合のスタイル設定 if (currentTheme.equals("dark")) { root.getStyleClass().add("dark-mode"); settingsBox.setStyle("-fx-background-color: #333333;"); for (Node node : settingsBox.getChildren()) { if (node instanceof Label) { ((Label) node).setStyle("-fx-text-fill: white;"); } } } else { // ライトモードの場合のスタイル設定 root.getStyleClass().remove("dark-mode"); settingsBox.setStyle("-fx-background-color: #ffffff;"); for (Node node : settingsBox.getChildren()) { if (node instanceof Label) { ((Label) node).setStyle("-fx-text-fill: black;"); } } } } /** * アプリケーションのエントリーポイント。JavaFXアプリケーションを起動します。 * @param args コマンドライン引数 */ public static void main(String[] args) { launch(args); } } |


まとめ
この記事では、JavaFXを用いたシンプルなエディタアプリの作成方法を学びました。基本的なUI設計やアーキテクチャを理解し、個人開発における可能性を広げるための第一歩を踏み出すことができました。サラリーマンエンジニアとしての未来に不安を感じているなら、JavaFXを活用した個人開発のスキルを身につけることが、今後のキャリアを安定させる鍵となります。
終わりにひとこと
IT業界は急速に変化しています。これまで企業主導で進んできたシステム開発が、今後は個人の手による開発が主流になる流れを見せています。特に、サラリーマンエンジニアとして企業に勤め続けることが、もはや安定した選択肢とは言えません。今後、サラリーマンエンジニアが参画するのは主にJavaサーブレットやJSPを使ったシステム開発であり、これらの分野はますます外国勢、特に低価格で開発ができる短金の安い外国勢と激しい価格競争に巻き込まれ、搾取される側に回るのは避けられません。
この競争環境において、サラリーマンエンジニアとして生き残るには、従来のような企業依存型の働き方ではなく、個人開発スキルが不可欠になります。特に、JavaFXは、個人開発者としてのスキルを活かしやすい技術のひとつです。個人開発の世界では、企業に頼らずに自分の力で収益を上げていける可能性が広がっています。
企業に依存するサラリーマンエンジニアとしての道は、今後ますます厳しくなる一方で、個人開発に切り替えることで、自分の自由な時間や収益源を確保できる道が開けます。このエディタアプリの開発を通じて、最低限の機能のみを実装したJavaFXベースの個人開発スキルを身に付ければ、将来的にはより高度な個人開発やフリーランスエンジニアとして活躍できる土台を築けるでしょう。
サラリーマンエンジニアが価格競争の波に飲み込まれる前に、個人開発に必要な技術を学んでおくことは、今後の安定したキャリアにおいて避けて通れない道です。フリーランスや個人開発者としての自由を手に入れるために、JavaFXを活用したアプリケーション開発を学び、新しい時代に適応するための一歩を踏み出しましょう。