WisdomSoft - for your serial experiences.

6.10 ツリー

木構造のデータを表示するツリーコンポーネントを作成します。ツリーコンポーネントはルート項目から始まり、複数の子項目を展開可能な UI を表示します。さらに Swing では、項目を自由にカスタマイズできます。

6.10.1 木構造データを表示する

複雑なデータを管理するプログラムの多くは木構造型のデータを制御することになるでしょう。特に、個人情報管理ソフトウェアやファイル管理ソフトウェアなどが木構造を利用するソフトの代表でしょう。例えば、一般的なコンピュータのファイルシステムやネットワークのリソースの概念は、ディレクトリの中に子ディレクトリやファイルが存在する木構造の形をとっています。

このように、親と子が何段階にも重なり合っている階層情報を GUI に表示するにはツリーコンポーネントが最も適しています。ツリーコンポーネントを表示するには javax.swing.JTree クラスを利用します。

javax.swing.JTree クラス
java.lang.Object
  |
  +--java.awt.Component
        |
        +--java.awt.Container
              |
              +--javax.swing.JComponent
                    |
                    +--javax.swing.JTree
public class JTree extends JComponent implements Scrollable, Accessible

JTree クラスの機能は非常に豊富で、木構造のデータモデルやノードの提供、セルの描画、選択、編集などの多くの機能をインタフェースとして抽象化しているため、細かい拡張が可能になっています。

木構造のデータは、コンストラクタからツリーに指定することができますが、データの定義の方法が配列を用いる方法とデータモデルを使う方法に分かれます。静的にデータを表示することが目的で、データを動的に管理する必要がないのであれば、配列を使ってコンストラクタからデータを指定する方法が最も簡単でしょう。JTree クラスの基本的なコンストラクタには次のようなものがあります。

JTree クラスのコンストラクタ
public JTree()
public JTree(Object[] value)
public JTree(Vector value)
public JTree(Hashtable value)

引数を指定しない JTree() コンストラクタでインスタンスを生成すると、サンプルモデルを持つツリーが作られます。Object[] 型を渡した場合は、この配列の各要素をリーフとして登録しますが、要素が配列の場合は子ノードとしてさらに展開します。コンポーネントに表示されるのは、通常はオブジェクトの toString() メソッドが返す文字列です。Vector オブジェクトの場合も、Object[] 型と同様に、リーフとノードに展開されます。

Hashtable を指定すると、実際に表示される文字列と、項目が参照するオブジェクトを分けることができます。ツリーには、Hashtable のキーをコンポーネントに表示するオブジェクト、キーに対応する値を実際のリーフ、またはノードとして認識します。キーに対応する値が Object[]、Vector または Hashtable オブジェクトである場合はノードとして認識されさらに展開されます。

コード1
import java.util.*;
import javax.swing.*;

public class Test  {
	public static void main(String args[]) {
		Object obj[] = { "string item1" , "string item2" , args };

		Vector vector = new Vector();
		vector.add("vector item1");
		vector.add("vector item2");
		vector.add(new Test());

		Hashtable map1 = new Hashtable();
		Hashtable map2 = new Hashtable();

		map2.put("vector" , vector);
		map2.put("int 1" , new Integer(1));

		map1.put("array obj" , obj);
		map1.put("vector" , vector);
		map1.put("hashtable" , map2);
		map1.put("string empty" , "");

		JFrame win = new JFrame();
		win.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		win.setBounds(10 , 10 , 400 , 300);
		win.getContentPane().add(new JTree(map1));		
		win.show();
	}

	public String toString() { return "This is Test class instance"; }
}
実行結果
コード1 実行結果

コード1は、配列、Vector、Hashtable オブジェクトを組み合わせてツリーを定義しています。配列に他の配列を含めると、要素はノードであると認識され、さらに深い階層に進みます。配列以外のオブジェクトであれば、そのオブジェクトの文字列表現がリーフとして表示されるでしょう。デフォルトでは、リーフはファイルアイコン、ノードはディレクトリアイコンで表示されます。ディレクトリアイコンをクリックすると、ノードが展開され子ノードやリーフが表示されるでしょう。

