I am customizing a JTree so some nodes have checkboxes, using santhosh tekuri's work as a base.
So the idea is writing a custom TreeCellRenderer, which in this case extends JPanel and implements TreeCellRenderer, and then at the method getTreeCellRendererComponent I have to decide for each node if it is receiving a checkbox or not.
I have seen some other examples for a typical JTree to customize each node icon, but they relay on converting each node to a JLabel and getting its text, while here it is a JPanel.
This is how my renderer looks like:
public class CheckTreeCellRenderer extends JPanel implements TreeCellRenderer{
private CheckTreeSelectionModel selectionModel;
private TreeCellRenderer delegate;
private TristateCheckBox checkBox;
protected CheckTreeManager.CheckBoxCustomizer checkBoxCustomer;
public CheckTreeCellRenderer(TreeCellRenderer delegate, CheckTreeSelectionModel selectionModel){
this.delegate = delegate;
this.selectionModel = selectionModel;
this.checkBox = new TristateCheckBox("");
this.checkBox.setOpaque(false);
setLayout(new BorderLayout());
setOpaque(false);
}
public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus){
Component renderer = delegate.getTreeCellRendererComponent(tree, value, selected, expanded, leaf, row, hasFocus);
// Inside this if clause, those cells which do not require checkbox will be returned
if({CODE_TO_GET_NODE_TEXT}.startsWith("A")){
return renderer;
}
TreePath path = tree.getPathForRow(row);
if(path != null){
if(checkBoxCustomer != null && !checkBoxCustomer.showCheckBox(path)){
return renderer;
}
if(selectionModel.isPathSelected(path, selectionModel.isDigged())){
checkBox.getTristateModel().setState(TristateState.SELECTED);
}
else{
checkBox.getTristateModel().setState(selectionModel.isDigged() && selectionModel.isPartiallySelected(path) ? TristateState.INDETERMINATE : TristateState.DESELECTED);
}
}
removeAll();
add(checkBox, BorderLayout.WEST);
add(renderer, BorderLayout.CENTER);
return this;
}
}
In this case I want to avoid setting a Tristate checkbox for those cells whose texts starts with "A" as an example. But I can't find a way to get the text from the value argument.
In case it helps, this is how I create the JTree:
DefaultMutableTreeNode root = new DefaultMutableTreeNode();
JTree tree = new JTree(root);
// Add nodes to the tree
DefaultMutableTreeNode friends_node = new DefaultMutableTreeNode("Friends");
friends_node.add(new DefaultMutableTreeNode("Anna"));
friends_node.add(new DefaultMutableTreeNode("Amador"));
friends_node.add(new DefaultMutableTreeNode("Jonas"));
friends_node.add(new DefaultMutableTreeNode("Mike"));
friends_node.add(new DefaultMutableTreeNode("Anthony"));
friends_node.add(new DefaultMutableTreeNode("Maya"));
friends_node.add(new DefaultMutableTreeNode("Pepe Vinyuela"));
root.add(friends_node);
tree.setCellRenderer(renderer = new CheckTreeCellRenderer(tree.getCellRenderer(), new CheckTreeSelectionModel(tree.getModel(), dig)));
Any idea?
If you're using String userObject = "Anna"; new DefaultMutableTreeNode(userObject);, then you might be able to use the DefaultMutableTreeNode#getUserObject() method:
#Override public Component getTreeCellRendererComponent(
JTree tree, Object value, boolean selected, boolean expanded,
boolean leaf, int row, boolean hasFocus) {
Component renderer = delegate.getTreeCellRendererComponent(
tree, value, selected, expanded, leaf, row, hasFocus);
if (value instanceof DefaultMutableTreeNode) {
Object userObject = ((DefaultMutableTreeNode) value).getUserObject();
// Inside this if clause, those cells which do not require checkbox will be returned
if(userObject instanceof String && ((String) userObject).startsWith("A")){
return renderer;
}
//...
Related
I have an application that displays a jTree. Each node in the tree has a boolean field called flagged which indicates whether it requires attention or not from the user.
If the field is true, then I would like it to be highlighted in red, otherwise no highlighting.
What is a good way to accomplish this? Should I extend DefaultTreeCellRenderer? Implement my own custom TreeCellRenderer? Some other method?
Since the custom rendering you want to do is pretty basic, I would just extend DefaultTreeCellRenderer and override its getTreeCellRendererComponent method. You could simply adjust the foreground color on the JLabel that the DefaultTreeCellRenderer uses. Here's a quick example:
tree.setCellRenderer(new DefaultTreeCellRenderer() {
#Override
public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded,
boolean leaf, int row, boolean hasFocus) {
JLabel label = (JLabel)super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus);
YourNode node = (YourNode)value;
if (node.isFlagged())
label.setForeground(Color.RED);
return label;
}
});
And the result:
I've written a custom TreeCellRenderer in order to change a components appearance. Everything works fine, except that setBackground has no effect. The code is definitely executed as the foreground color always changes correctly. Since selected items are rendered in blue and deselected item in white (without having written that code myself), I assume that my changes are overridden by JTree. So what would be the proper way to change the background color?
This is essentially my code:
JTree tree = new JTree();
tree.setCellRenderer(new MyCellRenderer());
///////
public class MyCellRenderer extends DefaultTreeCellRenderer{
#Override
public Component getTreeCellRendererComponent(JTree tree, Object value,
boolean isSelected, boolean expanded, boolean leaf, int row,
boolean hasFocus) {
JComponent c = (JComponent) super.getTreeCellRendererComponent(tree, value, isSelected, expanded, leaf, row, hasFocus);
DefaultMutableTreeNode node = (DefaultMutableTreeNode) value;
MyData data = (MyData)node.getUserObject();
if(data.isX()){
c.setForeground(Color.blue);
c.setBackground(Color.gray);
}
return c;
}
}
Try adding a call to c.setOpaque(true).
Please have a look at the SSCCE. How can I make the non-selected tree nodes' background transparent. At the moment the background of non-selected nodes is white. My cell renderer, however, should paint it non-opaque if it is not selected (and green when selected...what it does). In the end I want non-selected nodes to be just text without background, since the area which is red in the SSCCE has a gradient fill in my application.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTree;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeCellRenderer;
public class SimpleTree extends JFrame
{
public static void main(final String[] args)
{
new SimpleTree();
}
public SimpleTree()
{
super("Creating a Simple JTree");
final Container content = this.getContentPane();
content.setBackground(Color.RED);
final Object[] hierarchy = { "javax.swing", "javax.swing.border", "javax.swing.colorchooser", "javax.swing.event", "javax.swing.filechooser", new Object[] { "javax.swing.plaf", "javax.swing.plaf.basic", "javax.swing.plaf.metal", "javax.swing.plaf.multi" }, "javax.swing.table",
new Object[] { "javax.swing.text", new Object[] { "javax.swing.text.html", "javax.swing.text.html.parser" }, "javax.swing.text.rtf" }, "javax.swing.tree", "javax.swing.undo" };
final DefaultMutableTreeNode root = this.processHierarchy(hierarchy);
final JTree tree = new JTree(root);
tree.setOpaque(false);
tree.setCellRenderer(new MyCellRenderer());
final JScrollPane scroller = new JScrollPane(tree);
scroller.getViewport().setOpaque(false);
scroller.setOpaque(false);
content.add(scroller, BorderLayout.CENTER);
this.setSize(275, 300);
this.setVisible(true);
}
/**
* Small routine that will make node out of the first entry in the array,
* then make nodes out of subsequent entries and make them child nodes of
* the first one. The process is repeated recursively for entries that are
* arrays.
*/
private DefaultMutableTreeNode processHierarchy(final Object[] hierarchy)
{
final DefaultMutableTreeNode node = new DefaultMutableTreeNode(hierarchy[0]);
DefaultMutableTreeNode child;
for (int i = 1; i < hierarchy.length; i++)
{
final Object nodeSpecifier = hierarchy[i];
if (nodeSpecifier instanceof Object[]) // Ie node with children
child = this.processHierarchy((Object[]) nodeSpecifier);
else
child = new DefaultMutableTreeNode(nodeSpecifier); // Ie Leaf
node.add(child);
}
return (node);
}
public class MyCellRenderer extends DefaultTreeCellRenderer
{
#Override
public Component getTreeCellRendererComponent(final JTree tree, final Object value, final boolean sel, final boolean expanded, final boolean leaf, final int row, final boolean hasFocus)
{
final Component ret = super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus);
final DefaultMutableTreeNode node = ((DefaultMutableTreeNode) (value));
this.setText(value.toString());
if (sel)
{
this.setOpaque(true);
this.setBackground(Color.GREEN);
}
else
{
this.setOpaque(false);
this.setBackground(null);
}
return ret;
}
}
}
You should override getBackgroundNonSelectionColor,getBackgroundSelectionColor and getBackground of DefaultTreeCellRenderer and return appropriate values like so:
public class MyCellRenderer extends DefaultTreeCellRenderer {
#Override
public Color getBackgroundNonSelectionColor() {
return (null);
}
#Override
public Color getBackgroundSelectionColor() {
return Color.GREEN;
}
#Override
public Color getBackground() {
return (null);
}
#Override
public Component getTreeCellRendererComponent(final JTree tree, final Object value, final boolean sel, final boolean expanded, final boolean leaf, final int row, final boolean hasFocus) {
final Component ret = super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus);
final DefaultMutableTreeNode node = ((DefaultMutableTreeNode) (value));
this.setText(value.toString());
return ret;
}
}
which will produce:
Other suggestions:
Create and manipulate Swing components on Event Dispatch Thread.
Dont extend JFrame unnecessarily rather create an instance and use that.
Dont call setSize on JFrame rather use a correct LayoutManager and/or override getPreferredSize() and call pack() on JFrame before setting it visible but after adding all components.
Remember to call JFrame#setDefaultCloseOperation with either DISPOSE_ON_CLOSE or EXIT_ON_CLOSE (DISPOSE_XXX is usually preferred unless using Timers as this will allow main(String[] args) to continue its execution after Gui has been closed).
To avoid background refilling, just put UIManager.put("Tree.rendererFillBackground", false); before new SimpleTree(); or after super("Creating a Simple JTree");.
I have a JTree and nodes of it are driven from DefaultMutableTreeNode. Each node can be verify or not.At first the icon of all nodes are the same but, I am going to change the ICON of the verified nodes when I select them and press the verify button.I want to have the ability to click and write on each node so I can not use JLabel to show icons.
I made the following code but it returns NULLException.
class CustomIconRenderer extends DefaultTreeCellRenderer {
ImageIcon defaultIcon;
ImageIcon specialIcon;
ImageIcon closeIcon;
static DefaultTreeModel model;
static myDefaultMutableTreeNode root;
public CustomIconRenderer()
{
openIcon = new ImageIcon(CustomIconRenderer.class.getResource("icons/question.png"));
closeIcon = new ImageIcon(CustomIconRenderer.class.getResource("icons/Target-New-Logo.jpg"));
setLeafIcon(closeIcon);
}
#Override
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);
Object nodeObj = ((DefaultMutableTreeNode)value).getUserObject();
Check_each_nodes_are_verified_change_the_icon();
return this;
}
}
public class myDefaultMutableTreeNode extends DefaultMutableTreeNode{
private static int id=0;
private int nodeid;
private int verify;
private int depth;
}
Millions Thanks.
The DefaultTreeCellRenderer has setters, allowing to set open icon, closed icon and leaf icon. Inside the overridden getTreeCellRendererComponent, set these icons in your derived renderer class how you want and then return that is returned by super.getTreeCellRendererComponent. As you set for every node before you render, you can easily have some different icon for the particular node.
I have a created a following renderer which renders the JTree with checkboxes and I want to add different color and icon to different nodes. How do I do it? Please help me. Thank you in advance.
class CheckTreeCellRenderer extends JPanel implements TreeCellRenderer {
private CheckTreeSelectionModel selectionModel;
private TreeCellRenderer delegate;
private TristateCheckBox checkBox = new TristateCheckBox("",null,true);
public static final State NOT_SELECTED = new State();
public static final State SELECTED = new State();
public static final State DONT_CARE = new State();
public CheckTreeCellRenderer(TreeCellRenderer delegate, CheckTreeSelectionModel selectionModel) {
this.delegate = delegate;
this.selectionModel = selectionModel;
setLayout(new BorderLayout());
setOpaque(false);
checkBox.setState(Boolean.TRUE);
revalidate();
checkBox.setOpaque(false);
}
public Component getTreeCellRendererComponent
(JTree tree, Object value, boolean selected, boolean expanded,
boolean leaf, int row, boolean hasFocus) {
Component renderer = delegate.getTreeCellRendererComponent
(tree, value, selected, expanded, leaf, row, hasFocus);
TreePath path = tree.getPathForRow(row);
if(path!=null){
if(selectionModel.isPathSelected(path, true)) {
checkBox.setState(Boolean.TRUE);
}
else {
checkBox.setState
(selectionModel.isPartiallySelected(path) ? null : Boolean.FALSE);
}
}
setBackground(Color.pink);
removeAll();
add(checkBox, BorderLayout.WEST);
add(renderer, BorderLayout.CENTER);
return this;
}
}
The best place to learn about TreeCellRenderers is from the tutorial (at the bottom of the page).
Instead of adding renderer to BorderLayout.CENTER, you can just add a different icon of whatever color you like.
In order for your setBackground(Color.PINK) to have any visible effect, you should change the setOpaque(false) to setOpaque(true) in your constructor. That said, I second #John's suggestion that you read up on renderers in the Sun tutorials.