Kapitola 1. Stromy

Obsah

1.1. Základní informace
1.2. Základní princip
1.2.1. Primitivní použití
1.2.2. Události
1.2.3. Změna vzhledu
1.3. Změny ve stromu
1.4. Datový model
1.5. Lazy loading

1.1. Základní informace

  • 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

1.2. Základní princip

  • je to velmi podobné JTable

  • je nutné mít dvou (tří) stupňovou organizaci

    1. 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

    2. 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)

1.2.1. Primitivní použití

  • 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.

1.2.2. Události

  • 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
    }
}

1.2.3. Změna vzhledu

  • 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ší

1.3. Změny ve stromu

  • 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)

    Poznámka

    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;
    }

1.4. Datový model

1.5. Lazy loading

  • 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