例えば、Vector オブジェクトには文字列の項目と、Test クラスのインスタンスが追加されています。Test クラスはツリーに表示される文字列が toString() の戻り値であることを証明するため、toString() メソッドをオーバーライドしています。ツリーには、確かに toString() メソッドの戻り値が表示されていることを確認できるでしょう。

6.10.2 ツリーのノード

ツリーコンポーネントに表示されるノードやリーフは、データモデルとして定義されるインタフェースで表現することも可能です。ノードを表すデータモデルは javax.swing.tree.TreeNode インタフェースを実装します。javax.swing.tree 名前空間には、JTree クラスに関連するクラスやインタフェースが定義されています。

表1 TreeNode インタフェースのメソッド
メソッド 解説
public TreeNode getChildAt(int childIndex) 指定したインデックスの子 TreeNode を返す。
public int getChildCount() 格納する子 TreeNode の数を返す。
public TreeNode getParent() 親 TreeNode を返す。
public int getIndex(TreeNode node) node のインデックスを返します。node を格納しない場合は、-1 を返す。
public boolean getAllowsChildren() 子を許可する場合は true を返す。
public boolean isLeaf() 葉である場合は true を返す。
public Enumeration children() 子を Enumeration で返す。

TreeNode の役割は複雑なものではありません。ノードは入れ子にすることができるため、TreeNode インタフェースは自らを中心として相対的な親と子ノードを提供します。親ノードは最大でも 1 つまでしか持てませんが、子ノードは 0 から任意の数を保有することができます。特定のノードからその親子関係を辿れば、最終的に木構造全体を把握することができます。

任意のタイミングで親ノードや子ノードの情報を変更することができるノードを定義する場合は javax.swing.tree.MutableTreeNode インタフェースを用いると良いでしょう。このインタフェースは TreeNode インタフェースを継承し、親ノードや子ノードを変更するメソッドが追加されています。

表2 MutableTreeNode インタフェースのメソッド
メソッド 解説
public void insert(MutableTreeNode child, int index) child を index のレシーバに追加する。
public void remove(int index) レシーバから index の子を削除する。
public void remove(MutableTreeNode node) レシーバから node を削除する。
public void setUserObject(Object object) レシーバのユーザオブジェクトを object にリセットする。
public void setParent(MutableTreeNode newParent) レシーバの親を newParent に設定する。
public void removeFromParent() レシーバをその親から削除する。

MutableTreeNode オブジェクトを insert() メソッドで追加したり、remove() メソッドで子ノードから解除すると、子ノードとして操作されたノードは setParent() メソッドを通してメッセージを受けることができます。

これらのインタフェースを実装する Swing デフォルトのクラスには javax.swing.tree.DefaultMutableTreeNode クラスがあります。

javax.swing.tree.DefaultMutableTreeNode クラス
java.lang.Object
  |
  +--javax.swing.tree.DefaultMutableTreeNode
public class DefaultMutableTreeNode extends Object
	implements Cloneable, MutableTreeNode, Serializable

このクラスをノードとして利用すれば、ツリーコンポーネントのノードを動的に制御することが容易になるでしょう。このクラスのコンストラクタには次のようなものが定義されています。

DefaultMutableTreeNode クラスのコンストラクタ
public DefaultMutableTreeNode()
public DefaultMutableTreeNode(Object userObject)
public DefaultMutableTreeNode(Object userObject, boolean allowsChildren)

userObject にはこのノード、またはリーフが保有するオブジェクトを指定します。ツリーコンポーネントには、このオブジェクトの文字列表現が表示されるでしょう。allowsChildren には、このノードが子ノードを保有することができるかどうかを指定します。これに flase を指定した場合は、確実にリーフになります。また、子ノードを保有できる場合でも、子ノードが設定されていなければリーフであると判断されることもあります。

TreeNode オブジェクトをツリーコンポーネントに設定するには、JTree クラスの次のコンストラクタからルートノードとなるノードを設定します。

