Volviendo de nuevo con un nuevo post sobre Swing, esta vez os dejo un 
componente un poco más complicado de entender, el JTree. Su uso con el 
Swing Designer es bien simple de ponerlo en un frame y dibujar su 
interfaz, pero cuando entremos en su estructura que tenemos para 
manejarlo necesitaremos tener conocimientos de lo que es un Tipo 
Abstracto de Datos (TAD para los amigos), y en concreto lo que es un 
árbol. Lo que se puede hacer y cómo se estructura internamente lo 
supongo por entendido. Si no es el caso mejor empezar por ver ésto, sino
 lo de a continuación puede convertirse en un jeroglífico.
http://es.wikipedia.org/wiki/Tipo_de_dato_abstracto
Búsqueda en Google sobre TADs árboles
Construyendo el escenario
Para éste tutorial he utilizado Eclipse Juno y la versión 7 update 9 del
 JDK. Lo primero es crear, dentro de Eclipse, en la ventana del 
explorador de proyectos, un nuevo proyecto de Java. Luego crear un nuevo
 JFrame con el asistente de Eclipse y ya tenemos el esqueleto del 
programa listo para empezar. Yendo al diseñador de formularios del Swing
 Designer le añades un Absolute layout al formulario para poder 
posicionar los componentes donde quieras del formulario, y entonces para
 éste ejemplo le he añadido dos botón y un componente JTree. El 
componente JTree está a su vez dentro de un JScrollPane para que se 
muestren las barras de scroll si el contenido es más grande que lo que 
se puede ver. 
Antes de continuar te debe de haber quedado una ventana parecida a la de la imagen de inicio.
En la versión que tengo, cuando añado el JTree al Frame, se crea con un 
contenido sobre colores, deportes y comida. Éstos datos están en la 
propiedad model. De igual manera que algunos otros componentes, tenemos 
un modelo de árbol para usar el JTree. Por ejemplo, cuando usamos las 
listas tenemos modelos de listas, pues ahora son modelos de árbol. Si no
 establecemos el modelo, por defecto se construye con los elementos 
dichos. Vamos ahora con el TreeModel.
Creando el árbol
Vamos a usar dos tipos de datos para crear la estructura y ponerla en el JTree: el DefaultTreeModel y el  DefaultMutableTreeNode.
El botón para cargar el árbol lo he puesto para leer el árbol de 
directorios de tu disco duro. El tipo de datos TreeModel es el objeto 
que tiene internamente el JTree para manejar su estructura en árbol, 
nosotros vamos a usar una clase derivada de ésta porque se trata de una 
interfaz que no podemos usar directamente. Entonces tenemos el DefaultTreeModel que va a ser el tipo que usaremos.
Por otro lado tenemos que en cada elemento de un DefaultTreeModel es un DefaultMutableTreeNode.
 De forma que cada nodo de éstos puede tener a su vez hijos, formando 
así un árbol según tenga hijos o no. Para entenderlo ésto he pensado 
usar el árbol de directorio que todos conocemos.
Antes de seguir, explicando las dos funciones del JTree, si queremos crear una estructura de tipo árbol como por ejemplo:
nodoroot
- nodo1 (indice 0)
-- nodo1.1 (indice 0)
-- nodo1.2 (indice 1)
- nodo2 (indice 1)
-- nodo2.1 (indice 0)
-- nodo2.2 (indice 1)
...lo que tenemos que hacer es que cada nodo es un elemento de tipo 
DefaultMutableTreeNode, y cada elemento se puede hacer hijo de otro. Es 
decir, lo único que hay que decirle al programa es de cada nodo cuál es 
su padre con la función insertNodeInto(nodo, padre, índice). Por ejemplo
 para la estructura anterior programaríamos:
