
Neste artigo, veremos
como criar uma árvore (JTree) com nós e folhas personalizados. Para isso,
criaremos nossas classes auxiliares que representam nós (terminação Node) e
folhas (terminação Leaf). Todas necessitam ter um método toString() sobrescrito para que o
objeto JTree apresente o texto de cada nó/folha apropriadamente.
Vejam o exemplo das
classes abaixo:
...
// Classe que
representa um "nó" para Projeto
private class ProjectNode {
public String name;
public int id;
public ProjectNode(String name, int id) {
this.name = name;
this.id = id;
}
public String toString() {
return name;
}
}
// Classe que representa um "nó"
para Contrato
private class ContractNode {
public String name;
public ContractNode(String name) {
this.name = name;
}
public String toString() {
return name;
}
}
// Classe que representa um "nó"
para Conta
private class AccountNode {
public String name;
public AccountNode(String name) {
this.name = name;
}
public String toString() {
return name;
}
}
// Classe que representa um "nó"
para Equipe
private class TeamNode {
public String name;
public TeamNode(String name) {
this.name = name;
}
public String toString() {
return name;
}
}
// Classe que representa um
"sub-nó" para Equipe
private class TeamSubNode {
public String name;
public TeamSubNode(String name) {
this.name = name;
}
public String toString() {
return name;
}
}
// Classe que representa um "nó"
para Tarefa
private class TaskNode {
public String name;
public TaskNode(String name) {
this.name = name;
}
public String toString() {
return name;
}
}
// Classe que representa uma
"folha" para Contrato
private class ContractLeaf {
public String contract, company;
public double value;
public ContractLeaf(String contract, String
company, double value) {
this.contract = contract;
this.company = company;
this.value = value;
}
public String toString() {
return contract + " - Empresa:
" + company + " - Valor: " + value;
}
}
// Classe que representa uma
"folha" para Conta
private class AccountLeaf {
public String login, name;
public AccountLeaf(String login, String
name) {
this.login = login;
this.name = name;
}
public String toString() {
return login + " - " + name;
}
}
// Classe que representa uma
"folha" para membro de Equipe
private class MemberLeaf {
public String name, company;
public MemberLeaf(String name, String
company) {
this.name = name;
this.company = company;
}
public String toString() {
return name + " - Empresa: " +
company;
}
}
// Classe que representa uma
"folha" para Tarefa
private class TaskLeaf {
public String desc, priority, step;
public double prevHours, realHours;
public TaskLeaf(String desc, double
prevHours, double realHours, String priority,
String step) {
this.desc = desc;
this.prevHours = prevHours;
this.realHours = realHours;
this.priority = priority;
this.step = step;
}
public String toString() {
return desc + " - HH Prev: " +
prevHours + " - HH Real: " + realHours +
" - Prioridade: " +
priority + " - " + step;
}
}
...
A seguir, criaremos
um conjunto de ícones para serem usados depois; eles podem ser criados no
construtor da classe principal:
// Cria os ícones;
IMG_PATH é uma constante que representa o caminho para as imagens
projectNodeIcon = new ImageIcon(IMG_PATH +
"project.jpg");
contractNodeIcon = new ImageIcon(IMG_PATH +
"contracts.jpg");
contractLeafIcon = new ImageIcon(IMG_PATH +
"contract.jpg");
accountNodeIcon = new ImageIcon(IMG_PATH +
"accounts.jpg");
accountLeafIcon = new ImageIcon(IMG_PATH +
"account.jpg");
teamNodeIcon = new ImageIcon(IMG_PATH +
"team.jpg");
memberLeafIcon = new ImageIcon(IMG_PATH +
"member.jpg");
taskNodeIcon = new ImageIcon(IMG_PATH +
"tasks.jpg");
taskLeafIcon = new ImageIcon(IMG_PATH +
"task.jpg");
Agora que as classes
nó/folha estão criadas (e seus respectivos ícones), podemos partir para a
classe que será a responsável por renderizar tais nós/folhas. Ela deve extender
a classe DefaultTreeCellRenderer do pacote javax.swing.tree. A chave para essa
classe é o método public Component getTreeCellRendererComponent(...). Neste
método, obtemos o valor de um nó da árvore pelo parâmetro value, e fazemos um
"cast" como em:
DefaultMutableTreeNode node =
(DefaultMutableTreeNode) value;
a seguir, obtemos um Object usando Object
object = node.getUserObject(); de posse dessa variável object, usamos uma cadeia
de if's com comparações do tipo instanceof para verificar à qual classe
pertence a nossa variável object; dependendo de qual classe nó/folha ela
pertencer, nós ajustamos a fonte e o ícone de acordo.
Vamos ver abaixo como
deve se comportar esta classe (aqui, chamada de CustomRenderer):
// Classe interna
para renderizar as imagens dos nodos da árvore
private class CustomRenderer extends
DefaultTreeCellRenderer {
// Sobrescreve este método para obter os
efeitos desejados
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);
DefaultMutableTreeNode node =
(DefaultMutableTreeNode) value;
Object object = node.getUserObject();
if (object instanceof ProjectNode) {
setIcon(projectNodeIcon);
setFont(new Font("Tahoma",
Font.BOLD, 11));
}
else if (object instanceof ContractNode)
{
setIcon(contractNodeIcon);
setFont(new Font("Tahoma",
Font.BOLD, 11));
}
else if (object instanceof AccountNode) {
setIcon(accountNodeIcon);
setFont(new Font("Tahoma",
Font.BOLD, 11));
}
else if (object instanceof TeamNode) {
setIcon(teamNodeIcon);
setFont(new Font("Tahoma",
Font.BOLD, 11));
}
else if (object instanceof TeamSubNode) {
setIcon(teamNodeIcon);
setFont(new Font("Tahoma",
Font.PLAIN, 11));
}
else if (object instanceof TaskNode) {
setIcon(taskNodeIcon);
setFont(new Font("Tahoma",
Font.BOLD, 11));
}
else if (object instanceof ContractLeaf)
{
setIcon(contractLeafIcon);
setFont(new Font("Tahoma",
Font.PLAIN, 11));
}
else if (object instanceof AccountLeaf) {
setIcon(accountLeafIcon);
setFont(new Font("Tahoma",
Font.PLAIN, 11));
}
else if (object instanceof MemberLeaf) {
setIcon(memberLeafIcon);
setFont(new Font("Tahoma",
Font.PLAIN, 11));
}
else if (object instanceof TaskLeaf) {
setIcon(taskLeafIcon);
setFont(new Font("Tahoma",
Font.PLAIN, 11));
}
else
setFont(new Font("Tahoma",
Font.PLAIN, 11));
return this;
}
}
...
O nosso objeto JTree
deve ser criado como segue:
// Cria a árvore;
inicialmente vazia
root = new DefaultMutableTreeNode("--
Selecione um Projeto --");
projectTree = new JTree(root);
projectTree.setShowsRootHandles(true);
projectTree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
projectTree.putClientProperty("JTree.lineStyle",
"Angled");
projectTree.setCellRenderer(new
CustomRenderer()); // Aqui usamos a nossa classe renderizadora !!!
scroller = new JScrollPane(projectTree);
...
Quase tudo está
pronto. O único ponto a se observar agora é que quando formos popular a árvore
(a partir de um banco de dados, por exemplo), devemos criar seus objetos desse modo:
...
// Método que recebe
o nome e o id_projeto e cria toda a estrutura da árvore de
// projetos, realizando as consultas necessárias
private void populateProjectTree(String
projectName, int projectId) {
// "Nós" genéricos para uso
DefaultMutableTreeNode node, subNode,
child;
// Cria um objeto ProjectNode com o nome do
projeto
ProjectNode projectNode = new
ProjectNode(projectName, projectId);
// Atualiza o nó raiz com um objeto
ProjectNode
root.setUserObject(projectNode);
// Limpa toda a estrutura da árvore
root.removeAllChildren();
...
// Preenche os
contratos
node = new DefaultMutableTreeNode(new
ContractNode("Contratos"));
root.add(node);
...
while
(resultSet.next()) {
String contractNumber =
resultSet.getString("numero_contrato");
String companyName =
resultSet.getString("E.nome");
double contractValue =
resultSet.getDouble("valor_contrato");
child = new DefaultMutableTreeNode(
new ContractLeaf(contractNumber,
companyName, contractValue));
node.add(child);
}
...
// Preenche as contas
node = new DefaultMutableTreeNode(new
AccountNode("Contas"));
root.add(node);
...
while
(resultSet.next()) {
String login =
resultSet.getString("login");
String userName =
resultSet.getString("F.nome");
child = new DefaultMutableTreeNode(new
AccountLeaf(login, userName));
node.add(child);
}
...
// Preencher todos os
outros objetos de modo similar...
...
// Atualiza a árvore
projectTree.updateUI();
...
E assim por diante.
Supomos aqui, por conveniência, que estamos conectados a um banco de dados e
emitindo consultas SQL para popular os nós/folhas da árvore. Na prática, use
objetos DAO (Data Access Objects) para emitir as consultas e fornecer os
valores para os objetos nós/folhas, separando as camadas devidamente.
Pronto!! É isso...
Espero que o código essencial mostrado aqui tenha sido claro para ilustrar o
artigo. Abraços e até o próximo artigo.