JTree クラスのコンストラクタ
public JTree(TreeNode root)
public JTree(TreeNode root, boolean asksAllowsChildren)

root にはこのツリーのルートノードを設定します。asksAllowsChildren を true に設定すると子を許可しないノードだけがリーフと認識され false を指定すると子を持たないすべてのノードがリーフとして認識されます。

コード2
import javax.swing.*;
import javax.swing.tree.*;

public class Test  {
	public static void main(String args[]) {
		MutableTreeNode node1 = new DefaultMutableTreeNode("node1");
		MutableTreeNode node2 = new DefaultMutableTreeNode("node2");
		MutableTreeNode leaf[] = {
			new DefaultMutableTreeNode("leaf1" , false) ,
			new DefaultMutableTreeNode("leaf2" , false) ,
			new DefaultMutableTreeNode("leaf3" , false)
		};

		node1.insert(node2 , 0);
		node1.insert(leaf[0] , 1);

		node2.insert(leaf[1] , 0);
		node2.insert(leaf[2] , 1);

		JFrame win = new JFrame();
		win.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		win.setBounds(10 , 10 , 400 , 300);
		win.getContentPane().add(new JTree(node1));		
		win.show();
	}
}
実行結果
コード2 実行結果

コード2は、TreeNode インタフェースの実装を用いてツリーコンポーネントのデータを定義しています。MutableTreeNode インタフェースを実装する DefaultMutableTreeNode クラスのオブジェクトを用いれば、動的にノードを追加したり、削除したりすることができます。また、データ配列を用いた場合とは異なり、ノードのインデックスなどを詳細に設定できる点でも、より確実なデータ定義が可能です。

6.10.3 ツリーモデル

通常、ツリーは TreeNode をノードとして管理されるべきであると考えられますが、ツリーモデルを独自に実装することで、任意のオブジェクトを管理するツリーを定義することができます。データの型を抽象化してノードを管理するには javax.swing.tree.TreeModel インタフェースを使います。

表3 TreeModel インタフェースのメソッド
メソッド 解説
public void addTreeModelListener(TreeModelListener l) TreeModelEvent のリスナを追加する。
public void removeTreeModelListener(TreeModelListener l) 設定されている指定したリスナを削除する。
public Object getRoot() ツリーのルートを返す。ノードがない場合は、null を返す。
public Object getChild(Object parent, int index) インデックス index にある parent の子を返す。
public int getChildCount(Object parent) parent の子の数を返す。
public boolean isLeaf(Object node) node がリーフの場合は true を返す。
public void valueForPathChanged(TreePath path, Object newValue) newValue への path によって識別される項目の値をユーザが変更すると、メッセージで送信される。
public int getIndexOfChild(Object parent, Object child) 親における子のインデックスを返す。

表3に TreeModel インタフェースのメソッドを列挙します。宣言されているメソッドを見てわかるように、ノードやリーフを定義する型を Object 型として扱うため、様々なデータに対応することができます。通常は、TreeNode オブジェクトがノードとなるでしょう。

alueForPathChanged() メソッドでは TreePath 型が使われていますが、これはツリー上の特定のノードのパスを表すクラスです。このクラスについてはツリー項目の選択と一緒に詳しく説明します。

TreeModel インタフェースの Swing デフォルトの実装クラスは javax.swing.tree.DefaultTreeModel クラスです。

javax.swing.tree.DefaultTreeModel クラス
java.lang.Object
  |
  +--javax.swing.tree.DefaultTreeModel
public class DefaultTreeModel extends Object implements Serializable, TreeModel

このクラスは TreeNode オブジェクトを使ってツリーを表現する基本的なモデルの実装です。

6.10.4 ツリー項目の選択

ツリーコンポーネントに対するユーザーからの入力で最も重要なのはノード、またはリーフの選択でしょう。現在、選択されている項目の情報を管理しているのは javax.swing.tree.TreeSelectionModel インタフェースを実装するオブジェクトです。選択項目の情報提供を独自に実装したい場合はこのインタフェースを実装し、setSelectionModel() メソッドでモデルを変更することができます。現在の選択モデルは getSelectionModel() メソッドから取得することができます。