| 
1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
12 
13 
14 
15 | DefaultMutableTreeNode nodoroot, nodo1, nodo11, nodo12, nodo2, nodo21, nodo22;nodoroot = newDefaultMutableTreeNode("Éste es el nodo principal.");nodo1 = newDefaultMutableTreeNode("nodo1");nodo11 = newDefaultMutableTreeNode("nodo11");nodo12 = newDefaultMutableTreeNode("nodo12");nodo2 = newDefaultMutableTreeNode("nodo2");nodo21 = newDefaultMutableTreeNode("nodo21");nodo22 = newDefaultMutableTreeNode("nodo22");arbol.setRoot(nodoroot);arbol.insertNodeInto(nodo1, nodoroot, 0);arbol.insertNodeInto(nodo2, nodoroot, 1);arbol.insertNodeInto(nodo11, nodo1, 0);arbol.insertNodeInto(nodo12, nodo1, 1);arbol.insertNodeInto(nodo21, nodo2, 0);arbol.insertNodeInto(nodo22, nodo2, 1);  | 
En el ejemplo os he dejado algo más complicado, con una función 
recursiva que carga toda la estructura de directorios desde el 
directorio raiz "/". Es decir, cuando le damos al botón tenemos el 
código siguiente:
| 
1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 | btnCargarrbolDe.addActionListener(newActionListener() {    publicvoidactionPerformed(ActionEvent arg0) {        DefaultTreeModel arbol = (DefaultTreeModel) tree.getModel();        DefaultMutableTreeNode nroot = newDefaultMutableTreeNode("Árbol de directorios");        arbol.setRoot(nroot);        CargaEstructuraDirectorios(arbol, nroot, "/");    }}); | 
... una vez dicho cual es el nodo principal (nroot), la función CargaEstructuraDirectorios lo
 hace todo. Puede tardar bastante, depende lo que tengas en tu ordenador
 o lo rápido que sea, déjalo ejecutarse hasta el final si lo quieres ver
 el resultado.
No voy a entrar en detalle sobre funciones recursivas o lectura de 
directorios. Lo que hay que saber es que lista un directorio, añadiendo 
todo lo que encuentra al árbol en su lugar adecuado, de maner que si 
encuentra un directorio lo añade también. Y acto seguido entra al 
directorio y lo lista, de manera que si algo de dentro también es un 
directorio vuelve a hacer lo mismo, es decir, vuelve a entrar en éste 
segundo directorio y lo lista también añadiendo de nuevo los elementos 
en el lugar adecuado. Así sucesivamente hasta listar todos los 
directorios y subdirectorios.
Todo ésto queda simple con una función recursiva que os dejo aquí:
| 
1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
21 
22 
23 
24 | privatevoidCargaEstructuraDirectorios(DefaultTreeModel arbol, DefaultMutableTreeNode padre, String ruta) {    DefaultMutableTreeNode aux = null;    File archivo = newFile(ruta);    File[] archivos = archivo.listFiles();    if(archivos != null) {        for(inti = 0; i < archivos.length; i++) {            aux = newDefaultMutableTreeNode(archivos[i].getName());            arbol.insertNodeInto(aux, padre, i);            if(archivos[i].isDirectory()) {                try{                    CargaEstructuraDirectorios(arbol, aux, archivos[i].getAbsolutePath() + "/");                } catch(Exception e) {                    System.out.println(e.getMessage());                }            }        }    }} | 
Borrar un nodo
Igual que se añaden nodos, también se pueden borrar. Usando la función removeNodeFromParent que
 se usa en el ejemplo. Con un botón simple. No sólo podemos borrar un 
nodo, también podemos borrarlo de un sitio y ponerlo en otro, 
reordenando nuestro árbol, modificándolo, o lo que necesitemos. Pero con
 añadir o borrar tenemos lo básico para empezar...
Click, empieza el juego
Ahora se complica, pero empieza el juego xD ¿cómo hacer algo cuando 
hacemos click en un elemento del árbol? Haciendo click derecho con el 
ratón en el JTree añadimos el capturador de eventos que vamos a usar 
para hacer álgo cuando el usuario hace click en el JTree:
Eclipse, de nuevo nos genera el código esqueleto siguiente:
| 
1 
2 
3 
4 
5 
6 
7 | finalJTree tree = newJTree();tree.addTreeSelectionListener(newTreeSelectionListener() {    publicvoidvalueChanged(TreeSelectionEvent e) {        DefaultMutableTreeNode nseleccionado = (DefaultMutableTreeNode) tree.getLastSelectedPathComponent();        JOptionPane.showMessageDialog(frame, nseleccionado.getPath());    }}); | 
Se pueden capturar otros eventos. Y bueno, ya la imaginación o lo que 
necesitemos entra en juego para desarrollar lo que necesitemos. Con las 
funciones principales que nos proporcina el DefaultMutableTreeNode pordemos
 hacer lo que queramos. Podemos tener varios árboles y ponerlos en el 
