Obsah
Strom (arbor) je růstová forma vyšších rostlin.
Prýt (nadzemní část) stromu se skládá ze zdřevnatělé nevětvené spodní části - kmene, který se v určité výšce nad zemí dělí na jednotlivé větve (na rozdíl od keře, kde k větvení dochází již u země, nebo těsně nad zemí). Horní část stromu, kde dochází k větvení, se nazývá koruna.
Přesnou definici pojmu strom (nebo keř) není možné vymezit kvůli velké diverzitě rostlin. Někdy je proto výhodnější používat termín stromovitá forma.
Různý způsob větvení dává každému druhu charakteristický tvar. Tento tvar může být dále ovlivněn prostředím, ve kterém strom roste (zda roste osamoceně nebo uvnitř porostu - lesa).
V GUI je to komponenta, která zobrazuje hierarchická data
http://java.sun.com/docs/books/tutorial/uiswing/components/tree.html
http://java.sun.com/javase/6/docs/api/javax/swing/JTree.html
základní třída javax.swing.JTree
cca tucet podpůrných tříd a rozhraní jsou v balíku
javax.swing.tree
kromě nich jsou v balíku javax.swing
další podpůrné
třídy a rozhraní společné i pro JTable
CellEditor
ListCellRenderer
ListSelectionModel
Renderer
AbstractCellEditor
je to velmi podobné JTable
je nutné mít dvou (tří) stupňovou organizaci
na první (datové) úrovni je datový model
typicky splňuje rozhraní
javax.swing.tree.TreeModel
je to rozhraní mezi datovou a prezentační vrstvou
naprosto nejjednodušší je použít
DefaultTreeModel
na druhé (prezentační) úrovni je JTree
jako
vizuální komponenta
typicky v konstruktoru přebírá datový model
new JTree(mujTreeModel);
Swing nepoužívá termíny datová a prezentační vrstva, ale model a view -- resp. nepoužívá ani termín view (ale myslí se to tak)
Termíny TBD
node — uzel
root — node from which all nodes descend — kořen
branch node — nodes that can have children — whether or not they currently have children
leaf node — node that can not have children
expanded node — non-leaf node (as identified by TreeModel.isLeaf(node) returning false) that will displays its children
colapsed node — one which hides its children
hidden node — under a collapsed ancestor
viewable nodes — parents are expanded, but may or may not be displayed
displayed node — both viewable and in the display area
node může být identifikován buď pomocí
TreePath
objektu nebo
řádkou ve kterém se node nachází
elementární příklad:
//Where instance variables are declared: private JTree tree; ... public TreeDemo() { ... DefaultMutableTreeNode top = new DefaultMutableTreeNode("The Java Series"); createNodes(top); tree = new JTree(top); ... JScrollPane treeView = new JScrollPane(tree); ... }
přidání uzlů je intuitivní
private void createNodes(DefaultMutableTreeNode top) { DefaultMutableTreeNode category = null; DefaultMutableTreeNode book = null; category = new DefaultMutableTreeNode("Books for Java Programmers"); top.add(category); //original Tutorial book = new DefaultMutableTreeNode(new BookInfo ("The Java Tutorial: A Short Course on the Basics", "tutorial.html")); category.add(book); //Tutorial Continued book = new DefaultMutableTreeNode(new BookInfo ("The Java Tutorial Continued: The Rest of the JDK", "tutorialcont.html")); category.add(book); ... }
parametrem konstruktoru DefaultMutableTreeNode
je
tzv. user object -- to je buď
String
nebo
objekt který má vhodně překrytou metody
toString
pokud není možné překrýt toString
(například protože toString
je využíváne v
programu i někde jinde, kde jsou na ni kladeny jiné
požadavky než by se hodilo v JTree
), tak se
musí přetížit (přepsat defaultní) metoda
convertValueToText
třídy
JTree
.
není třeba řešit expand a collapse funkcionalitu
naopak je třeba řešit co se stane když uživatel vybere nějakou položku:
nastavíte selection mode
implementujete posluchače a zaregistrujete ho
v posluchači implementujete metodu
valueChanged
(nebo jinou - v závislosti na vybranem
selection módu)
//Where the tree is initialized: tree.getSelectionModel().setSelectionMode (TreeSelectionModel.SINGLE_TREE_SELECTION); //Listen for when the selection changes. tree.addTreeSelectionListener(this); ... public void valueChanged(TreeSelectionEvent e) { // Returns the last path element of the selection. // This method is useful only when the selection // model allows a single selection. DefaultMutableTreeNode node = (DefaultMutableTreeNode) tree.getLastSelectedPathComponent(); if (node == null) return; //Nothing is selected. Object nodeInfo = node.getUserObject(); if (node.isLeaf()) { BookInfo book = (BookInfo)nodeInfo; displayURL(book.bookURL); // TOHLE je ta užitečná akce kterou chceme provést } else { displayURL(helpURL); // i TOHLE je ta užitečná akce kterou chceme provést } }
metoda setRootVisible(true)
metoda setShowsRootHandles(true)
metoda tree.putClientProperty("JTree.lineStyle",
"Horizontal");
pro změnu ikony se (podobně jako u
JTable
) používá render čili
zobrazovač -- třída implementující rozhraní
TreeCellRenderer
-- standardně
DefaultTreeCellRenderer
a jeho tři metody:
setLeafIcon
(for leaf nodes)
setOpenIcon
(for expanded branch
nodes)
setClosedIcon
(for collapsed branch
nodes)
ImageIcon leafIcon = createImageIcon("images/middle.gif"); // viz 3/4 přednáška if (leafIcon != null) { DefaultTreeCellRenderer renderer = new DefaultTreeCellRenderer(); renderer.setLeafIcon(leafIcon); tree.setCellRenderer(renderer); }
pro tool tip je třeba oddědit od
DefaultTreeCellRenderer
a překrýt metodu
getTreeCellRendererComponent
protože DefaultTreeCellRenderer
je potřída
JLabel
lze používat metody jako
setIcon
, setToolTipText
a další
pro dynamické přidáváni a ubírání uzlů, například:
inicializace stromu:
rootNode = new DefaultMutableTreeNode("Root Node"); treeModel = new DefaultTreeModel(rootNode); treeModel.addTreeModelListener(new MyTreeModelListener()); // moje vlastní třída tree = new JTree(treeModel); tree.setEditable(true); // < ------------------------------- TADY tree.getSelectionModel().setSelectionMode (TreeSelectionModel.SINGLE_TREE_SELECTION); tree.setShowsRootHandles(true);
model je instance třídy DefaultMutableTreeNode a díky kouzelnému
slovíčku mutable lze používat metody jako
insertNodeInto
(tato metoda není deklarovana v
jednodušším rozhraní TreeModel
)
Ačkoliv DefaultMutableTreeNode
má metodu pro
změnu obsahu uzlu, je třeba poslat změnu přes metody třídy
DeafultTreeModel
, jinak se nevygenerují příslušné
události a posluchači (jako např. strom sám) by se o změně
nedozvědeli.
Změna jména uzlu -- jak implementovat rozhraní TreeModelListener:
class MyTreeModelListener implements TreeModelListener {
public void treeNodesChanged(TreeModelEvent e) {
/*
* If the event lists children, then the changed
* node is the child of the node we have already
* gotten. Otherwise, the changed node and the
* specified node are the same.
*/
try {
int index = e.getChildIndices()[0];
node = (DefaultMutableTreeNode)
(node.getChildAt(index));
} catch (NullPointerException exc) {}
System.out.println("The user has finished editing the node.");
System.out.println("New value: " + node.getUserObject());
// typicky zde voláme metodu datové vrstvy -- uložení do DB, souboru a pod.
}
public void treeNodesInserted(TreeModelEvent e) {
}
public void treeNodesRemoved(TreeModelEvent e) {
}
public void treeStructureChanged(TreeModelEvent e) {
}
}
actionPerformed
obsluha události tlačítka
Add:
treePanel.addObject("New Node " + newNodeSuffix++);
dvojice metod addObject:
public DefaultMutableTreeNode addObject(Object child) {
DefaultMutableTreeNode parentNode = null;
TreePath parentPath = tree.getSelectionPath();
if (parentPath == null) {
//There is no selection. Default to the root node.
parentNode = rootNode;
} else {
parentNode = (DefaultMutableTreeNode) (parentPath.getLastPathComponent());
}
return addObject(parentNode, child, true);
}
public DefaultMutableTreeNode addObject(DefaultMutableTreeNode parent,
Object child,
boolean shouldBeVisible) {
DefaultMutableTreeNode childNode =
new DefaultMutableTreeNode(child);
...
treeModel.insertNodeInto(childNode, parent,
parent.getChildCount());
//Make sure the user can see the lovely new node.
if (shouldBeVisible) {
tree.scrollPathToVisible(new TreePath(childNode.getPath()));
}
return childNode;
}
Může se stát, že defaultní implementace modelu nestačí
protože data nemají stromovou strukturu nebo
mají stromovou strukturu, ale jinou, než chceme zobrazovat v
JTree
dobrá zpráva je, že TreeModel
akceptuje libovolný
Object
(nikoliv povinně podtřídu
DefaultMutableTreeNode
nebo tak něco) tj. není nutné
implementovat "další strom" jen proto že JTree vyžaduje TreeModel -- stačí vzít existující
data (z datové vrstvy) a "obalit je" rozhraními (tj. zavázat se k
implementaci rozhraní)
Dobrý příklad k prostudování je http://java.sun.com/docs/books/tutorial/uiswing/components/tree.html#data
JTree
má explicitní podporu pro lazy loading
lazy loading je technika umožnující úsporu času a paměti za běhu programu
nenačítají se data, pokud nejsou opravdu potřeba
teprve v okamžiku kdy jsou data potřeba (například collapsed uzel je otevřen), data se načtou do paměti a zobrazí se příslušné komponenty
v případě JTree
se využívá rozhraní
TreeWillExpandListener