JTree クラス setSelectionModel() メソッド
public void setSelectionModel(TreeSelectionModel selectionModel)
JTree クラス getSelectionModel() メソッド
public TreeSelectionModel getSelectionModel()

selectionModel パラメータには新しい TreeSelectionModel オブジェクトを指定します。これを null に設定すると一切の選択を許可しないツリーを作成することができます。通常は、選択を目的としないツリーを作成するときに null を設定するくらいで、特殊な事情がない限り選択モデルを独自に実装する必要はありません。

ツリーのノードやリーフの選択状態が変更されると、リスナとして登録されている javax.swing.event.TreeSelectionListener インタフェースvalueChanged() メソッドが呼び出されます。このリスナを登録しておけば、ツリーの選択状態が変更されたことを知ることができます。

TreeSelectionListener インターフェイス valueChanged() メソッド
public void valueChanged(TreeSelectionEvent e)

e パラメータに渡されるオブジェクトは javax.swing.event.TreeSelectionEvent クラスのオブジェクトです。

javax.swing.event.TreeSelectionEvent クラス
java.lang.Object
  |
  +--java.util.EventObject
        |
        +--javax.swing.event.TreeSelectionEvent
public class TreeSelectionEvent extends EventObject

このクラスは、新しく選択、または削除されたノードまでのパスを提供します。ツリーは選択モデルによっては複数のノードを選択することもできるため、getPath() メソッドで選択されている最初のパスを、getPaths() メソッドで追加、または削除されたパスを得ることができます。

TreeSelectionEvent クラス getPath() メソッド
public TreePath getPath()
TreeSelectionEvent クラス getPaths() メソッド
public TreePath[] getPaths()

これらのメソッドが返すノードの位置を表す情報は javax.swing.tree.TreePath クラスのオブジェクトです。TreePath クラスは木構造のノードやリーフの位置を正確に参照するための情報を提供します。このオブジェクトは、選択されているルートから対象ノードまでのオブジェクトの配列を内部で管理しています。

表4 TreePath クラスのコンストラクタとメソッド
コンストラクタ 解説
public TreePath(Object singlePath) 単一要素のみを持つパスを作成する。
public TreePath(Object[] path) Object の配列からパスを作成する。
メソッド
public Object getLastPathComponent() このパスの末尾コンポーネントを返す。
public Object[] getPath() パスのコンポーネントを格納する Object の順序付き配列を返す。
public int getPathCount() パス内の要素数を返す。
public Object getPathComponent(int element) 指定されたインデックスにあるパスのオブジェクトを返す。
public boolean isDescendant(TreePath aTreePath) aTreePath がこのパスの下位オブジェクトである場合は true を返す。
public TreePath pathByAddingChild(Object child) このパスに加えて child 要素を格納する新しいパスを返す。
public TreePath getParentPath() オブジェクトのすべての要素を格納するパスを返す。

TreePath オブジェクトを利用すれば、選択されている項目までのノードを個別に調べることも簡単です。目的が選択されたノードやリーフだけで、その過程にこだわらない場合は getLastPathComponent() メソッドを使えばよいでしょう。

コード3
import java.awt.*;
import javax.swing.*;
import javax.swing.event.*;

public class Test extends JFrame implements TreeSelectionListener {
	public static void main(String args[]) {
		JFrame win = new Test();
		win.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		win.setBounds(10 , 10 , 400 , 300);
		win.show();
	}

	private JTree tree;
	private JLabel label = new JLabel("項目を選択してください");
	public Test() {
		Object node[] = {
			new String[] { "leaf1" , "leaf2" } ,
			new String[] { "leaf3" }
		};
		tree = new JTree(node);
		tree.addTreeSelectionListener(this);

		getContentPane().add(tree);
		getContentPane().add(label , BorderLayout.SOUTH);
		
	}
	public void valueChanged(TreeSelectionEvent e) {
		label.setText(e.getPath().getLastPathComponent().toString());
	}
}
実行結果
コード3 実行結果

