Baumdarstellungen mit JTree
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 Klasse, in deren Konstruktor ein JFrame
erzeugt und die Methode initTree()
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 javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.SwingUtilities;
/**
* Zeigt einen sehr einfachen Baum
*
*/
public class VerySimpleTreeClass {
private JTree tree;
public VerySimpleTreeClass() {
JFrame frame = new JFrame("Ein sehr einfacher Baum");
initTree(frame);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
private void initTree(JFrame frame) {
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);
frame.add(new JScrollPane(tree));
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> 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.SwingUtilities;
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 implements TreeSelectionListener {
private JTree tree;
public SimpleTreeClass() {
JFrame frame = new JFrame("Ein einfacher Baum");
frame.setLayout(new BorderLayout());
initTree(frame);
JButton butt = new JButton("Anderes Model");
butt.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
tree.setModel(new MyModel(new MyLeaf("", "Wurzel")));
tree.updateUI();
}
});
frame.add(butt, BorderLayout.SOUTH);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
private void initTree(JFrame frame) {
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);
frame.add(new JScrollPane(tree), BorderLayout.CENTER);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> 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 String getLastName() {
return lastName;
}
}
class MyModel extends DefaultTreeModel {
public MyModel(DefaultMutableTreeNode node) {
super(node);
node.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;
}
}
Wenn Ihnen javabeginners.de gefällt, freue ich mich über eine Spende an diese gemeinnützigen Organisationen.