TreeViewのモデルとビューの作成を、前回までに進めました。
今回は、MVCパターンに沿うという意味で、TreeViewのコントローラを作ります。
SwingもそうですがJavaFXにもコントローラのAPIがあるわけではなく、開発者がゼロから作り上げるものです。
といっても、今回はとりあえず「TreeCellでラベル名の編集ができる」ことが目的なので、コントローラはあまり作り込んでいません。
package jfxtreeview;
import javafx.scene.control.SelectionMode;
import javafx.scene.control.TreeCell;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeView;
import javafx.util.Callback;
/**
*JavaFXの{@link javafx.scene.control.TreeView}を制御するコントローラです。
*/
public class TreeViewController {
/**
*コンストラクタです。
*@param treeview {@link javafx.scene.control.TreeView}オブジェクト
*/
public TreeViewController(TreeView<TreeItemData> treeview) {
//TreeViewのモデル(TreeItem)を構築する
final TreeItem<TreeItemData> rootNode = new TreeItem<>(new TreeItemData("Root", TreeItemData.Type.GROUP));
final TreeItem<TreeItemData> node0 = new TreeItem<>(new TreeItemData("Node0", TreeItemData.Type.GROUP));
final TreeItem<TreeItemData> node1 = new TreeItem<>(new TreeItemData("Node1", TreeItemData.Type.GROUP));
rootNode.setExpanded(true);
rootNode.getChildren().add(node0);
rootNode.getChildren().add(node1);
for(int i=0; i<3; i++){
node0.getChildren().add(new TreeItem<>(new TreeItemData("Node0"+i)));
node1.getChildren().add(new TreeItem<>(new TreeItemData("Node1"+i)));
}
//TreeItemのルートをTreeViewに設定する
treeview.setRoot(rootNode);
//独自TreeCellを生成するクラスを設定する
treeview.setCellFactory(new TreeViewCellFactory());
}
/**
*TreeItemのデータ名が変更されたときに呼び出されます。
*@param treeItem データ名が変更されたTreeItem
*@param name 新しいデータ名
*@return 新しいデータ名を反映したTreeItem (データ名の変更をキャンセルする場合はnullを返す)
*/
protected TreeItemData treeItemDataRenamed(TreeItem<TreeItemData> treeItem, String name){
return new TreeItemData(name, treeItem.getValue().getType());
}
/**
*独自のTreeCellを生成するファクトリクラスです。
*/
final class TreeViewCellFactory implements Callback<TreeView<TreeItemData>,TreeCell<TreeItemData>> {
@Override
public TreeCell<TreeItemData> call(TreeView<TreeItemData> treeview){
return new TreeCellImpl(TreeViewController.this);
}
}
}
ポイントは2つです。
1つめは、TreeView.setCellFactory()を使っている点です。これにより、「その2」で作成したTreeCellImplがTreeViewで使われるようになります。
2つめは、treeItemDataRenamed()です。これは「その3」のTreeCellGraphからコールバックされるものです。今回の例ではコールバックされるものはこの1つだけですが、たとえばTreeItemを追加したり削除したり、また移動したりする機能をTreeViewに追加する場合には、新たにコールバックされるメソッドを追加していきます。つまり、TreeViewの制御をコントローラが担うということです。
最後に、JavaFXのApplicationの派生クラスとFXMLの例を出して、このシリーズは終わりにしたいと思います。
ソースコード一式は、GitHubに上げておきました。
package jfxtreeview;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.control.TreeView;
import javafx.scene.image.Image;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;
public final class Main extends Application {
private TreeViewController controller;
public static void main(String[] args) {
Application.launch(args);
}
@Override
public void start(final Stage stage) {
try {
//ウィンドウのアイコンを設定
final Image img16 = new Image(getClass().getResourceAsStream("treeviewsample16.jpg"));
final Image img32 = new Image(getClass().getResourceAsStream("treeviewsample32.jpg"));
//GUI構築
final Pane root = (Pane)FXMLLoader.load(this.getClass().getResource("fxml001.fxml"));
this.controller =new TreeViewController((TreeView<TreeItemData>)root.lookup("#treeview"));
stage.setScene(new Scene(root));
stage.setTitle("TreeView Sample");
stage.getIcons().addAll(img16, img32);
stage.show();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.geometry.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.paint.*?>
<BorderPane prefHeight="400.0" prefWidth="600.0" xmlns:fx="http://javafx.com/fxml">
<left>
<TreeView fx:id="treeview" editable="true" prefHeight="200.0" prefWidth="200.0" showRoot="false" />
</left>
<center>
<TextArea fx:id="textarea" />
</center>
<padding>
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
</padding>
</BorderPane>
いつかはTreeViewでのDrag and Drop処理も紹介できたらいいなぁと考えていますが、実現するかどうか…^^;;