Swingを利用したウィンドウの表示ボタンやラベルの配置テキスト入力イベント処理と一通り、GUIアプリケーションに必要な機能を作成してきたので、最後に複合的なGUIアプリケーションを作成する。

ToDoリストアプリケーションの作成

今回作成するアプリケーションは以下の機能を提供する。また、ToDoリストの一覧部分は“JList”を使用して表現する。

  • ToDoの一覧を表示する
  • ToDoを一覧に追加できる
  • 一覧のToDoを編集できる
  • 一覧のToDoを削除できる

 

JList
リストボックスを表現するコンポーネント。
複数のオブジェクトを一覧表示することができ、また、動的に一覧へのオブジェクトの追加、変更、削除を行うことができる。

JListの生成
JListは5つの種類のコンストラクタを持っている。
今回は、5つのうちのListModelを渡すタイプのコンストラクタを使用する。
このコンストラクタを使用すると、渡したリストモデルが管理しているデータを表示するリストボックスが生成される。

ListModel
JListの表示データを管理しているクラス。
JList内の表示データの操作はListModelに対して行う。
JListが表示データの管理をListModelに委譲しているのは、データを管理するクラスと、ビューを管理するクラスを分離するというSwingの設計思想によるもの。
また、JListは.ListDataListenerを使用してListModelの内容を監視しており、ListModelに変更があった場合は、自身のビューを更新する。
この仕組みにより、表示データの操作やデータモデルの拡張はListModelに対してのみ行えばよく、ビューの問題に関しては意識する必要がない。

DefaultListModel
ListModelの基本的な機能を実装したクラス。
このクラスはListModelへの動的な追加、編集、削除に対応している。
今回作成するToDoリストは追加、編集、削除機能を提供するため、DefaultListModelを使用する。

 

プロジェクトの作成

プロジェクト名「ToDoList」を作成する。

1.[ファイル]⇒[新規]⇒[プロジェクト]を選択

2.[Javaプロジェクト]を選択し[次へ]をクリック

3.プロジェクト名等を入力し[完了]をクリック

  • プロジェクト名に「ToDoList」を入力
  • プロジェクト・レイアウトで[プロジェクト・フォルダーを・・・]を選択

 

SwingAppMainクラスの作成

GUIアプリケーションの起動および土台となるフレームの生成を担当するクラス「SwingAppMain」を作成する。

1.[ファイル]⇒[新規]⇒[クラス]を選択

2.[新規Javaクラス]ダイアログで各項目を入力し[完了]をクリック

  • パッケージに「swing.sample」を入力
  • 名前に「SwingAppMain」を入力
  • public static void main(String[] args)をチェック

3.作成したクラスのソースコードを以下のように変更

 

ToDoListPaneクラスの作成

ToDoリストのすべての機能を提供するクラス「ToDoListPane」を作成する。

1.[ファイル]⇒[新規]⇒[クラス]を選択

2.[新規Javaクラス]ダイアログで各項目を入力し[完了]をクリック

  • パッケージに「swing.sample」を入力
  • 名前に「ToDoListPane」を入力
  • public static void main(String[] args)をチェック

3.作成したクラスのソースコードを以下のように変更

 

アプリケーションの実行

実行すると一覧部、入力テキストフィールド、追加・編集・削除ボタンが配置されたウィンドウが開く。
[追加]ボタンをクリックすると、テキストフィールドに入力されている文字列(ToDo)を一覧に追加。
一覧でアイテムを選択し、[編集]ボタンをクリックすると選択されている1行の文字列がテキストフィールドに表示されている文字列に変更される。
一覧でアイテムを選択し、[削除]ボタンをクリックすると選択されている1行が削除される。

 

SwingAppMainとToDoListPaneの2つのクラス、それぞれの役割

プログラムの解説
ToDoリストが提供する機能はToDoListPaneクラスが担当し、アプリケーションの起動等の機能はSwingAppMainクラスが担当するという構成になっている。

