martes, 29 de marzo de 2011

JTREE

El JTree nos permite mostrar información jerárquica, es excelente para mostrar datos como un árbol familiar o la estructura del sistema de archivos. Utilizar un JTree es algo sencillo, pero no siempre es muy claro cómo llenarlo para no gastar mucha memoria y para que no se gaste mucho tiempo al llenarlo. En este Trial vamos a hacer una aplicación guíada en la que utilizaremos un JTree para mostrar el sistema de archivos de la partición raíz de la computadora.
El archivo contiene una versión termianda del programa que vamos a crear en este Trial.r una aplicación guíada en la que utilizaremos un JTree para mostrar el sistema de archivos de la partición raíz de la computadora.
El archivo contiene una versión termianda del programa que vamos a crear en este Trial.

¿Cómo utilizar un JTree?

La aplicación que queremos crear es una aplicación similar al Explorador de Windows.
En la aplicación vamos a tener un JTree con los directorios que se encuentran en la
estructura de archivos, y en la otra parte vamos a tener los archivos que se encuentren
dentro del directorio seleccionado.
Además de utilizar el JTree vamos a utilizar extensivamente la clase File que nos
 permite muchas facilidades en el manejo de archivos.
Primero que nada crea una nueva aplicación de Java en que la clase principal se llame Main.
En la clase Main copia el siguiente código:






En este código estamos creando una nueva ventana. La ventana va a contener un 
JTree y un JList y cada uno va a tener su propio JScrollPane para permitir 
que tengan barras de desplazamiento.
En el constructor de la clase podemos ver que estamos creando un nuevo objeto 
de la clase File. La claseFile contiene muchos métodos que nos permiten 
manejar archivos, saber de que tipo son y algunos otros datos importantes sobre ellos.
Nuestro JTree es un conjunto de nodos que tienen un padre y cero o más hijos,
 así como un directorio tiene un directorio padre y puede contener cero o más directorios. 
En este caso, cada directorio va a ser un nuevo nodo de la claseDefaultMutableTreeNode.
 En el código podemos ver que creamos un nuevo objeto de esta clase con un constructor
 que recibe un objeto (en este caso el objeto File que creamos pero podría recibir 
cualquier otra cosa, incluyendo objetos creados por nosotros). Este nodo, 
al que estoy llamando top, va a ser la raíz de nuestro JTree y vamos a agregarle 
elementos más adelante para crear nuestro sistema de archivos.
Una vez que definimos nuestro nodo inicial (y que agregamos los componentes necesarios)
 vamos a crear un nuevo JTree que reciba como parámetro del constructor el nodo que
 acabamos de crear.
Después de definir el JTree le ponemos unJScrollPane para permitir que tenga barras
 de desplazamiento y después creamos todo el resto de la interfaz.
Esta clase utiliza el método pack(). Este método le pregunta a los componentes de la
 interfaz gráfica cuánto espacio necesitan y, dependiendo de esto, construye una ventana 
suficientemente grande para que todos los componentes puedan desplegarse de manera
 satisfactoria.
En el método main() únicamente creamos una instancia nueva de nuestro programa.


Al ejecutar el programa podemos ver que nuestra interfaz todavía es muy sencilla y no tiene 
ninguna funcionalidad. Nuestro siguiente paso es agregarle esta funcionalidad.


El arbol es por naturaleza una estructura recursiva. Lo que esto significa es que la mayoría 
de las veces vamos a tener que plantearnos soluciones recursivas para agregar y eliminar 
nodos. Por ejemplo, si tenemos un directorio (1), debemos encontrar sus hijos (1.1 y 1.2) y 
agregarlos, pero si estso hijos tienen hijos a su vez (1.1.1), también tenemos que agregar 
esos hijos. Es decir: Para un directorio cualquiera, debemos agregar sus subdirectorios y 
repetir el proceso con estos.
Para hacer esto con código debemos crear un método que agregue los hijos al padre y que
 por cada hijo se mande a llamar a si mismo, pero ahora con el hijo. Es decir, debemos crear
 un método recursivo. Agrega el siguiente código a tu aplicación:





