I am wanting to create a custom JTree where it's nodes are JProgressBar's I have this working just like I want it

However you'll notice that it's missing it's open/closed icon, which I also want to be a custom +/- icon instead of the standard expandable folder. Not entirely sure what I'm doing wrong (Just developing the concept at the moment before prettifying the code)
package com.testingarea;
import java.awt.Component;
import java.awt.Graphics;
import java.util.HashMap;
import javax.swing.BoxLayout;
import javax.swing.Icon;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.JScrollPane;
import javax.swing.JTree;
import javax.swing.UIManager;
import javax.swing.plaf.IconUIResource;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeCellRenderer;
public class TestTrees {
public static void main(String args[]) {
JFrame frame = new JFrame("Tree");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
ProgressTreeCellRenderer renderer = new ProgressTreeCellRenderer();
JProgressBar progressBar = new JProgressBar(0, 100);
progressBar.setValue(50);
progressBar.setStringPainted(true);
DefaultMutableTreeNode top =
new DefaultMutableTreeNode(progressBar);
renderer.getNodeMap().put(top, new ProgressTreeCellRenderer2(progressBar));
createNodes(top, renderer);
DefaultTreeModel model = new DefaultTreeModel(top);
JTree tree = new JTree(model);
tree.setCellRenderer(renderer);
JScrollPane pane = new JScrollPane(tree);
tree.clearSelection();
frame.getContentPane().add(pane);
frame.setSize(250, 250);
frame.setVisible(true);
}
private static void createNodes(DefaultMutableTreeNode top, ProgressTreeCellRenderer renderer) {
JProgressBar progressBar1 = new JProgressBar(0, 100);
progressBar1.setValue(25);
progressBar1.setStringPainted(true);
DefaultMutableTreeNode one =
new DefaultMutableTreeNode(progressBar1);
top.add(one);
renderer.getNodeMap().put(one, new ProgressTreeCellRenderer2(progressBar1));
JProgressBar progressBar2 = new JProgressBar(0, 100);
progressBar2.setValue(25);
progressBar2.setStringPainted(true);
DefaultMutableTreeNode two =
new DefaultMutableTreeNode(progressBar2);
top.add(two);
renderer.getNodeMap().put(two, new ProgressTreeCellRenderer2(progressBar2));
}
}
@SuppressWarnings("serial")
class ProgressTreeCellRenderer extends DefaultTreeCellRenderer {
private HashMap<DefaultMutableTreeNode, ProgressTreeCellRenderer2> nodeMap = new HashMap<DefaultMutableTreeNode, ProgressTreeCellRenderer2>();
@Override
public Component getTreeCellRendererComponent(JTree tree, final Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) {
if(nodeMap.containsKey(value)) {
//I was expecting this to work, alas it does not?
setClosedIcon(new IconUIResource(new NodeIcon('+')));
setOpenIcon(new IconUIResource(new NodeIcon('-')));
return nodeMap.get(value).getTreeCellRendererComponent(tree, value, selected, expanded, leaf, row, hasFocus);
}
return super.getTreeCellRendererComponent(tree, value, selected, expanded, leaf, row, hasFocus);
}
public HashMap<DefaultMutableTreeNode, ProgressTreeCellRenderer2> getNodeMap() {
return nodeMap;
}
}
class ProgressTreeCellRenderer2 implements TreeCellRenderer {
private JProgressBar _progressBar;
private DefaultTreeCellRenderer _defaultRenderer;
public ProgressTreeCellRenderer2(JProgressBar progressBar) {
_progressBar = progressBar;
_defaultRenderer = new DefaultTreeCellRenderer();
}
@Override
public Component getTreeCellRendererComponent(JTree tree, final Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) {
Component render = _defaultRenderer.getTreeCellRendererComponent(tree, value, selected, expanded, leaf, row, hasFocus);
final JPanel panel = new JPanel();
BoxLayout layout = new BoxLayout(panel, BoxLayout.X_AXIS);
panel.setLayout(layout);
panel.add(_progressBar);
render = panel;
return render;
}
}
class NodeIcon implements Icon {
private static final int SIZE = 9;
private char type;
public NodeIcon(char type) {
this.type = type;
}
public void paintIcon(Component c, Graphics g, int x, int y) {
g.setColor(UIManager.getColor("Tree.background"));
g.fillRect(x, y, SIZE - 1, SIZE - 1);
g.setColor(UIManager.getColor("Tree.hash").darker());
g.drawRect(x, y, SIZE - 1, SIZE - 1);
g.setColor(UIManager.getColor("Tree.foreground"));
g.drawLine(x + 2, y + SIZE / 2, x + SIZE - 3, y + SIZE / 2);
if (type == '+') {
g.drawLine(x + SIZE / 2, y + 2, x + SIZE / 2, y + SIZE - 3);
}
}
public int getIconWidth() {
return SIZE;
}
public int getIconHeight() {
return SIZE;
}
}
In case of
ProgressTreeCellRenderercallingsetClosedIconorsetOpenIconhas no effect as you are returning another renderer that is retrieved from anodeMap. You'd have to add the icon in the implementation ofProgressTreeCellRenderer2. For example, try this to add an icon to the panel inProgressTreeCellRenderer2:However, note that your current implementation is very expensive. Not only you have a new renderer per node, but also, you reallocate the panel instance inside
getTreeCellRendererComponent(). Try to use a single renderer for all nodes with preallocated controls where you only update properties based on the arguments ofgetTreeCellRendererComponent. See How to Use Trees for renderer details and examples of a tree display customization.Take a look at this answer (by @mKorbel) that illustrates a single progress bar renderer in a table.