コード3は、ツリーに TreeSelectionListener オブジェクトを追加してノードの選択を監視しています。新しいノードが選択されると、ウィンドウ下部に表示されているラベルに、現在選択されている最初のノードを表示します。

6.10.5 ツリーのセルレンダラ

リストなどと同様に、ツリーのノードもセルレンダラを保有しています。ツリーのセルレンダラの役割は、ノードの状態に応じてノードを適切に描画するコンポーネントを返すことです。概念的には、リストのセルレンダラと変わりはありません。

ツリーのセルレンダラは javax.swing.tree.TreeCellRenderer インタフェースを実装します。このインタフェースが宣言しているメソッドは getTreeCellRendererComponent() メソッドだけです。このメソッドは、引数に与えられた情報にしたがって、ノードを描画するコンポーネントを返します。

Item name...
public Component getTreeCellRendererComponent(
	JTree tree, Object value,
	boolean selected, boolean expanded, boolean leaf,
	int row, boolean hasFocus
)

tree パラメータには、ノードを描画する JTree オブジェクト、value パラメータは描画するノードのオブジェクトが設定されます。selected パラメータが true であればノードは選択状態であり、expanded パラメータが true であればノードは展開されていると考えられます。leaf パラメータが true であれば項目はリーフであると考えられます。row パラメータにはノードの行インデックスを表し、hasFocus パラメータが true ならば、セルはフォーカスを保有しています。

このメソッドを実装し、Jtree クラスの setCellRenderer() メソッドを用いてセルレンダラを設定すると、ツリーを描画する必要があるときにレンダラが呼び出されます。現在設定されているセルレンダラを取得したければ getCellRenderer() メソッドを使いましょう。

Jtree クラス setCellRenderer() メソッド
public void setCellRenderer(TreeCellRenderer x)
Jtree クラス getCellRenderer() メソッド
public TreeCellRenderer getCellRenderer()

ノードの描画が必要になれば、描画するノードごとに getTreeCellRendererComponent() メソッドが呼び出されます。JLabel 等を用いてノードを描画するコンポーネントを返してください。

コード4
import java.util.*;
import java.awt.*;
import javax.swing.*;
import javax.swing.tree.*;

public class Test implements TreeCellRenderer {
	public static void main(String args[]) {
		Object obj[] = { "item1" , "item2" , "item3" };

		Hashtable map = new Hashtable();
		map.put("node" , obj);

		JTree tree = new JTree(map);
		tree.setCellRenderer(new Test());

		JFrame win = new JFrame();
		win.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		win.setBounds(10 , 10 , 400 , 300);
		win.getContentPane().add(tree);		
		win.show();
	}

	public Component getTreeCellRendererComponent(
			JTree tree, Object value,
			boolean selected, boolean expanded, boolean leaf,
			int row, boolean hasFocus) {
		String text = expanded ? "↓ " : "← ";
		text += leaf ? "◆ " : "■ ";
		JLabel label = new JLabel(text + value.toString());
		label.setForeground(selected ? Color.RED : Color.BLACK);
		return  label;
	}
}
実行結果
コード4 実行結果

コード4は、独自に実装したツリーのセルレンダラを使って、ツリーに表示されるノードをカスタマイズしています。このセルレンダラはオブジェクトの文字列表現でノードを描画し、展開状態とリーフであるかどうかを記号で表現しています。また、ノードが選択されている場合は赤、そうでなければ黒色の文字で表示します。

この機能を使えば、文字列だけではなく、オブジェクトの型に応じて適切な情報をノードに表示させることができるようになります。例えば、イメージの木構造を管理するツリーが、小さなサムネイル画像ををノードとして表示するといったことが可能になります。

6.10.6 ツリーの編集

ファイルシステムのディレクトリに同期したツリーを表示する場合、ファイル名やディレクトリ名を変更するには、プログラムのツリーコンポーネントから直接編集できると便利です。ツリーのノードは、単純に表示するだけではなく、編集用コンポーネントを表示してユーザーがデータを入力することができます。

