24 de noviembre de 2008

Personalizar Iconos en un JTree


A mas de uno se le habra ocurrido crear un JTree con la necesidad de cambiar los iconos por defecto con los que viene el arbol. Lastimosamente el IDE Netbeans (con el que trabajo frecuentemente) no nos permite hacer mucho (tal igual como lo que pasa con los JTable). En pocas palabras, necesitamos recordar nuestros conceptos de Herencia e Interfaces.


En mi caso particular, me planteo desarrollar un programa de mensajería instantanea (IM), el modelo que presentan los JTree y los arboles en general es muy adecuado para agrupar contactos por estado, por grupo, etc. y decidi usarlo en el proyecto que me encontraba desarrollando. Un aproximado podria ser el siguiente:

Figura 1: Modelo de JTree a crear


Donde, los iconos de contacto1 y contacto2 deben ser de un color distinto al de contacto3 y contacto4, el grupo conectados y no conectados deben tener un mismo icono y bueno, el icono de la raiz no tiene importancia.


CONCEPTOS PREVIOS:


Cuando un JTree necesita pintar los datos que contiene, esta llama a otra para obtener detalles de este dibujado. Esta clase a la que llama se denomina DefaultTreeCellRenderer y proporciona informacion de cada hoja, del icono de cada nodo dentro del arbol.


La clase DefaultTreeCellRenderer implementa a su vez la interfaz TreeCellRenderer , la cual, haciendo uso del metodo getTreeCellRendererComponent devuelve toda la informacion necesaria para dibujar un arbol y sus nodos.


El JTree, en su método getCellRenderer() nos devuelve la clase que está usando para saber cómo dibujar los datos. Con su método setCellRenderer() podríamos pasarle una clase hecha a nuestra medida. Haciendo uso de estos métodos, nosotros podemos redefinir los iconos en el arbol (aunque de una forma muy limitada), haciendo uso de los métodos:



  • setLeafIcon() para cambiar el icono de los nodos hoja (nodos sin hijos).

  • setOpenIcon() para cambiar el icono de los nodos con hijos que están desplegados (sus hijos son visibles).

  • setClosedIcon() para cambiar el icono de los nodos con hijos que están cerrados (los hijos no son visibles).


Sin embargo, esta solucion no permite distinguir entre los hijos de tal o cual padre dentro del arbol (para el caso de los hijos del nodo "Conectados" y los hijos del nodo "No Conectados").


Con todo lo que hemos descrito solo nos quedan dos cosas por hacer: Crear una clase que implemente la interfaz TreeCellRenderer (lo cual implica la construccion completa del metodo getTreeCellRendererComponent, lo cual no es muy adecuado) o crear una clase que herede de la superClase DefaultTreeCellRenderer y sobre la cual hariamos solo algunas modificaciones finales para lograr lo que buscamos.


Asumimos que ya tenemos en el proyecto un objeto instancia de JTree, la cual se denomina "arbol". Lo que haremos es modificar los datos que contiene por defecto para colocar los que en realidad necesitamos para nuestro proposito. Esto lo hacemos con el método remodelarArbol()






public void remodelarArbol(){

//DefaultTreeCellRenderer dt = (DefaultTreeCellRenderer)arbol.getCellRenderer();

DefaultTreeModel def = (DefaultTreeModel)arbol.getModel();

DefaultMutableTreeNode contactos = new DefaultMutableTreeNode("Contactos");

DefaultMutableTreeNode conectados = new DefaultMutableTreeNode("Conectados");

DefaultMutableTreeNode noconectados = new DefaultMutableTreeNode("No Conectados");

DefaultMutableTreeNode c1 = new DefaultMutableTreeNode("Contacto 1");

DefaultMutableTreeNode c2 = new DefaultMutableTreeNode("Contacto 2");

DefaultMutableTreeNode nc1 = new DefaultMutableTreeNode("Contacto 3");

DefaultMutableTreeNode nc2 = new DefaultMutableTreeNode("Contacto 4");

conectados.add(c1);

conectados.add(c2);

noconectados.add(nc1);

noconectados.add(nc2);

contactos.add(conectados);

contactos.add(noconectados);

def.setRoot(contactos);

arbol.setModel(def);

arbol.setCellRenderer(new RendererArbol());//añadimos nuevo renderer

}

Si nos fijamos en la ultima linea, añadimos a nuestro arbol remodelado una nueva clase que se encargará de su dibujado. Esta clase la tenemos que crear y ajustar a nuestras necesidades.



import javax.swing.tree.*;

import javax.swing.*;

import java.awt.*;



/**

*

* @author Jhon Anthony Alvarez Borja

*/

public class RendererArbol extends DefaultTreeCellRenderer{



/** Creates a new instance of RendererArbol */

ImageIcon conectados;

ImageIcon noConectados;

public RendererArbol() {

conectados = new ImageIcon("src/Resources/Pack MSN/Enligne.png");

noConectados = new ImageIcon("src/Resources/Pack MSN/Horsligne.png");

}



public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus)

{

super.getTreeCellRendererComponent(tree,value,selected,expanded,leaf,row,hasFocus);

String val = value.toString();

ImageIcon i;

DefaultMutableTreeNode nodo = (DefaultMutableTreeNode)value;

if(val.compareTo("Conectados")!=0 && val.compareTo("No Conectados")!=0 && val.compareTo("Contactos")!=0){

TreeNode t = nodo.getParent();

if(t!=null){

if(t.toString().compareTo("Conectados")==0){

setIcon(conectados);

}else if(t.toString().compareTo("No Conectados")==0){

setIcon(noConectados);

}

}

}

return this;

}

}






Lo que hacemos en esta clase es implementar el metodo getTreeCellRendererComponent modificando solo lo que nos interesa
lo cual es en este caso los íconos para los usuarios conectados y no conectados. Al hacer uso de la palabra reservada y el metodo super.getTreeCellRendererComponent lo que hacemos es llamar al método original de la clase y lineas abajo, colocamos las modificaciones que queremos hacer al momento de mostrar un determinado nodo en funcion de nuestras necesidades. En este caso basta con identificar el nombre de cada nodo padre, y de acuerdo a ello colocamos el icono correspondiente a conectado o no conectado.

Espero les sea de ayuda este pequeño tutorial. Aca les dejo el codigo fuente




Fuentes:

3 comentarios:

Cesar Castejon dijo...

Hola que tal Jhon Alvarez.
Mi nombre es Cesar soy de Honduras y estudio Ing. en Computacion en una Universidad de Honduras.

Bueno te quiero decir que se mira muy bueno tu proyecto, y ademas te escribo para pedirte ayuda con el mio.

Ya que se trata de JTree y es que lo que tengo que hacer es un eplorador de archivos con un jtree y jlist. ya hee logrado hacer algo.
Pero tengo que filtrar tipos de archivos. abrir txt, .doc y otros de office.

Si me pudieras dar una ayudadita te lo agradeceria mucho porfavor, ya que miro que tienes muy buen conocimiento sobre java y mas que hiciste esto con un jtree

Cesar Castejon dijo...

Haaa se me olvido mi corrreo
cesar.castejon@gmail.com
y
yeya8810@hotmail.com

GRACIAS DE ANTEMANO!!

Espero tu respuesta

Guillermo dijo...

Hola Jhon, interesante el post :) pero te recomiendo usar la etiqueta PRE de html para que subas tus códigos en blogger =)! saludos.

Guillermo