Reputation: 529
I'm currently facing a challenge with my JTree
implementation, and I'm seeking to find a solution. The JTree
displays a hierarchical structure with nodes
and checkboxes
, and I want to make the root node selectable as a Checkbox
. Checkboxes in the nodes are selectable.
Tried this implementation, and it shows the root
as a checkbox
but it remains unclickable. I've tried adjusting the renderer and editor classes, but so far, without success. Tried to write a Minimum Reproducible Version
, but it is still quite lengthy due to the TreeCellRenderer
implementation:
MainFrame.java:
package test;
import javax.swing.*;
import java.awt.*;
import java.util.*;
import java.util.List;
import javax.swing.tree.*;
public class MinimalReproducibleExample{
private JFrame frame;
private JTree tree;
public MainFrame() {
initialize();
}
public static void main(String[] args) {
EventQueue.invokeLater(() -> {
try {
MainFrame window = new MainFrame();
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>> packagesAndFiles = 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());
}
}
packagesAndFiles.put("Root", nodes);
tree = new CustomCheckBoxTreeSample().createCustomCheckBoxTree(packagesAndFiles);
JScrollPane scrollPane = new JScrollPane(tree);
frame.getContentPane().add(scrollPane, BorderLayout.CENTER);
}
}
CustomCheckBoxNodeEditor.java:
package test;
import javax.swing.*;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.TreeCellEditor;
import javax.swing.tree.TreeCellRenderer;
import javax.swing.tree.TreePath;
import java.awt.*;
import java.awt.event.MouseEvent;
import java.util.EventObject;
import java.util.List;
import java.util.Map;
import java.util.Vector;
public class CustomCheckBoxTreeSample {
public CustomCheckBoxTreeSample() {
}
public JTree createCustomCheckBoxTree(Map<String, List<String>> packagesAndFiles) {
List<Vector<Object>> vectors = new Vector<>();
for (Map.Entry<String, List<String>> entry : packagesAndFiles.entrySet()) {
String packageName = entry.getKey();
packageName = packageName.substring(1);
System.out.println("Package: " + packageName);
List<String> fileNames = entry.getValue();
CustomCheckBoxNode checkBoxNodes[] = new CustomCheckBoxNode[fileNames.size()];
for (int i = 0; i < fileNames.size(); i++) {
checkBoxNodes[i] = new CustomCheckBoxNode(fileNames.get(i), true);
}
Vector<Object> packageVector = new NamedVector(packageName, checkBoxNodes);
vectors.add(packageVector);
}
CustomCheckBoxNodeRenderer renderer = new CustomCheckBoxNodeRenderer();
Object rootNodes[] = new Object[packagesAndFiles.size()];
for (int i = 0; i < vectors.size(); i++) {
rootNodes[i] = vectors.get(i);
}
Vector<Object> rootVector = new NamedVector("Root", rootNodes);
JTree tree = new JTree(rootVector);
tree.setVisibleRowCount(50);
tree.setRootVisible(false);
tree.setShowsRootHandles(true);
tree.setCellRenderer(renderer);
tree.setCellEditor(new CustomCheckBoxNodeEditor(tree));
tree.setEditable(true);
return tree;
}
}
class CustomCheckBoxNodeRenderer implements TreeCellRenderer {
private JCheckBox leafRenderer = new JCheckBox();
private Color selectionForeground, selectionBackground, textForeground, textBackground;
protected JCheckBox getLeafRenderer() {
return leafRenderer;
}
public CustomCheckBoxNodeRenderer() {
Font fontValue = UIManager.getFont("Tree.font");
if (fontValue != null) {
leafRenderer.setFont(fontValue);
}
Boolean booleanValue = (Boolean) UIManager.get("Tree.drawsFocusBorderAroundIcon");
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 CustomCheckBoxNode) {
CustomCheckBoxNode node = (CustomCheckBoxNode) userObject;
leafRenderer.setText(node.getText());
leafRenderer.setSelected(node.isSelected());
}
}
returnValue = leafRenderer;
return returnValue;
}
}
class CustomCheckBoxNodeEditor extends AbstractCellEditor implements TreeCellEditor {
private static final long serialVersionUID = 1L;
private CustomCheckBoxNodeRenderer renderer = new CustomCheckBoxNodeRenderer();
private JTree tree;
public CustomCheckBoxNodeEditor(JTree tree) {
this.tree = tree;
}
@Override
public Object getCellEditorValue() {
JCheckBox checkbox = renderer.getLeafRenderer();
CustomCheckBoxNode customCheckBoxNode = new CustomCheckBoxNode(checkbox.getText(), checkbox.isSelected());
return customCheckBoxNode;
}
@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 = ((treeNode.isLeaf()) && (userObject instanceof CustomCheckBoxNode));
}
}
}
return returnValue;
}
@Override
public Component getTreeCellEditorComponent(JTree tree, Object value, boolean isSelected, boolean expanded, boolean leaf, int row) {
return renderer.getTreeCellRendererComponent(tree, value, true, expanded, leaf, row, true);
}
}
class CustomCheckBoxNode {
private String text;
private boolean selected;
public CustomCheckBoxNode(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 String name;
public NamedVector(String name) {
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 + "]";
}
}
Upvotes: 0
Views: 59