Al correr el programa nos damos cuenta que faltan muchos directorios!
 Esto es comportamiento esperado porque al ir reduciendo la variable depth le indicamos
 al programa que no ponga todos los directorios sino únicamente los primeros dos niveles.
 Esto lo hacemos ya que tomaría muchísimo tiempo poner todos los directorios (un conteo 
rápido del número de archivos de mi disco duro indica que tengo más de cien mil archivos, 
imagina todo el tiempo que tomaría agregarlos todos). Este problema vamos a dejarlo volando
 un momento y antes vamos a resolver un punto un poco más importante.
Ya que tenemos un poco más de directorios en nuestro JTree podemos hacer que una vez
 que el usuario haga click sobre el directorio aparezcan los archivos que contiene en la lista 
de la izquierda. Para esto vamos a utilizar un TreeSelectionListener y el método 
valueChanged().
Primero que nada agrega el código:
 implements TreeSelectionListener
En el encabezado de la clase y utiliza la siguiente línea de código para agregar la capacidad
 de manejar eventos a nuestro JTree:
 treeDir.getSelectionModel().addTreeSelectionListener(this);
Después copia el siguiente código:
Al manejar eventos con un
JTree es muy importante saber
 qué nodo está seleccionado y qué objeto está guardado en ese nodo. Para poder obtener el nodo seleccionado vamos a utilizar el métodogetLastSelectedPathComponent() este método que parece trabalenguas nos devuelve unDefaultMutableTreeNode con el último nodo que fue seleccionado.
Para obtener el objeto que está guardado dentro del nodo podemos utilizar el método getUserObject()del nodo que acabamos de obtener. Como este método nos devuelve un objeto nosotros debemos hacer un cast al tipo de dato que está guardado.
En este método podemos ver que obtenemos el nodo y luego el objeto guardado, con este objeto obtenemos una lista de todos los archivos que están guardados en el directorio. Una vez más, el código que hace esto no es objetivo del Trial y no lo vamos a ver. Por último borramos la lista y agregamos todos los archivos, para saber cómo funciona el DefaultListModel y un JList puedes consultar el trial de Listas.
Al correr el programa podemos ver que ya aparecen los archivos que contiene el directorio seleccionado al hacer click sobre él. Por último vamos a resolver el problema que dejamos volando antes. Lo que queremos hacer ahora es que al expandir alguna carpeta se lean los directorios que contiene y que los agregue a esta carpeta para que los muestre de la misma manera que lo hace con el nodo raíz. Para resolver este problema podemos utilizar un TreeExpansionListener.
Agrega el TreeExpansionListener a la declaración de la clase y después agrega la siguiente línea de código para permitir que nuestro JTree responda a eventos de expansión y reducción de carpetas:
treeDir.addTreeExpansionListener(this);
Ahora agrega el siguiente código para manejar el evento de expansión:








Para obtener el nodo que se va a expandir en este método debemos hacer dos pasos, el primero es obtener un objeto TreePath mediante el métodogetPath() de event y el segundo es obtener elDefaultMutableTreeNode utilizando el métodogetLastPathComponent(). Una vez que conseguimos el nodo podemos utilizar el métodogetUserObject() de la misma manera que arriba para poder obtener el archivo.
Para agregar los subdirectorios al directorio seleccionado podemos utilizar el método populateNode() que ya habíamos creado.
Por último obtenemos el DefaultTreeModel que guarda todos los nodos de nuestro JTree (más o menos en la misma manera que elDefaultListModel guarda los objetos del JList) meidante el método getModel() y vamos a utilizar el método nodeStructureChanged() que recibe el nodo que ha cambiado como parámetro para avisarle alJTree que debe volverse a dibujar utilizando los valores nuevos.
En este método también podemos ver el métodosetCursor() que nos permite cambiar el cursor para avisarle al usuario que el método puede tardar.


Nuestro JTree ya nos permite ver archivos y nos permite navegar por todos los directorios de nuestro sistema de archivos, pero tiene muchas deficiencias.
La más notable es que los íconos no son consistentes y visualmente el programa es muy poco amigable. Para poder resolver este problema es necesario cambiar el DefaultCellRenderer que utiliza el JTree para dibujarse en pantalla, esto es un poco complicado por lo que no lo vamos a cubrir en el Trial, pero si quieres ver cómo se hace el código viene en el archivo de inicio.
Otra cosa que podríamos hacer es utilizar el método treeCollapsed para descargar los folders que no se están viendo, pero la memoria que podríamos recuperar es insignificante, por lo que no vale la pena descargar las carpetas de memoria y luego tener que perder en tiempo.

1 comentario: