====== Δένδρα (Trees) ====== Μπορείτε να δημιουργήσετε μία δενδρική δομή απεικόνισης των δεδομένων σας με χρήση της κλάσης [[http://docs.oracle.com/javase/7/docs/api/javax/swing/JTree.html|JTree]]. Ένα παράδειγμα δενδρικής δομής δίνεται στο παρακάτω σχήμα. {{ :swing:jtree-01.png |}} Όπως φαίνεται στο παραπάνω σχήμα ένα Component τύπου //JTree// απεικονίζει τα δεδομένα του κατακόρυφα ξεκινώντας από ένα κόμβο που ονομάζεται ρίζα του δένδρου και κάθε γραμμή του δένδρου περιέχει μόνο ένα κόμβο. Ένας κόμβος μπορεί να έχει ένα ή περισσότερα παιδιά (//branch node//) ή όχι (//leaf node//). Ένας κόμβος μπορεί να προσδιοριστεί μέσα από το [[http://docs.oracle.com/javase/7/docs/api/javax/swing/tree/TreePath.html|TreePath]] το οποίο προσδιορίζει τη σειρά τον κόμβων από την ρίζα και έως τον συγκεκριμένο κόμβο. Μπορείτε να δείτε το [[swing:jtree_demo_01|παράδειγμα δημιουργίας και διαχείρισης ενός JTree]] από το site της Oracle. Παρακάτω θα αναλύσουμε τα διάφορα στάδια του προγράμματος. ===== Δημιουργία του δένδρου ===== Κάθε δένδρο είναι ένα αντικείμενο της κλάσης [[http://docs.oracle.com/javase/7/docs/api/javax/swing/JTree.html|JTree]]. Επίσης ένα δένδρο περιέχει τον κόμβο που αντιστοιχεί στην ρίζα του δένδρου, αλλά και όλους τους άλλους κόμβους που συναποτελούν το δένδρο και διαμορφώνουν την τελική δενδρική δομή. Κάθε κόμβος του δένδρου θα πρέπει να υλοποιεί το interface [[http://docs.oracle.com/javase/7/docs/api/javax/swing/tree/MutableTreeNode.html|MutableTreeNode]]. Οι μέθοδοι του συγκεκριμένου interface δίνονται παρακάτω: void insert(MutableTreeNode child, int index) //Adds child to the receiver at index. void remove(int index) //Removes the child at index from the receiver. void remove(MutableTreeNode node) //Removes node from the receiver. void removeFromParent() //Removes the receiver from its parent. void setParent(MutableTreeNode newParent) //Sets the parent of the receiver to newParent. void setUserObject(Object object) Resets the user object of the receiver to object. Από τις παραπάνω μεθόδους είναι προφανές ότι σε ένα κόμβο μπορείτε να προσθέσετε άλλους κόμβους ή να αφαιρέσετε κόμβους ή να μετακινήσετε κόμβους. Ένας απλός τρόπος ώστε οι κόμβοι σας να υλοποιούν το συγκεκριμένο interface είναι να ανήκουν στην κλάση [[http://docs.oracle.com/javase/7/docs/api/javax/swing/tree/DefaultMutableTreeNode.html|DefaultMutableTreeNode]] που υλοποιεί το συγκεκριμένο interface. Η κλάση αυτή περιέχει πολλές βοηθητικές μεθόδους οι οποίες σας βοηθούν να πλοηγηθείτε στο δένδρο όταν το χρειαστείτε. Παρακάτω δίνεται ο κώδικας που δημιουργεί το δένδρο. DefaultMutableTreeNode top = new DefaultMutableTreeNode("The Java Series"); createNodes(top); //Create a tree that allows one selection at a time. tree = new JTree(top); tree.setEditable(true); tree.getSelectionModel().setSelectionMode (TreeSelectionModel.SINGLE_TREE_SELECTION); Ο παραπάνω κώδικας δημιουργεί το στοιχείο της ρίζας με τον όνομα //top// και με βάση αυτό δημιουργεί και τα υπόλοιπα αντικείμενα μέσω της μεθόδου ''void createNodes(DefaultMutableTreeNode top)''. Στην συνέχεια, δημιουργεί το δένδρο στο οποίο ορίζει ως ρίζα το στοχείο //top//. Επίσης, ορίζει ότι το δένδρο μπορεί να μεταβληθεί από τον χρήστη και ορίζει ότι μόνο ένας κόμβος του δένδρου μπορεί να επιλεγεί από τον χρήστη κάθε φορά. tree.setEditable(true); tree.getSelectionModel().setSelectionMode (TreeSelectionModel.SINGLE_TREE_SELECTION); Οι επιλογές που έχετε αναφορικά με τον αριθμό των κόμβων που μπορείτε να επιλέξετε είναι οι παρακάτω: static int CONTIGUOUS_TREE_SELECTION //Πολλαπλοί συνεχόμενοι κόμβοι static int DISCONTIGUOUS_TREE_SELECTION //Πολλαπλοί κόμβοι, όχι απαραίτητα συνεχόμενοι static int SINGLE_TREE_SELECTION //Μόνο ένας κόμβος μπορεί να επιλεγεί κάθε φορά. Παρακάτω δίνεται ένα τμήμα της μεθόδου //createNodes//. 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); //JFC Swing Tutorial book = new DefaultMutableTreeNode(new BookInfo ("The JFC Swing Tutorial: A Guide to Constructing GUIs", "swingtutorial.html")); category.add(book); .... } Από τον παρακάτω κατασκευαστή της κλάσης [[http://docs.oracle.com/javase/7/docs/api/javax/swing/tree/DefaultMutableTreeNode.html|DefaultMutableTreeNode]] παρατηρούμε ότι κατά την δημιουργία αντικειμένων της κλάσης αυτής μπορούμε να αποθηκεύσουμε οποιοδήποτε αντικείμενο μέσα σε αυτή. public DefaultMutableTreeNode(Object userObject); Έτσι στη μέθοδο createNodes αποθηκεύονται Strings, αν πρόκειται για κόμβους που αναφέρονται στις κατηγορίες των βιβλίων ή αντικείμενα της κλάσης //BookInfo// αν πρόκειται για βιβλία. Αντίστοιχα, αν θέλετε να λάβετε το περιεχόμενο ενός κόμβου μπορείτε να το προσπελάσετε με την μέθοδο public Object getUserObject() Σε αυτή την περίπτωση θα χρειαστείτε να γνωρίζετε την κλάση του αντικειμένου το οποίο επιστρέφει η παραπάνω μέθοδος και να εφαρμόσετε κατάλληλο typecasting. ==== Ανταποκρινόμενοι στα events ενός δένδρου ==== Ένα δένδρο μπορεί να παράγει δύο τύπους //events//: * **[[http://docs.oracle.com/javase/7/docs/api/javax/swing/event/TreeSelectionEvent.html|TreeSelectionEvent]]:** events που συνδέονται με επιλογή κόμβων του δένδρου. Τα events αυτά λαμβάνονται μέσα από ένα αντικείμενο που υλοποιεί το interface [[http://docs.oracle.com/javase/7/docs/api/javax/swing/event/TreeSelectionListener.html|TreeSelectionListener]]. * **[[http://docs.oracle.com/javase/7/docs/api/javax/swing/event/TreeModelEvent.html|TreeModelEvent]]:** events που συνδέονται με μεταβολή της δομής του δένδρου. Τα events αυτά λαμβάνονται μέσα από ένα αντικείμενο που υλοποιεί το interface [[http://docs.oracle.com/javase/7/docs/api/javax/swing/event/TreeModelListener.html|TreeModelListener]]. === TreeSelectionEvents === Σε όλα τα events που συνδέονται με ένα αντικείμενο μπορείτε να πάρετε το αντικείμενο το οποίο συνδέεται με το event μέσα από την μέθοδο ''public Object getSource()'' της κλάσης [[http://docs.oracle.com/javase/7/docs/api/java/util/EventObject.html|EventObject]]. Εκτός της παραπάνω μεθόδου, οι μέθοδοι που υποστηρίζει κάθε event αυτού του τύπου είναι τα εξής: Object cloneWithSource(Object newSource) //Returns a copy of the receiver, but with the source being newSource. TreePath getNewLeadSelectionPath() //Returns the current lead path. TreePath getOldLeadSelectionPath() //Returns the path that was previously the lead path. TreePath getPath() //Returns the first path element. TreePath[] getPaths() //Returns the paths that have been added or removed from the selection. boolean isAddedPath() //Returns whether the path identified by getPath was added to the selection. boolean isAddedPath(int index) //Returns whether the path at getPaths()[index] was added to the selection. boolean isAddedPath(TreePath path) //Returns whether the specified path was added to the selection. Στο δένδρο του τρέχοντος παραδείγματος μας που υποστηρίζει το μοντέλο επιλογής κόμβων ''SINGLE_TREE_SELECTION'' η μόνη μέθοδος που σας χρειάζεται είναι η getPath() από την οποία λαμβάνεται το [[http://docs.oracle.com/javase/7/docs/api/javax/swing/tree/TreePath.html|TreePath]] του κόμβου που επιλέξατε. Αντίστοιχα, το interface [[http://docs.oracle.com/javase/7/docs/api/javax/swing/event/TreeSelectionListener.html|TreeSelectionListener]] υποστηρίζει μόνο μία μέθοδο η οποία καλείται κάθε φορά που επιλέγεται νέος κόμβος, όπως φαίνεται παρακάτω. void valueChanged(TreeSelectionEvent e) Called whenever the value of the selection changes. === TreeModelEvents === Τα events [[http://docs.oracle.com/javase/7/docs/api/javax/swing/event/TreeModelEvent.html|αυτού του τύπου]] δημιουργούνται όταν έχουμε μεταβολή στην δομή του δένδρου. Σε αυτή την περίπτωση θα χρειαστείτε μία κλάση που υλοποιεί το interface [[http://docs.oracle.com/javase/7/docs/api/javax/swing/event/TreeModelListener.html|TreeModelListener]] για να λάβετε τις μεταβολές στη δομή του δένδρου. Ο συγκεκριμένος Listener ορίζει τις παρακάτω μεθόδους. void treeNodesChanged(TreeModelEvent e) //Invoked after a node (or a set of siblings) has changed in some way. void treeNodesInserted(TreeModelEvent e) //Invoked after nodes have been inserted into the tree. void treeNodesRemoved(TreeModelEvent e) //Invoked after nodes have been removed from the tree. void treeStructureChanged(TreeModelEvent e) //Invoked after the tree has drastically changed structure from a given node down. ==== Αλλάζοντας τα εικονίδια και την εμφάνιση στους κόμβους ενός δένδρου ==== Ένα από τα βασικά πράγματα που πιθανόν θα χρειαστείτε είναι να μεταβάλλεται την εμφάνιση των κόμβων ενός δένδρου βάζοντας διαφορετικά εικονίδια για τους κόμβους που είναι φύλλα, για τους κόμβους που δεν είναι φύλλα, αλλά είναι επιλεγμένοι και ανοιχτοί ή για τους κόμβους που είναι κλειστοί. Για να το κάνετε αυτό θα χρειαστείτε ένα αντικείμενο που υλοποιεί το interface [[http://docs.oracle.com/javase/7/docs/api/javax/swing/tree/TreeCellRenderer.html|TreeCellRenderer]]. Η πιο απλή επιλογή είναι να χρησιμοποιήσετε την κλάση [[http://docs.oracle.com/javase/7/docs/api/javax/swing/tree/DefaultTreeCellRenderer.html|DefaultTreeCellRenderer]]. Το παρακάτω απόσπασμα είναι ενδεικτικό για το πως μπορείτε να αλλάξετε τα εικονίδια των κόμβων σε κάθε μία από τις παραπάνω κατηγορίες. DefaultTreeCellRenderer tRenderer = new DefaultTreeCellRenderer(); ImageIcon folderIcon = new ImageIcon( System.getProperty("user.dir")+"/icons/small/folder.png" ); tRenderer.setLeafIcon( folderIcon ); tRenderer.setClosedIcon( folderIcon ); tRenderer.setOpenIcon( folderIcon ); tRenderer.setTextSelectionColor(Color.RED); tree.setCellRenderer(tRenderer);