ツリーが編集可能かどうかは、JTree クラスの setEditable() メソッドから設定します。この設定を true にすることで、ユーザーがツリーのノードを編集することができるようになります。現在のツリーが編集可能かどうかについては isEditable() メソッドから得ることができます。

JTree クラス setEditable() メソッド
public void setEditable(boolean flag)
JTree クラス isEditable() メソッド
public boolean isEditable()

デフォルトのツリーのセルレンダラは、ノードの値をテキストとして表示するため、これに対応してデフォルトのツリーの編集用エディタはテキスト入力用コンポーネントを表示します。テキスト入力用コンポーネントの詳細は Chapter8 を参照してください。

コード5
import java.util.*;
import javax.swing.*;

public class Test  {
	public static void main(String args[]) {
		Hashtable map = new Hashtable();
		Object obj[] = { "string item1" , "string item2" };
		map.put("Object" , obj);

		JTree tree = new JTree(map);
		tree.setEditable(true);

		JFrame win = new JFrame();
		win.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		win.setBounds(10 , 10 , 400 , 300);
		win.getContentPane().add(tree);		
		win.show();
	}
}
実行結果
コード5 実行結果

コード5は、生成した JTree コンポーネントの setEditable() メソッドから true を指定し、ツリーを編集可能に設定しています。選択状態のノードをさらにクリックすると、図 06_10_04 のようにテキストが入力できるようになります。テキストを編集して決定キー(Enter など)を押すと、ノードの値が入力したテキストに変更されます。

しかし、ノードの値は Object 型として扱うため、事実上あらゆるデータ型をツリーに表示することができます。通常は toString() が返す文字列をノードに表示しますが、これは TreeCellRenderer インタフェースを独自に実装することで、異なる方法でノードを表示することができました。

ところが、編集可能なツリーはテキストとしてデータを編集、設定するため、イメージなどテキスト以外のデータを扱うツリーでは、レンダラと編集の相性が問題となってしまいます。そこで、ツリーの編集を行うセルエディタを独自に拡張する方法があります。

セルエディタとは、ノードを編集するためのコンポーネントや編集結果となるデータを提供する javax.swing.tree.TreeCellEditor インタフェースを実装するオブジェクトを指します。TreeCellEditor インタフェースは、編集用コンポーネントを返すgetTreeCellEditorComponent() メソッドを宣言しています。

TreeCellEditor インターフェイス getTreeCellEditorComponent() メソッド
public Component getTreeCellEditorComponent(JTree tree, Object value, boolean isSelected, boolean expanded, boolean leaf, int row)

このメソッドが受け取る引数は、tree に編集を要求しているツリー、value に編集されるセルの値が格納されています。isSelected が選択状態、expanded がノードが展開されているかどうか、leaf には葉ノードかどうか、row にはノードの行番号が格納されています。これらの情報から、セルエディタは適切な編集用コンポーネントを返さなければなりません。

ただし、TreeCellEditor インタフェースは、編集可能なセルのエディタを提供する総合的な javax.swing.CellEditor インタフェースを継承しています。 getTreeCellEditorComponent() メソッド以外に、表5に示すメソッドを実装しなければなりません。

表5 CellEditor インタフェースのメソッド
メソッド 解説
public void addCellEditorListener(CellEditorListener l) エディタが編集の停止、または取り消しを行うときに通知を受けるリスナーを追加する。
public void removeCellEditorListener(CellEditorListener l) 通知を受けるリスナーをリストから削除する。
public Object getCellEditorValue() エディタに保持された値を返す。
public boolean isCellEditable(EventObject anEvent) anEvent を使って編集を始められるかどうかをエディタに問い合わせる。
public boolean shouldSelectCell(EventObject anEvent) 編集セルを選択する場合は true を返し、そうでない場合は false を返す。
public boolean stopCellEditing() 編集を停止して値を受け付けるように、エディタに通知する。
public void cancelCellEditing() 編集を取り消して値を受け付けないようにエディタに通知する。

