Estamos ante un servlet que consulta una base de datos para devolver un vector de objetos, este vector es el resultado de la consulta hecha con JDBC. Desde el punto de vista gráfico el applet tiene aspectos que pueden interesar:
Al igual que ocurre con listas y tablas, tenemos el componente y su scroll:
public class BasicoApplet extends JApplet {
JScrollPane pnlSArbol;
JTree arbol = new JTree();
....
El primer paso es asignar el arbol a su scroll:
pnlSArbol = new JScrollPane( arbol, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
En nuestro caso hemos asignado un renderer al arbol. El renderer lo llamamos BasicoRenderer:
arbol.setCellRenderer( new BasicoRenderer( ...argumento... ));
El renderer define el aspecto (color, fuente,icono, etc.) de cada nodo en tiempo de ejecución. Por ejemplo, si tuvieramos un árbol de contactos donde cada uno de los contactos tiene asignado teléfonos, direcciones, etc. nos puede interesar tener iconos diferentes para los nodos: unos para teléfonos, otros para direcciones, etc. En nuestro caso tenemos un icono para el nodo raíz y otro para cada cliente.
¿Cómo añadir nodos al arbol? Al igual que ocurre en listas y arboles se utiliza el patrón modelo-vista. Tenemos que crear un modelo al que asignamos un nodo raiz (al nodo raíz se le asocia un título, en este caso "Clientes"):
DefaultMutableTreeNode nodoRaiz = new DefaultMutableTreeNode( "Clientes" ); DefaultTreeModel modeloArbol = new DefaultTreeModel( nodoRaiz );
En nuestro ejemplo tenemos un vector de objetos de la clase BasicoCliente. Hay un aspecto importante (al igual que podemos ver en las listas): cada nodo (DefaultMutableTreeNode) tiene asociado un objeto del dominio del problema (en nuestro caso se denomina BasicoCliente). En suma, el nodo encapsula el objeto del dominio. Esto tiene una ventaja: la estructura del interfaz es isomorfa con la estructura de domino. Así, si añadimos o borramos nodos, también añadimos y borramos referencias a los objetos del dominio. En nuestro ejemplo obtenemos el objeto del domino mediante filas.get(i). Todos los nodos se añaden al nodo raíz mediante add(nodo_hijo):
for (int i=0; i < filas.size(); i++) {
nodoNuevo = new DefaultMutableTreeNode( filas.get(i) ); // El nuevo nodo contiene el objeto
nodoRaiz.add( nodoNuevo ); // Añado el nuevo nodo a su nodo padre (nodo raiz)
}
arbol.setModel( modeloArbol ); // Asigno modelo al arbol
La última línea hace algo imprescindible una vez definidos los nodos: asigna el modelo al arbol.
Una advertencia: en caso de querer añadir nodos una vez definido el árbol, utilizariamos el método insertNodeInto():
//// Consigo el modelo de arbol e INSERTO NUEVO NODO HIJO DefaultTreeModel modelo_arbol = (DefaultTreeModel) arbol.getModel(); modelo_arbol.insertNodeInto( nodo_hijo, nodo_padre, nodo_padre.getChildCount()); //// Me aseguro que el nuvo nodo es visible arbol.scrollPathToVisible(new TreePath( nodo_hijo.getPath()));
El servlet realiza una consulta a la base de datos mediante JDBC. El resultado de la consulta se almacena en un vector y se envia al applet. Para que este envio sea posible es necesario que las clases que se envian usen el interfaz serializable (java.util.Vector usa dicho interfaz). En el servlet se abre un stream de salida para objetos y escribimos el vector en el stream:
ObjectOutputStream salida = new ObjectOutputStream(response.getOutputStream()); salida.writeObject( vectorSalida ); salida.flush();
En el applet se realiza la conexión al servlet y se crea un stream de entrada. La lectura del stream, con readObject(), obtiene el vector:
URL urlServlet = new URL( pathServlet ); URLConnection conServlet = urlServlet.openConnection(); // Abro conexión con url conServlet.setUseCaches( false ); // El navegador no usa caché //// Creamos un stream de entrada a partir de la conexión. Recibimos vector de objetos. ObjectInputStream streamEntrada = new ObjectInputStream (conServlet.getInputStream()); Vector filas = (Vector) streamEntrada.readObject(); // Obtenemos objetos streamEntrada.close();
En el constructor se cargan los iconos. El renderer debe implementar el método getTreeCellRendererComponent(). Este método se ejecutará para cada nodo, cada vez que el nodo es actualizado. Es la JVM la que llama a este método cada vez que hay que definir el aspecto de un nodo.
import javax.swing.Icon;
import java.awt.Component;
import java.awt.Color;
import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.JTree;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.ImageIcon;
/********************************************************************
* Renderer para celdas de un arbol
********************************************************************/
public class BasicoRenderer extends DefaultTreeCellRenderer {
//// Iconos que usará el renderer
ImageIcon iiCliente;
ImageIcon iiVenta;
/********************************************************************
* Constructor que carga los iconos.
********************************************************************/
public BasicoRenderer( Utilidades ut ) {
iiCliente = ut.getImageIcon( ut.getParametro( "icono.cliente") );
iiVenta = ut.getImageIcon( ut.getParametro( "icono.venta") );
}
/********************************************************************
* getListCellRendererComponent(), invocado por JVM cuando una celda se dibuja
********************************************************************/
public Component getTreeCellRendererComponent( JTree tree, Object value, boolean sel,
boolean expanded, boolean leaf,
int row, boolean hasFocus) {
super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row,
hasFocus);
try {
//// Obtengo el nodo
DefaultMutableTreeNode nodo = (DefaultMutableTreeNode) value;
if (nodo == null)
return this;
//// En función del nivel del nodo dentro del arbol, lo pinto ....
if ( nodo.getLevel() < 1 ) {
setForeground(Color.BLACK);
setIcon(iiCliente);
}
else {
setForeground(Color.BLUE);
setIcon(iiVenta);
}
return this;
}
catch (Exception e) {
e.printStackTrace();
return this;
}
}
}