JListの生成
ToDoリストは追加/編集/削除機能を持つため、ListModel も状態の変更を受けて更新する処理が必要。
ここでは、これらの機能を満たしているDefaultListModelを生成している。(ここでは標準ライブラリに用意されている実装を利用しているが、ListModelを実装することで、自由に実装することが可能)

2行目、3行目でDefaultListModelを使用してJListを生成し、JListにスクロール機能を与えている。
最後にaddメソッドによりToDoListPaneに追加し、ToDo一覧の作成が完了する。

ToDo追加機能の実装(AddActionHandler)
AddActionHandlerクラスは、[追加]ボタンのイベントに対する処理を担当するインナークラス。
「toDoListModel.addElement();」にて、toDoListModelへパラメータで指定したデータを追加することができる。
追加されたデータの情報は、ListModelの監視の仕組みにより、JListのビューへ反映される。

ToDo編集機能の実装(ModifyActionHandler)
ModifyActionHandlerクラスは、編集ボタンのイベントに対する処理を担当するインナークラス。
「toDoListModel. set(int index, Object target);」にて、toDoListModelのindex番目のデータ、targetを設定することができる。

ToDo削除機能の実装(RemoveActionHandler)
RemoveActionHandlerクラスは、削除ボタンのイベントに対する処理を担当するインナークラス。
「setButtonsEnabled(false)」にて、すべてのボタンを非活性化し、その後doLongTaskにて、10秒待つ。
その後、「toDoListModel.remove(int index);」にて、toDoListModelのindex番目のデータを削除する。
削除した後、「setButtonsEnabled(true)」にて、すべてのボタンを活性化する。

ToDo選択行反映機能の実装(TodoListSelectionHandler)
TodoListSelectionHandlerクラスは、ToDoリストで選択した行の内容をテキストフィールドに反映する。

 

イベントディスパッチスレッド
削除処理にあえて10秒間待つ処理を入れた。
もし単純にこれを処理してしまうと、削除処理中は何も操作できなくなってしまう。
このような事態を避けるために、時間のかかる処理を別のスレッドに任せて、ユーザーが操作を行えるようにしながらバックグラウンドで処理を行う。
ここでは、RemoveThreadクラス内で別スレッドを呼び出し、時間のかかる処理(doLongTask)と削除処理を実行させることによって実現している。
時間のかかる処理を行った後、最後にSwingUtilities.invokeLater()を呼び出している。
これは、「toDoListModel.remove(int index);」および「setButtonsEnabled(true)」の2つの処理をイベントディスパッチスレッドで実行させるために使っている。
Swingのスレッドポリシーとして、“コンポーネントの「状態に依存する」あるいは「状態に影響を与える」処理はすべてイベントディスパッチスレッド上で動作しなければならない“というものがある。
これはいい換えると、Swingの描画に関連する処理は、シングルスレッドで処理しなくてはならないということ。
そして、そのシングルスレッドがイベントディスパッチスレッドになる。
このルールに則るため、わざわざSwingUtilities.invokeLater()を使っている。
今回の実装でも、sleepは描画に関係がないため別スレッドを生成して実行させたが、描画に関する処理(toDoListModel.remove、setButtonsEnabled)はイベントディスパッチスレッドで実行させるようにしている。

 

SwingAppMainクラス

SwingUtilities.invokeLater
「SwingAppMain」クラスのmainメソッド内ではSwingUtilities.invokeLater を呼び出し、Frameを描画する処理をイベントディスパッチスレッドに実行させている。
mainメソッドのスレッドが描画しているわけではない。

SwingUtilities.isEventDispatchThread
「SwingAppMain」クラスのmainメソッドとcreateAndShowTodoListメソッドでは、SwingUtilities.isEventDispatchThreadというメソッドが呼び出されている。
これは、実行されているスレッドがイベントディスパッチスレッドであればtureを、別のスレッドであればfalseを返す。
「SwingAppMain」を実行すると、コンソールに以下のように表示される。

createAndShorTodoListメソッドは、イベントディスパッチスレッド上で実行されていることが分かる。