El modelo de renderizado permitirá personalizar el aspecto completo del árbol teniendo en cuenta cada nodo o cada hoja de forma individual. Como ya se vio anteriormente, las modificaciones que se pueden realizar sobre el aspecto es cambiar los iconos de los nodos cerrados / abiertos o de las hojas

DefaultTreeCellRenderer renderArbol = new DefaultTreeCellRenderer();
renderArbol.setLeafIcon(new ImageIcon(getClass().getResource("/resources/document.png")));
renderArbol.setOpenIcon(new ImageIcon(getClass().getResource("/resources/open.png")));
renderArbol.setClosedIcon(new ImageIcon(getClass().getResource("/resources/close.png")));
renderArbol.setBorder(BorderFactory.createTitledBorder("Arbol"));
arbol.setCellRenderer(renderArbol);

Esta forma de personalizar puede que se quede un poco corta, debido a que tan solo puedes poner tres tipos de icono y siempre el contenido del nodo aparecerá de la misma forma. Para poder profundizar en el aspecto se debe crear un modelo implementando la interfaz TreeCellRenderer si se quieren hacer cosas completas o de la interfaz DefaultTreeCellRenderer si se quieren hacer cosas simples. La implementación del primero obliga a traer el método getTreeCellRendererComponent el cual se encarga de pintar cada uno de los componentes del árbol

public class ModeloPersonalizado implements TreeCellRenderer {
    @Override
    public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) {
        return null;
    }
}

Los parámetros que admite el método son:

  • tree: árbol sobre el que se el componente
  • value: nodo que representará
  • selected: indica si está o no seleccionado
  • expanded: indica si está o no expandido
  • leaf: indica si el nodo es una hoja
  • row: fila del árbol en la que está el nodo.
  • hasFocus: indica si el nodo tiene el foco.

Con estos parámetros se puede personalizar el aspecto de cada nodo teniendo en cuenta múltiples variables. Como valor de retorno en el método se tendrá que pasar el componente que se quiera pintar (el elemento gráfico)

Lo primero que hay que tener en cuenta es que cada nodo representará una unidad independiente de tipo Component, por lo tanto todo lo que se quiera devolver tendrá que ir en esta unidad. Para eso la clase además de implementar la interfaz TreeCellRenderer extenderá de JPanel para poder convertirla en agrupador de componentes y tendrá todos los componentes que queramos representar. Supongamos que al árbol se le pasan nodos con objetos de tipo Persona (nombre, apellido, teléfono y trabajado (bool)). Se quiere representar en el árbol el nodo con el nombre y y check de su estado laboral

public class ModeloPersonalizado extends JPanel implements TreeCellRenderer {
    
    JLabel nombre;
    JCheckBox trabajando;
    DefaultTreeCellRenderer modeloDefecto;

    public ModeloPersonalizado() {
        this.nombre = new JLabel();
        this.trabajando = new JCheckBox();
        this.modeloDefecto = new DefaultTreeCellRenderer();
    }

    @Override
    public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) {
        return null;
    }
}

El modelo por defecto se utilizará para aquellos elementos que no queramos una personalización completa. El siguiente paso es crear la estructura gráfica que se representará en cada nodo

public ModeloPersonalizado() {
        this.nombre = new JLabel();
        this.trabajando = new JCheckBox();
        this.modeloDefecto = new DefaultTreeCellRenderer();
        this.add(nombre);
        this.add(trabajando);
        this.setBorder(BorderFactory.createTitledBorder("elemento"));
    }

En este punto se puede añadir y personalizar el panel tanto como se quiera. Lo siguiente será rellenar de forma individual cada nodo en el método getTreeCellRendererComponent teniendo en cuenta la situación de cada nodo (si es hoja o si es nodo, si es root, si esta seleccionado, etc…) y devolviendo el valor del componente a representar

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

        if (row > 0) {
            return this;
        } else {
            modeloDefecto.setIcon(new ImageIcon(getClass().getResource("/resources/close.png")));
            modeloDefecto.setText("Root");
            return modeloDefecto;
//return modeloDefecto.getTreeCellRendererComponent(tree, value, selected, expanded, leaf, row, hasFocus);
        }

    }

La primera evaluación por fila indica que si la fila es 0 (root) utilice el modelo por defecto con un icono preparado para el root. De no ser así se personalizará el componente. Para ello se obtendrá información del objeto asignado al nodo con el que se está trabajando (mediante el parámetro value) y se irá personalizando cada uno de los componentes.

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

        tree.setRowHeight(36);

        if (row > 0) {

            if (leaf){
                if (selected){
                    this.setBackground(Color.ORANGE);
                }
                else this.setBackground(Color.WHITE);

                Persona p = (Persona) ((DefaultMutableTreeNode)value).getUserObject();
                nombre.setText(p.getNombre());
                trabajando.setSelected(p.isMarcado());
                return this;

            }
            else {
                modeloDefecto.setIcon(new ImageIcon(getClass().getResource("/resources/close.png")));
                modeloDefecto.setText("Departamento");
                return modeloDefecto;
            }

        } else {
            modeloDefecto.setText("Inicial");
            modeloDefecto.setIcon(new ImageIcon(getClass().getResource("/resources/close.png")));
            return modeloDefecto;
        }

    }

[ezcol_2third]Del mismo modo se puede evaluar cada objeto asociado a cada nodo para saber si es de un tipo u otro y así representarlos de manera diferente

if (value instanceof Persona){}
else if(value instanceof Departamento){}

El último paso sería realizar una asociación con el modelo renderizado en la clase donde se encuentre el árbol

ModeloPersonalizado modeloGrafico = new ModeloPersonalizado();
arbol.setCellRenderer(modeloGrafico);

[/ezcol_2third] [ezcol_1third_end][/ezcol_1third_end]