セルが編集可能かどうかについては、CellEditor インタフェースの isCellEditable() メソッドも関与しています。JTree クラスの isEditable() メソッドのように、編集を要求するコンポーネントの編集可能性と、エディタの編集可能性の両方が満たされると、セルを編集することができます。shouldSelectCell() メソッドは、イベントがセルを選択するべきかどうかを返します。

stopCellEditing() メソッドが呼び出されると、セルの編集を終了し、結果を保存しなければなりません。stopCellEditing() メソッドが true を返すと編集が中止されたことを表します。編集が終了すると、getCellEditorValue() メソッドから、セルに保存する最終的な編集結果を返します。

CellEditor インタフェースを実装するには、このインタフェースで宣言されているメソッドを単純実装している javax.swing.AbstractCellEditor クラスを継承すると良いでしょう。

javax.swing.AbstractCellEditor クラス
java.lang.Object
  |
  +--javax.swing.AbstractCellEditor
public abstract class AbstractCellEditor extends Object implements CellEditor, Serializable

このクラスは、isEditable() メソッドと shouldSelectCell()、stopCellEditing() メソッドの結果は常に true を返します。リスナの登録や解除なども実装しているため、後は getCellEditorValue() メソッドとTreeCellEditor インタフェースを実装すれば独自のエディタを作成することができます。

セルエディタが用意できれば、JTree クラスの setCellEditor() メソッドから設定することができます。現在設定されているセルエディタは getCellEditor() メソッドから得ることができます。

JTree クラス setCellEditor() メソッド
public void setCellEditor(TreeCellEditor cellEditor)
JTree クラス getCellEditor() メソッド
public TreeCellEditor getCellEditor()

セルエディタに null を設定した場合は、ツリーが編集できないことを示します。

コード6
import java.util.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.tree.*;

public class Test  {
	public static void main(String args[]) {
		Hashtable map = new Hashtable();
		Object obj[] = { new Boolean(true) , new Boolean(false) };
		map.put("Object" , obj);

		JTree tree = new JTree(map);
		tree.setEditable(true);
		tree.setCellEditor(new BooleanEditor());

		JFrame win = new JFrame();
		win.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		win.setBounds(10 , 10 , 400 , 300);
		win.getContentPane().add(tree);		
		win.show();
	}
}

class BooleanEditor extends AbstractCellEditor implements TreeCellEditor {
	JCheckBox check = new JCheckBox();

	public BooleanEditor() {
		check.addKeyListener(new KeyAdapter() {
			public void keyReleased(KeyEvent e)  {
				if (e.getKeyCode() == KeyEvent.VK_ENTER)
					stopCellEditing();
			}
		});
	}

	public Component getTreeCellEditorComponent(
			JTree tree,Object value, boolean isSelected,
			boolean expanded, boolean leaf,  int row) {
		check.setText(value.toString());
		return check;
	}
	public Object getCellEditorValue() {
		return new Boolean(check.isSelected());
	}
}
実行結果
コード6 実行結果

コード6は、AbstractCellEditor クラスを継承し、TreeCellEditor インタフェースを実装するツリーのセル編集用コンポーネントを提供する BooleanEditor クラスを定義しています。このクラスは JTree クラスに setCellEditor() メソッドから BooleanEditor クラスのオブジェクトを設定し、Boolean オブジェクトを編集するセルエディタを表示します。

BooleanEditor クラスは、getTreeCellEditorComponent() メソッドで JCheckBox コンポーネントを返し、常に Boolean 型のオブジェクトを編集します。このクラスは、セルエディタの実装方法を学習するための簡単な実装なので、セルの情報を調べずに、セルのデータ型や種類が何であれ Boolean 型として編集しようとします。leaf 引数や value のデータ型に応じて適切な編集用コンポーネントを返すように工夫すれば、より実用的なセルエディタを実現できるでしょう。

編集した結果をセルに保存するには、何らかのアクションに反応して stopCellEditing() メソッドを呼び出して編集を終了しなければなりません。編集が適切に終了すれば getCellEditorValue() が返す値がセルに設定されるでしょう。このプログラムでは、キーボードの入力イベントで VK_ENTER コードのキーが離されたタイミングで編集を決定します。