Facing this issue with my code while working with checkboxes in a JTree. The root node is treated as a "Named Vector" in first iteration instead of a "CheckBoxNode"As a result, it doesn't iterate through its child checkboxes.
In the second iteration, the root node becomes a "CheckBoxNode," causing all child checkboxes to be selected, but they should already be selected and instead should be deselected:
Second Iteration and Second Click:
Result:
JanelaPrincipal.java:
package br.josueborges.principal;
import javax.swing.*;
import java.awt.*;
import java.util.*;
import java.util.List;
import javax.swing.tree.*;
public class JanelaPrincipal {
private JFrame frame;
private JTree tree;
public JanelaPrincipal() {
initialize();
}
public static void main(String[] args) {
EventQueue.invokeLater(() -> {
try {
JanelaPrincipal window = new JanelaPrincipal();
window.frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
});
}
private void initialize() {
frame = new JFrame();
frame.setBounds(100, 100, 400, 400);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().setLayout(new BorderLayout());
DefaultMutableTreeNode root = new DefaultMutableTreeNode("Root");
DefaultMutableTreeNode folder1 = new DefaultMutableTreeNode("Folder 1");
DefaultMutableTreeNode folder2 = new DefaultMutableTreeNode("Folder 2");
DefaultMutableTreeNode file1 = new DefaultMutableTreeNode("File 1");
DefaultMutableTreeNode file2 = new DefaultMutableTreeNode("File 2");
DefaultMutableTreeNode file3 = new DefaultMutableTreeNode("File 3");
root.add(folder1);
root.add(folder2);
folder1.add(file1);
folder2.add(file2);
folder2.add(file3);
Map<String, List<String>> pacotesEArquivos = new HashMap<>();
List<String> nodes = new ArrayList<>();
Enumeration<?> enumeration = root.breadthFirstEnumeration();
while (enumeration.hasMoreElements()) {
DefaultMutableTreeNode node = (DefaultMutableTreeNode) enumeration.nextElement();
if (node.isLeaf()) {
nodes.add(node.toString());
}
}
pacotesEArquivos.put("Root", nodes);
tree = new CheckBoxNodeTreeSample().retornaJTree(pacotesEArquivos);
JScrollPane scrollPane = new JScrollPane(tree);
frame.getContentPane().add(scrollPane, BorderLayout.CENTER);
}
}
CheckBoxNode.java:
package br.josueborges.principal;
import java.awt.Color;
import java.awt.Component;
import java.awt.Font;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.MouseEvent;
import java.util.EventObject;
import java.util.List;
import java.util.Map;
import java.util.Vector;
import java.util.*;
import javax.swing.AbstractCellEditor;
import javax.swing.JCheckBox;
import javax.swing.JTree;
import javax.swing.UIManager;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeCellEditor;
import javax.swing.tree.TreeCellRenderer;
import javax.swing.tree.TreePath;
public class CheckBoxNodeTreeSample {
public CheckBoxNodeTreeSample() {
}
public JTree retornaJTree(Map<String, List<String>> pacotesEArquivos) {
ArrayList<Vector<Object>> vetores = new ArrayList<Vector<Object>>();
for (Map.Entry<String, List<String>> entry : pacotesEArquivos.entrySet()) {
String pacote = entry.getKey();
pacote = pacote.substring(1);
System.out.println("Pacote: " + pacote);
List<String> nomesArquivos = entry.getValue();
CheckBoxNode checkBoxNode[] = new CheckBoxNode[nomesArquivos.size()];
// String nomeArquivo : nomesArquivos
for (int i = 0; i < nomesArquivos.size(); i++) {
checkBoxNode[i] = new CheckBoxNode(nomesArquivos.get(i), false);
}
Vector<Object> vetorDoJTree = new NamedVector(pacote, checkBoxNode);
vetores.add(vetorDoJTree);
}
CheckBoxNodeRenderer renderer = new CheckBoxNodeRenderer();
Object rootNodes[] = new Object[pacotesEArquivos.size()];
for (int i = 0; i < vetores.size(); i++) {
rootNodes[i] = vetores.get(i);
}
Vector<Object> rootVector = new NamedVector("Teste", rootNodes);
JTree tree = new JTree(rootVector);
tree.setVisibleRowCount(50);
tree.setRootVisible(false);
tree.setShowsRootHandles(true);
tree.setCellRenderer(renderer);
tree.setCellEditor(new CheckBoxNodeEditor(tree));
tree.setEditable(true);
return tree;
}
}
class CheckBoxNodeRenderer implements TreeCellRenderer {
private JCheckBox leafRenderer = new JCheckBox();
private Color selectionForeground, selectionBackground, textForeground, textBackground;
protected JCheckBox getLeafRenderer() {
return leafRenderer;
}
public CheckBoxNodeRenderer() {
Font fontValue;
fontValue = UIManager.getFont("Tree.font");
if (fontValue != null) {
leafRenderer.setFont(fontValue);
}
Boolean booleanValue = (Boolean) UIManager.get("Tree.drawsFocusBorderAroundIcon");
System.out.println("booleanValue: " + booleanValue);
leafRenderer.setFocusPainted((booleanValue != null) && (booleanValue.booleanValue()));
UIManager.getColor("Tree.selectionBorderColor");
selectionForeground = UIManager.getColor("Tree.selectionForeground");
selectionBackground = UIManager.getColor("Tree.selectionBackground");
textForeground = UIManager.getColor("Tree.textForeground");
textBackground = UIManager.getColor("Tree.textBackground");
}
@Override
public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected, boolean expanded,
boolean leaf, int row, boolean hasFocus) {
Component returnValue;
String stringValue = tree.convertValueToText(value, selected, expanded, leaf, row, false);
leafRenderer.setText(stringValue);
leafRenderer.setSelected(false);
leafRenderer.setEnabled(tree.isEnabled());
if (selected) {
leafRenderer.setForeground(selectionForeground);
leafRenderer.setBackground(selectionBackground);
} else {
leafRenderer.setForeground(textForeground);
leafRenderer.setBackground(textBackground);
}
if ((value != null) && (value instanceof DefaultMutableTreeNode)) {
Object userObject = ((DefaultMutableTreeNode) value).getUserObject();
if (userObject instanceof CheckBoxNode) {
CheckBoxNode node = (CheckBoxNode) userObject;
leafRenderer.setText(node.getText());
leafRenderer.setSelected(node.isSelected());
}
}
returnValue = leafRenderer;
return returnValue;
}
}
class CheckBoxNodeEditor extends AbstractCellEditor implements TreeCellEditor {
private static final long serialVersionUID = 1L;
private CheckBoxNodeRenderer renderer = new CheckBoxNodeRenderer();
private JTree tree;
public CheckBoxNodeEditor(JTree tree) {
this.tree = tree;
}
@Override
public Object getCellEditorValue() {
JCheckBox checkbox = renderer.getLeafRenderer();
CheckBoxNode checkBoxNode = new CheckBoxNode(checkbox.getText(), checkbox.isSelected());
return checkBoxNode;
}
@Override
public boolean isCellEditable(EventObject event) {
boolean returnValue = false;
if (event instanceof MouseEvent) {
MouseEvent mouseEvent = (MouseEvent) event;
TreePath path = tree.getPathForLocation(mouseEvent.getX(), mouseEvent.getY());
if (path != null) {
Object node = path.getLastPathComponent();
if ((node != null) && (node instanceof DefaultMutableTreeNode)) {
DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode) node;
Object userObject = treeNode.getUserObject();
returnValue = true;
}
}
}
return returnValue;
}
@Override
public Component getTreeCellEditorComponent(JTree tree, Object value, boolean selected, boolean expanded,
boolean leaf, int row) {
Component editor = renderer.getTreeCellRendererComponent(tree, value, true, expanded, leaf, row, true);
ItemListener itemListener = new ItemListener() {
@Override
public void itemStateChanged(ItemEvent itemEvent) {
if (stopCellEditing()) {
fireEditingStopped();
}
}
};
if (editor instanceof JCheckBox) {
JCheckBox checkBox = (JCheckBox) editor;
checkBox.removeItemListener(itemListener);
checkBox.addItemListener(itemListener);
// Ao clicar no checkbox, atualizar o estado de todos os checkboxes filhos e pais
DefaultMutableTreeNode node = (DefaultMutableTreeNode) value;
Object userObject = node.getUserObject();
if (userObject instanceof CheckBoxNode) {
CheckBoxNode parentNode = (CheckBoxNode) userObject;
boolean parentSelected = checkBox.isSelected();
// Percorrer os nós filhos (arquivos) e definir o mesmo estado do checkbox pai
for (int i = 0; i < node.getChildCount(); i++) {
DefaultMutableTreeNode childNode = (DefaultMutableTreeNode) node.getChildAt(i);
Object childUserObject = childNode.getUserObject();
if (childUserObject instanceof CheckBoxNode) {
CheckBoxNode childCheckBoxNode = (CheckBoxNode) childUserObject;
childCheckBoxNode.setSelected(parentSelected);
}
}
// Atualizar o estado dos nós pais
updateParentNodes(node, parentSelected);
// Notificar a árvore que houve mudança no modelo
((DefaultTreeModel) tree.getModel()).nodeStructureChanged(node);
}
}
return editor;
}
// Método para atualizar o estado dos nós pais
private void updateParentNodes(DefaultMutableTreeNode node, boolean selected) {
DefaultMutableTreeNode parentNode = (DefaultMutableTreeNode) node.getParent();
if (parentNode != null) {
Object parentUserObject = parentNode.getUserObject();
if (parentUserObject instanceof CheckBoxNode) {
CheckBoxNode parentCheckBoxNode = (CheckBoxNode) parentUserObject;
// Verificar se todos os filhos estão selecionados e atualizar o estado do nó
// pai
boolean allChildrenSelected = true;
for (int i = 0; i < parentNode.getChildCount(); i++) {
DefaultMutableTreeNode childNode = (DefaultMutableTreeNode) parentNode.getChildAt(i);
Object childUserObject = childNode.getUserObject();
if (childUserObject instanceof CheckBoxNode) {
CheckBoxNode childCheckBoxNode = (CheckBoxNode) childUserObject;
if (!childCheckBoxNode.isSelected()) {
allChildrenSelected = false;
break;
}
}
}
parentCheckBoxNode.setSelected(allChildrenSelected);
// Continuar atualizando os nós pais recursivamente
updateParentNodes(parentNode, selected);
}
}
}
}
class CheckBoxNode {
private String text;
private boolean selected;
public CheckBoxNode(String text, boolean selected) {
this.text = text;
this.selected = selected;
}
public boolean isSelected() {
return selected;
}
public void setSelected(boolean newValue) {
selected = newValue;
}
public String getText() {
return text;
}
public void setText(String newValue) {
text = newValue;
}
@Override
public String toString() {
return getClass().getName() + "[" + text + "/" + selected + "]";
}
}
class NamedVector extends Vector<Object> {
private static final long serialVersionUID = 1L;
private String name;
public NamedVector(String name) {
super();
this.name = name;
}
public NamedVector(String name, Object elements[]) {
this.name = name;
for (int i = 0, n = elements.length; i < n; i++) {
add(elements[i]);
}
}
@Override
public String toString() {
return "[" + name + "]";
}
}