Ein durch die Klasse
JTree repräsentierter Datenbaum besteht im
Wesentlichen aus drei Hauptelementen:
- dem Inhalt des Baumes, dem sog. Model, d.h. den Daten, die im Baum
dargestellt werden
- der Erscheinung des Baumes, d.h. der Swing-Komponente JTree
- einem Renderer, der für die detaillierte Darstellung und Manipulation
der Baumknoten verantwortlich ist.
Im einfachsten Fall stellt Java jedoch diese Elemente in einer
Standardvariante bereit, sodass man sich gar nicht oder nur rudimentär darum
kümmern muss. Betrachten wir einen solchen Fall im Beispiel unten.
Es
zeigt eine von
JFrame abgeleitete Klasse, deren Besonderheit
lediglich in der Methode
initTree() besteht, die im Konstruktor
aufgerufen wird.
Der Ablauf der Routinen in ihr ist charakteristisch
für die Erzeugung eines Baumes:
- Erzeugung eines Wurzelknotens
- Herstellung eines leeren Models mit der soeben erzeugten Wurzel
- Füllen des Models, hier am Beispiel des Hinzufügens eines Elementes
- Generierung des JTree mit dem erzeugten Model
Es fällt auf, dass der o.a. Renderer im Quelltext gar nicht auftaucht.
Vielmehr wird der Baum in der Standard-Darstellung erzeugt.
Die beiden
folgenden Methodenaufrufe sind selbst erklärend: Sie bewirken, dass der
Wurzelknoten sichbar ist und dass neben mit Unterelementen versehenen
Einträgen ein Zeichen - je nach Look-and-Feel oft ein Dreieck - zum
'Aufklappen' des Baumabschnitts erscheint. Das letzte Statement der Methode
fügt den Baum einem
JScrollPane hinzu, das wiederum in den Frame
eingebettet wird. Dies ist notwendig, um den Baum bei entsprechender Länge
scrollfähig zu machen.
import java.awt.BorderLayout;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTree;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
public class VerySimpleTreeClass extends JFrame {
private JTree tree;
public VerySimpleTreeClass() {
initTree();
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setExtendedState(JFrame.MAXIMIZED_BOTH);
this.setLocationRelativeTo(null);
this.setTitle("Ein sehr einfacher Baum");
this.setVisible(true);
}
private void initTree() {
DefaultMutableTreeNode root = new DefaultMutableTreeNode("Root");
DefaultTreeModel model = new DefaultTreeModel(root);
root.add(new DefaultMutableTreeNode("Version 1"));
tree = new JTree(model);
tree.setRootVisible(true);
tree.setShowsRootHandles(true);
this.add(new JScrollPane(tree));
}
public static void main(String[] args) {
new VerySimpleTreeClass();
}
}
Das zweite Beispiel demonstriert einige Möglichkeiten der individuellen
Konfiguration eines
JTree. Hierbei sollen
- eine eigene Klasse für die Elemente (=Knoten, Blätter) des Baumes
erzeugt werden
- die Baum-Knoten auf die Auswahl reagieren
- die Elemente in einem explizit deklarierten Model gespeichert werden
- das Model gewechselt werden können
- ein Renderer zur Formatierung eines Baumeintrags erstellt werden
Die Knoten
Die Klasse der Baumknoten wird von DefaultMutableTreeNode
abgeleitet. Sie definiert lediglich zwei String-Instanzvariablen, die über
den Konstruktor initialisiert werden.
Das Model
Das Model erweitert DefaultTreeModel und übergibt sein
Konstruktor-Attribut vom Typ DefaultMutableTreeNode als
Wurzelknoten an den Superkonstruktor. Zur Demonstration wird anschließend
ein weiterer Knoten als Blatt angefügt.
Der Renderer
Er ist abgeleitet von der Klasse DefaultTreeCellRenderer, die
wiederum das Interface TreeCellRenderer implementiert. Die hier
deklarierte Methode getTreeCellRendererComponent() besitzt eine
Reihe von Attributen, die den Baum selbst, den Inhalt eines Knoten-Objekts
und diverse seiner Eigenschaften repräsentieren. Sie können abgefragt werden
und auf diese Weise die Eigenschaften der Knoten selbst modifizieren.
Der 'Zusammenbau' des Baumes erfolgt wie im ersten Beispiel in der Methode initTree().
Die Unterschiede neben der Anpassung der neuen Typen für das Model und die
Knoten bestehen im Hinzufügen des Renderers und der Anmeldung des Baumes bei
einem TreeSelectionListener. In dessen Methode valueChanged()
werden die Eigenschaften der selektierten Knoten abgefragt und auf der
Konsole ausgegeben.
Ein Wechsel des Models erfolgt im einfachsten Fall, wie im Beispiel, über
ein beliebiges Event (hier ein ActionEvent eines Buttons), das das Setzen
des neuen Models mittels JTree.setModel() anstößt. Wichtig ist
hier, dass das Neuladen des Tree-UserInterface durch updateUI()
erfolgt.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTree;
import javax.swing.border.LineBorder;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.DefaultTreeModel;
public class SimpleTreeClass extends JFrame implements TreeSelectionListener {
private JTree tree;
public SimpleTreeClass() {
this.setLayout(new BorderLayout());
initTree();
JButton butt = new JButton("Anderes Model");
butt.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
tree.setModel(new DefaultTreeModel(new DefaultMutableTreeNode("Wurzel")));
tree.updateUI();
}
});
this.add(butt, BorderLayout.SOUTH);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.pack();
this.setLocationRelativeTo(null);
this.setTitle("Ein einfacher Baum");
this.setVisible(true);
}
private void initTree() {
MyLeaf root = new MyLeaf("", "Mitarbeiter");
DefaultTreeModel model = new MyModel(root);
root.add(new MyLeaf("Paul", "Meier"));
tree = new JTree(model);
tree.addTreeSelectionListener(this);
tree.setCellRenderer(new MyRenderer());
tree.setRootVisible(true);
tree.setShowsRootHandles(true);
this.add(new JScrollPane(tree), BorderLayout.CENTER);
}
public static void main(String[] args) {
new SimpleTreeClass();
}
public void valueChanged(TreeSelectionEvent e) {
MyLeaf node = (MyLeaf) tree.getLastSelectedPathComponent();
if (node != tree.getModel().getRoot() && node != null)
System.out.println(((MyLeaf) node).getFirstName() + " "
+ ((MyLeaf) node).getLastName());
}
}
class MyLeaf extends DefaultMutableTreeNode {
String firstName, lastName;
public MyLeaf(String firstName, String lastName) {
super(lastName);
this.firstName = firstName;
this.lastName = lastName;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
}
class MyModel extends DefaultTreeModel {
public MyModel(DefaultMutableTreeNode node) {
super(node);
DefaultMutableTreeNode wurzel = node;
wurzel.add(new MyLeaf("Karl", "Schmitz"));
}
}
class MyRenderer extends DefaultTreeCellRenderer {
public MyRenderer() {
setTextSelectionColor(new Color(120, 120, 120));
setOpaque(true);
}
public Component getTreeCellRendererComponent(JTree tree, Object value,
boolean sel, boolean expanded, boolean leaf, int row,
boolean hasFocus) {
super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf,
row, hasFocus);
if (sel)
this.setBorder(new LineBorder(Color.BLACK));
else
this.setBorder(null);
if (leaf) {
this.setBackground(Color.RED);
} else {
this.setBackground(Color.GREEN);
}
if (sel) {
this.setBackground(Color.YELLOW);
}
return this;
}
}