JTree cuando queramos uno u otro, podemos recorrer los nodos por los 
índices, podemos saber cuál es el nodo principal con la función .getRoot, saber cuántos hijos tiene un nodo con .getChildCount, etcétera...
No hay más que curiosear para qué sirven las funciones proporcionadas, como podemos ver en la imagen siguiente del Eclipse:
Hay que saber que cuando hacemos cambios en la estructura de árbol, 
éstos automáticamente se visualizan en el JTree, con lo que sólo tenemos
 que centrarnos en el árbol. 
Para más información me remito de nuevo a la documentación oficial:
Códigos del ejemplo
En el ejemplo he comentado algunas cosas más. Aquí están en descarga directa los códigos fuentes.
 Hay un .jar, para ejecutarlo necesitas el JRE instalado, si quieres 
modificarlo, sólo necesitas un editor de texto ya que sólo hay un 
fichero .java y recompilarlo.
Todo el código comentado es el siguiente para el que no quiera descargarlo:
| 
1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
21 
22 
23 
24 
25 
26 
27 
28 
29 
30 
31 
32 
33 
34 
35 
36 
37 
38 
39 
40 
41 
42 
43 
44 
45 
46 
47 
48 
49 
50 
51 
52 
53 
54 
55 
56 
57 
58 
59 
60 
61 
62 
63 
64 
65 
66 
67 
68 
69 
70 
71 
72 
73 
74 
75 
76 
77 
78 
79 
80 
81 
82 
83 
84 
85 
86 
87 
88 
89 
90 
91 
92 
93 
94 
95 
96 
97 
98 
99 
100 
101 
102 
103 
104 
105 
106 
107 
108 
109 
110 
111 
112 
113 
114 
115 
116 
117 
118 
119 
120 
121 
122 
123 
124 
125 
126 
127 
128 
129 
130 
131 
132 
133 
134 
135 
136 
137 
138 
139 
140 
141 
142 
143 
144 
145 
146 
147 
148 
149 
150 
151 
152 
153 
154 
155 
156 
157 
158 
159 
160 
161 
162 
163 
164 
165 
166 
167 
168 
169 | importjava.awt.BorderLayout;importjava.awt.Component;importjava.awt.EventQueue;importjavax.swing.JFrame;importjavax.swing.JOptionPane;importjavax.swing.JPanel;importjavax.swing.border.EmptyBorder;importjavax.swing.event.TreeModelListener;importjavax.swing.JButton;importjavax.swing.JTree;importjavax.swing.tree.DefaultTreeModel;importjavax.swing.tree.DefaultMutableTreeNode;importjavax.swing.tree.MutableTreeNode;importjavax.swing.tree.TreeModel;importjavax.swing.tree.TreePath;importjava.awt.event.ActionListener;importjava.awt.event.ActionEvent;importjava.io.File;importjavax.swing.JScrollPane;importjavax.swing.event.TreeSelectionListener;importjavax.swing.event.TreeSelectionEvent;// La clase principalpublicclassPrincipal extendsJFrame {    // el panel contenedor    privateJPanel contentPane;    // el JFrame    staticPrincipal frame;    /**     * esta es la función que primero se ejecuta creando el JFRame y visualizándolo     */    publicstaticvoidmain(String[] args) {        EventQueue.invokeLater(newRunnable() {            publicvoidrun() {                try{                    frame = newPrincipal();                    frame.setVisible(true);                } catch(Exception e) {                    e.printStackTrace();                }            }        });    }    /**     * la creación del JFrame principal donde está programado todo lo de éste ejemplo     */    publicPrincipal() {        // título de ventana        setTitle("Java Swing 8 El JTree by Jnj");        // operación al cerra el JFrame        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);        // dimensiones y posición en el escritorio        setBounds(100, 100, 450, 306);        // se crea el panel        contentPane = newJPanel();        // los bordes        contentPane.setBorder(newEmptyBorder(5, 5, 5, 5));        // se establece        setContentPane(contentPane);        contentPane.setLayout(null);        // se pone el botón en la ventana        JButton btnCargarrbolDe = newJButton(                "Cargar \u00E1rbol de directorios");        btnCargarrbolDe.setBounds(10, 11, 200, 23);        contentPane.add(btnCargarrbolDe);        // las barras de escroll para el JTree        JScrollPane scrollPane = newJScrollPane();        scrollPane.setBounds(10, 45, 414, 206);        contentPane.add(scrollPane);        // el JTree        finalJTree tree = newJTree();        // que captura el evento click        tree.addTreeSelectionListener(newTreeSelectionListener() {            publicvoidvalueChanged(TreeSelectionEvent e) {                // se obtiene el nodo seleccionado                DefaultMutableTreeNode nseleccionado = (DefaultMutableTreeNode) tree.getLastSelectedPathComponent();                // visualiza el path del nodo                JOptionPane.showMessageDialog(frame, nseleccionado.getPath());            }        });        // se pone el árbol en el panel de las barras de scroll        scrollPane.setViewportView(tree);        // aquí el botón que borra el último elemento de los primeros hijos        // es decir, desde el nodo root, borra sólo el último hijo        JButton btnBorrarltimoNodo = newJButton("Borrar \u00FAltimo nodo");        btnBorrarltimoNodo.addActionListener(newActionListener() {            publicvoidactionPerformed(ActionEvent arg0) {                DefaultTreeModel arbol = (DefaultTreeModel) tree.getModel();                DefaultMutableTreeNode padre = (DefaultMutableTreeNode) arbol.getRoot();                intnumeroDeHijos = arbol.getChildCount(padre);                // borra el último hijo del padre                arbol.removeNodeFromParent((MutableTreeNode) arbol.getChild(                        padre, numeroDeHijos - 1));            }        });        btnBorrarltimoNodo.setBounds(220, 11, 204, 23);        contentPane.add(btnBorrarltimoNodo);        // evento click del botón de carga del árbol        // simplemente añade el nodo root y llama a la función de carga        // para añadir todos los nodos hijos al nodo root        btnCargarrbolDe.addActionListener(newActionListener() {            publicvoidactionPerformed(ActionEvent arg0) {                DefaultTreeModel arbol = (DefaultTreeModel) tree.getModel();                DefaultMutableTreeNode nroot = newDefaultMutableTreeNode(                        "Árbol de directorios");                arbol.setRoot(nroot);                CargaEstructuraDirectorios(arbol, nroot, "/");            }        });                }    // función recursiva que lista todos los directorios y subdirectorios    // a partir de una ruta, añadiéndolos a la estructura en árbol    privatevoidCargaEstructuraDirectorios(DefaultTreeModel arbol,            DefaultMutableTreeNode padre, String ruta) {        DefaultMutableTreeNode aux = null;        File archivo = newFile(ruta); // puntero al directorio de la ruta        File[] archivos = archivo.listFiles(); // lista todos los archivos de la ruta        // recorre lo que hay en la ruta        if(archivos != null) {            for(inti = 0; i < archivos.length; i++) {                // creando un nodo con cada cosa del directorio                aux = newDefaultMutableTreeNode(archivos[i].getName());                // inserta el nodo hijo                 arbol.insertNodeInto(aux, padre, i);                // si encontramos un directorio volvemos a hacer lo mismo con sus hijos                if(archivos[i].isDirectory()) {                    try{                                                // llamando recursivamente de nuevo a ésta misma función                        CargaEstructuraDirectorios(arbol, aux,                                archivos[i].getAbsolutePath() + "/");                                            } catch(Exception e) {                        System.out.println(e.getMessage()); // por si acaso le he puesto un try xD                    }                }            }        }    }        // termina la creación del frame}// fin de la clase | 


