Ao invés de usar o DefaultMutableTreeNode, criem um TreeModel próprio.
É muito mais fácil.
Considerem uma classe de Livros, que contém uma lista de autores, como essa aqui:
[code]
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class Livro {
private String nome;
private List<Autor> autores = new ArrayList<Autor>();
public Livro(String nome) {
this.nome = nome;
}
public String getNome() {
return nome;
}
@Override
public String toString() {
return getNome();
}
public void addAutor(Autor autor) {
autores.add(autor);
}
public List<Autor> getAutores() {
return Collections.unmodifiableList(autores);
}
}[/code]
A classe do Autor não é muito diferente:
[code]
public class Autor {
private String nome;
public Autor(String nome) {
this.nome = nome;
}
public String getNome() {
return nome;
}
@Override
public String toString() {
return getNome();
}
}[/code]
Ok. Agora queremos uma árvore com vários livros e a lista dos seus autores. O primeiro passo, é criar um modelo para essa árvore. Fazemos isso estendendo TreeModel:
[code]
import java.util.ArrayList;
import java.util.List;
import javax.swing.event.TreeModelListener;
import javax.swing.tree.TreeModel;
import javax.swing.tree.TreePath;
public class LivroTreeModel implements TreeModel {
//Criamos um objeto para nossa raiz. O List não pode ser usado diretamente pois
//seu hash irá mudar sempre que um novo livro for adicionado.
//Fora que é mais bonitinho escrever “Livros” no topo.
private String raiz = “Livros”;
// Escrevam depois AbstractTreeModel para lidar com os listeners.
private List<TreeModelListener> listeners = new ArrayList<TreeModelListener>();
// Raiz da nossa árvore, vamos exibir uma lista de livros.
private List<Livro> livros;
public LivroTreeModel(List<Livro> livros) {
this.livros = livros;
}
/**
* Com esse método, o Java quem é o objeto que está num determinado índice
* do pai. Cada nó de uma árvore pode ser encarado como uma lista. Sendo o
* pai a lista e o índice um dos filhos.
*
* @param parent
* É o pai, que tem os filhos. No caso do Livro, o próprio livro.
* @param index
* Índice do filho. No caso do livro, o índice corresponde aos
* autores.
*/
public Object getChild(Object parent, int index) {
if (parent == raiz) // É o nó principal?
return livros.get(index); // Pegamos da lista de livro
if (parent instanceof Livro) // O pai é um livro?
{
// Devolvemos um autor
return ((Livro) parent).getAutores().get(index);
}
// Se o pai não é nenhum desses. Melhor dar erro.
throw new IllegalArgumentException("Invalid parent class"
+ parent.getClass().getSimpleName());
}
/**
* Retornamos quantos filhos um pai tem. No caso de um livro, é a contagem
* de autores. No caso da lista de livros, é a quantidade de livros.
*/
public int getChildCount(Object parent) {
// Mesma lógica.
if (parent == raiz)
return livros.size();
if (parent instanceof Livro) // O pai é um livro?
return ((Livro) parent).getAutores().size();
// Se o pai não é nenhum desses. Melhor dar erro.
throw new IllegalArgumentException("Invalid parent class"
+ parent.getClass().getSimpleName());
}
/**
* Dado um pai, indicamos qual é o índice do filho correspondente.
*/
public int getIndexOfChild(Object parent, Object child) {
if (parent == raiz)
return livros.indexOf(child);
if (parent instanceof Livro)
return ((Livro) parent).getAutores().indexOf(child);
return 0;
}
/**
* Devemos retornar quem é o nó raiz da árvore. Afinal, a árvore tem que
* começar em algum lugar.
*/
public Object getRoot() {
return raiz;
}
/**
* Indicamos se um nó é ou não uma folha. Isso é, se ele não tem filhos. No
* nosso caso, os autores são as folhas da árvore.
*/
public boolean isLeaf(Object node) {
return node instanceof Autor;
}
public void valueForPathChanged(TreePath path, Object newValue) {
// Com esse método, a tree avisa que um objeto mudou.
// Editem se quiserem que um nó seja editável
}
// Esses dois métodos abaixo poderiam ir para classe abstrata
public void removeTreeModelListener(TreeModelListener l) {
listeners.remove(l);
}
public void addTreeModelListener(TreeModelListener l) {
listeners.add(l);
}
}[/code]
Com o model pronto, agora só falta desenhar uma Tree usando esse model. Aqui está uma telinha que faz exatamente isso, para servir de demonstração:
[code]
import java.awt.BorderLayout;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTree;
import javax.swing.tree.TreeModel;
public class Tela extends JDialog {
private static final long serialVersionUID = 1L;
private JPanel jContentPane = null;
private JScrollPane scrlTree = null;
private JTree treeLivros = null;
private TreeModel treeModel;
public Tela(TreeModel treeModel) {
super();
this.treeModel = treeModel;
initialize();
}
private void initialize() {
this.setSize(361, 240);
this.setLocationRelativeTo(null);
this.setTitle("Tree");
this.setContentPane(getJContentPane());
this.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
}
private JPanel getJContentPane() {
if (jContentPane == null) {
jContentPane = new JPanel();
jContentPane.setLayout(new BorderLayout());
jContentPane.add(getScrlTree(), BorderLayout.CENTER);
}
return jContentPane;
}
private JScrollPane getScrlTree() {
if (scrlTree == null) {
scrlTree = new JScrollPane();
scrlTree.setViewportView(getTreeLivros());
}
return scrlTree;
}
private JTree getTreeLivros() {
if (treeLivros == null) {
treeLivros = new JTree(treeModel);
treeLivros.setRootVisible(false);
treeLivros.setShowsRootHandles(true);
}
return treeLivros;
}
public static void main(String[] args) {
List<Livro> livros = new ArrayList<Livro>();
Livro livro = new Livro("Duna");
livro.addAutor(new Autor("Frank Herbert"));
livros.add(livro);
Livro livro2 = new Livro("50 Anos Depois");
livro2.addAutor(new Autor("Chico Xavier"));
livro2.addAutor(new Autor("Emmanuel (Espírito)"));
livros.add(livro2);
Livro livro3 = new Livro("O rapto do garoto de ouro");
livro3.addAutor(new Autor("Marcos Rey"));
livros.add(livro3);
Tela tela = new Tela(new LivroTreeModel(livros));
tela.setVisible(true);
}
} [/code]
Escrever o seu próprio TreeModel não só é mais fácil para Trees maiores, como também deixa o código mais separado e orientado a objetos. Note que a minha classe de interface gráfica praticamente não se preocupou com a lógica da tree em si. Quem faz isso é o modelo. Esse decoplamento é util, caso você queira reaproveitar a tree em outras telas, já que não é a tela em si que conhece a estrutura da árvore.
Também tem a vantagem de que o TreeModel manipula diretamente as suas classes de negócio. Para tornar a Tree modificável, você deve inserir métodos no model, e notificar os listeners de que a árvore mudou, e onde. Anexo a esse post, está um AbstractTreeModel que costumo a usar nos meus programas, retirado dos próprios exemplos da Sun.
Ele já cuida da parte dos listeners de eventos, além de fornecer métodos práticos para notificar a tree sobre mudanças.
A minha sugestão (e eu já sofri muito da outra forma antes de descobrir essa) é que vocês não usem os DefaultMutableTreeNodes para nada.