I'm trying to visualize a JTree node as a JButton, I already tried creating a DefaultMutableTreeNode with a Jbutton object and the added it to the JTree:
JButton button = new JButton("Thing");
DefaultMutableTreeNode root = new DefaultMutableTreeNode(button);
JTree alberoClassi = new JTree(root);
These only show an error line with black parameters.
Searching the net I only found that I need a TreeRendererCell class, but none of the results show how to use it.
Are there some tutorials where I can find some examples to accomplish the task?
Finally I managed to solve the problem using an external class which implements the javax.swing.tree.TreeCellRenderer interface.
public class ClassCellRenderer implements javax.swing.tree.TreeCellRenderer {
JLabel className = new JLabel(" ");
JButton renderer = new JButton();
DefaultTreeCellRenderer defaultRenderer = new DefaultTreeCellRenderer();
public ClassCellRenderer() {
renderer.add(className);
}
#Override
public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) {
Component returnValue = null;
if ((value != null) && (value instanceof DefaultMutableTreeNode)) {
Object userObject = ((DefaultMutableTreeNode) value).getUserObject();
if (userObject instanceof String) {
String e = (String) userObject;
className.setText(e);
}
returnValue = renderer;
}
if (returnValue == null) {
returnValue = defaultRenderer.getTreeCellRendererComponent(tree, value, selected, expanded,
leaf, row, hasFocus);
}
return returnValue;
}
}
The JLabel and JButton could be changed with any other Component needed.
To add the renderer to the Tree, I used these lines:
alberoClassi = new JTree(root);
TreeCellRenderer renderer = new ClassCellRenderer();
alberoClassi.setCellRenderer(renderer); //aggiunta del renderer per i nodi
Related
I have a JTree, based on several custom classes. I want to give several Nodes a specific icon. Therefore i did the following code based on this link: Dynamically change icon of specific nodes in JTree
DefaultTreeCellRenderer renderer = new DefaultTreeCellRenderer() {
private static final long serialVersionUID = 1L;
private Icon good = new ImageIcon(getClass().getResource("/good.png"));
private Icon dunno = new ImageIcon(getClass().getResource("/dunno.png"));
private Icon bad = new ImageIcon(getClass().getResource("/bad.png"));
#Override
public Component getTreeCellRendererComponent(JTree tree,
Object value, boolean selected, boolean expanded,
boolean isLeaf, int row, boolean focused) {
Component c = super.getTreeCellRendererComponent(tree, value, selected, expanded, isLeaf, row, focused);
// JTreePanelNode node = (JTreePanelNode)c; - not possible
return c;
}
};
this.getTree().setCellRenderer(renderer);
Each of my Nodes is an object of JTreePanelNode (custom class), which saves a specific state which can be set via setState(String s) and get via getState(). So what i want is to something like this:
if(node.getState().equals("good")) ..
else if(node.getState.equals("bad")) ..
else ..
How can i achieve something like that? From what i understand the renderer goes through every node with getTreeCellRendererComponent and applies a specific icon which i can choose with setIcon and several ifs(). However i cannot cast to JTreePanelNode. Any solution? Thanks :)
All depends on your JTreePanelNode Class. if it implements the TreeNode interface you are good to go with:
public Component getTreeCellRendererComponent(JTree tree, Object value,
boolean sel, boolean expanded, boolean leaf, int row,
boolean hasFocus) {
Component comp = super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf,
row, hasFocus);
TreeNode current = (TreeNode)value;
if (leaf) {
//set leaf icon
} else if (expanded) {
//set expanded icon
} else {
// set default state
}
if (hasFocus)
//set what it looks like if focused
else if (selected)
//set what it looks like if selected
else
//set default l&f
comp.setIcon(whatevericonset in above conditions);
return comp;
}
Of course you can add many more states depending on your node class.getState().
you can get access to the Object represented in that tree:
#Override
public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected, boolean expanded, boolean isLeaf, int row, boolean focused) {
Component c = super.getTreeCellRendererComponent(tree, value, selected, expanded, isLeaf, row, focused);
DefaultMutableTreeNode node = (DefaultMutableTreeNode ) value;
Object representedObject = node.getUserObject();
JLabel superLabel = super.getTreeCellRendererComponent();
String text = label.getText();
if(object.isGood() ){ //i don't know your code
superLabel .setText (text +"goooooooood");
}else{
superLabel .setText (text +"badbadbabd");
}
return superLabel ;
}
I want to set for each node in my JTree a different icon, actually I'm loading each node from a data base, with a "while", I set each icon like a root, leaf or parent. Like this:
All my declarations are global:
private ResultSet myResultSet;
protected DefaultTreeModel treeModel;
private DefaultMutableTreeNode rootNode,childNode,parent1,parent2;
And this is the code where I set my nodes:
myResultSet=rtnNodes(); /*Method that returns a RS with my nodes*/
while(myResultSet.next()){
switch(myResultSet.getInt(1)){ /*The first column is the type of node: root, parent, leaf...*/
case 0: treeModel = new DefaultTreeModel((rootNode=new DefaultMutableTreeNode(myResultSet.getString(2)))); break; /*root node*/
case 1: case 4: parent1 = parent2 = makeNode(rootNode); break; /*parent node*/
case 2: makeNode(parent2); break; /*leaf node*/
case 3: parent2 = makeNode(parent1); break; /*sub patern node*/
} /*makeNode is the method where I create the nodes*/
}
The method makeNode is this:
public DefaultMutableTreeNode makeNode(DefaultMutableTreeNode parent){
//The second column in the RS is the name of the node
treeModel.insertNodeInto((childNode=new DefaultMutableTreeNode(myResultSet.getString(2))),parent,parent.getChildCount());
return childNode;
}
After to fill the treemodel with my nodes, I set the model to my JTree:
myJTree.setModel(treeModel);
myJTree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
But the problem is. when I try to set the icons. I create a subclass called myTreeRenderer, and I use this:
myJTree.setCellRenderer(new treeRenderer());
But it doesn't set the icons as I want, the subclass is:
private ImageIcon root,parent,leaf;
public myTreeRenderer() {
root=setIcons(2); /*setIcons is a method that I dont publish in this post, that helps me to set the path of the icons*/
parent=setIcons(3);
leaf=setIcons(4);
}
#Override
public Component getTreeCellRendererComponent(JTree tree,Object value,boolean selected,boolean expanded,boolean leaf,int row,boolean hasFocus){
super.getTreeCellRendererComponent(tree,value,selected,expanded,leaf,row,hasFocus);
DefaultMutableTreeNode nodo = (DefaultMutableTreeNode)value;
TreeNode t = nodo.getParent();
if(t!=null){
setIcon(root);
}
return this;
}
How I can set the icon for each node without using his name? The code of the subclass, as is, set all the nodes with the same icon, and each time I selected a node in the jtree, the getTreeCellRendererComponent runs, I donĀ“t want this.
You can change default UI values for icons of JTree nodes without any custom renderer:
URL resource = logaff.class.getResource(IMAGE);
Icon icon = new ImageIcon(resource);
UIManager.put("Tree.closedIcon", icon);
UIManager.put("Tree.openIcon", icon);
UIManager.put("Tree.leafIcon", icon);
or use something like next:
#Override
public Component getTreeCellRendererComponent(JTree tree,
Object value, boolean selected, boolean expanded,
boolean leaf, int row, boolean hasFocus) {
super.getTreeCellRendererComponent(tree, value, selected,expanded, leaf, row, hasFocus);
DefaultMutableTreeNode nodo = (DefaultMutableTreeNode) value;
if (tree.getModel().getRoot().equals(nodo)) {
setIcon(root);
} else if (nodo.getChildCount() > 0) {
setIcon(parent);
} else {
setIcon(leaf);
}
return this;
}
Also read about rendering mechanism.
You can use it, a shorter way. "tree" is my JTree component.
DefaultTreeCellRenderer renderer = (DefaultTreeCellRenderer) tree.getCellRenderer();
Icon closedIcon = new ImageIcon("closed.png");
Icon openIcon = new ImageIcon("open.png");
Icon leafIcon = new ImageIcon("leaf.png");
renderer.setClosedIcon(closedIcon);
renderer.setOpenIcon(openIcon);
renderer.setLeafIcon(leafIcon);
I'm trying to create a JTree in which some nodes are compound objects containing a JLabel and a JButton. The Node is representing a server and port shown by the JLabel, the JButton will use the Desktop API to open the default browser and go to the URL.
I have read the following already and have followed them as closely as I can. The Node is displayed how I want it (mostly - I can deal with making it nicer later) but when I try to click on the button the JTree is responding to the events, not the button.
java swing: add custom graphical button to JTree item
http://www.java2s.com/Code/Java/Swing-JFC/TreeCellRenderer.htm
https://stackoverflow.com/a/3769158/1344282
I need to know how to allow the events to pass through the JTree so that they are handled by the object(s) underneath - the JButton or JLabel.
Here is my TreeCellEditor:
public class UrlValidationCellEditor extends DefaultTreeCellEditor
{
public UrlValidationCellEditor(JTree tree, DefaultTreeCellRenderer renderer)
{
super(tree, renderer);
}
#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);
}
#Override
public boolean isCellEditable(EventObject anEvent)
{
return true; // Or make this conditional depending on the node
}
}
Here is the TreeCellRenderer:
public class UrlValidationRenderer extends DefaultTreeCellRenderer implements TreeCellRenderer
{
JLabel titleLabel;
UrlGoButton goButton;
JPanel renderer;
DefaultTreeCellRenderer defaultRenderer = new DefaultTreeCellRenderer();
public UrlValidationRenderer()
{
renderer = new JPanel(new GridLayout(1, 2));
titleLabel = new JLabel(" ");
titleLabel.setForeground(Color.blue);
renderer.add(titleLabel);
goButton = new UrlGoButton();
renderer.add(goButton);
renderer.setBorder(BorderFactory.createLineBorder(Color.black));
backgroundSelectionColor = defaultRenderer
.getBackgroundSelectionColor();
backgroundNonSelectionColor = defaultRenderer
.getBackgroundNonSelectionColor();
}
#Override
public Component getTreeCellRendererComponent(JTree tree, Object value,
boolean selected, boolean expanded, boolean leaf, int row,
boolean hasFocus)
{
Component returnValue = null;
if ((value != null) && (value instanceof DefaultMutableTreeNode))
{
Object userObject = ((DefaultMutableTreeNode) value)
.getUserObject();
if (userObject instanceof UrlValidation)
{
UrlValidation validationResult = (UrlValidation) userObject;
titleLabel.setText(validationResult.getServer()+":"+validationResult.getPort());
goButton.setUrl(validationResult.getUrl());
if (selected) {
renderer.setBackground(backgroundSelectionColor);
} else {
renderer.setBackground(backgroundNonSelectionColor);
}
renderer.setEnabled(tree.isEnabled());
returnValue = renderer;
}
}
if (returnValue == null)
{
returnValue = defaultRenderer.getTreeCellRendererComponent(tree,
value, selected, expanded, leaf, row, hasFocus);
}
return returnValue;
}
}
I would appreciate any insight or suggestions. Thanks!
The renderers do not work this way. They are used as rubber stamps, which means that there is really only one instance of renderer that is painted all over the place as the JList is painted. So it cannot handle mouse inputs, as the objects are not really there - they are just painted.
In order to pass mouse events to objects underneath, you need to implement a cell editor. Sometimes, the editor looks different than the renderer (String renderers are labels, editors are textfields, for example). Following this logic, the editor must be implemented using another instance of a component.
Now you are going to render buttons and use them for manipulating (ie. editing). The editor then must be another instance of JButton, distinctive from the renderer. Implementing button as renderer is easy, but the tricky part is to implement is as an editor. You need to extend AbstractCellEditor and implement TreeCellEditor and ActionListener. The button is then a field of the editor class. In the constructor of the editor class, you initialize the button and add this as a new action listener for the button. In the getTreeCellEditorComponent method, you just return the button. In the actionPerformed, you call whatever code you need to do on button press and then call stopCellEditing().
This way it works for me.
I made a SSCCE that demonstrates the usage on a String Tree
public class Start
{
public static class ButtonCellEditor extends AbstractCellEditor implements TreeCellEditor, ActionListener, MouseListener
{
private JButton button;
private JLabel label;
private JPanel panel;
private Object value;
public ButtonCellEditor(){
panel = new JPanel(new BorderLayout());
button = new JButton("Press me!");
button.addActionListener(this);
label = new JLabel();
label.addMouseListener(this);
panel.add(button, BorderLayout.EAST);
panel.add(label);
}
#Override public Object getCellEditorValue(){
return value.toString();
}
#Override public void actionPerformed(ActionEvent e){
String val = value.toString();
System.out.println("Pressed: " + val);
stopCellEditing();
}
#Override public Component getTreeCellEditorComponent(JTree tree, Object value, boolean isSelected, boolean expanded, boolean leaf, int row){
this.value = value;
label.setText(value.toString());
return panel;
}
#Override public void mouseClicked(MouseEvent e){
}
#Override public void mousePressed(MouseEvent e){
String val = value.toString();
System.out.println("Clicked: " + val);
stopCellEditing();
}
#Override public void mouseReleased(MouseEvent e){
}
#Override public void mouseEntered(MouseEvent e){
}
#Override public void mouseExited(MouseEvent e){
}
}
public static class ButtonCellRenderer extends JPanel implements TreeCellRenderer
{
JButton button;
JLabel label;
ButtonCellRenderer(){
super(new BorderLayout());
button = new JButton("Press me!");
label = new JLabel();
add(button, BorderLayout.EAST);
add(label);
}
#Override public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus){
label.setText(value.toString());
return this;
}
}
public static void main(String[] args){
JTree tree = new JTree();
tree.setEditable(true);
tree.setCellRenderer(new ButtonCellRenderer());
tree.setCellEditor(new ButtonCellEditor());
JFrame test = new JFrame();
test.add(new JScrollPane(tree));
test.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
test.setSize(500, 500);
test.setLocationRelativeTo(null);
test.setVisible(true);
}
}
the node should have 2 parts a label and a button. When the user clicks the label then some detailed information about the node should appear in a different part of the GUI. When the user clicks the button it should result in a browser window opening. ..
Don't do it that way. Instead, have just the label in the tree. Add the button to the same GUI that displays the 'detailed information about the node'.
I rendered a check box node tree.
The renderer renders the parent nodes with a (check box + folder like icon ) and the leaf nodes as (Only check box) .
I have rendered it and now i want to make it editable . (i.e) when i click it , the check boxes must be checked and unchecked .
I tried writing an editor . But i am not clear as to how to write it . Please guide me as to how to accomplish this .
Many thanks in advance .
I have built the tree from a Vector . The vector is called NamedVector and it contains Parent node objects . The parent node object holds the leaf nodes . The leaf nodes are of type CheckBoxNode.
public class CheckBoxNodeRenderer implements TreeCellRenderer{
NonLeafRenderer nonLeafRenderer = new NonLeafRenderer();
protected JCheckBox check;
protected JLabel label;
public JPanel panel;
CheckBoxNode checkNode;
public JCheckBox getLeafRenderer()
{
return leafRenderer;
}
public CheckBoxNodeRenderer()
{
panel = new JPanel();
panel.setLayout(new BorderLayout());
check = new JCheckBox();
label = new JLabel();
Font fontValue;
fontValue = UIManager.getFont("Tree.font");
if (fontValue != null) {
leafRenderer.setFont(fontValue);
}
Boolean booleanValue = (Boolean) UIManager
.get("Tree.drawsFocusBorderAroundIcon");
leafRenderer.setFocusPainted((booleanValue != null)
&& (booleanValue.booleanValue()));
selectionBorderColor = UIManager.getColor("Tree.selectionBorderColor");
selectionForeground = UIManager.getColor("Tree.selectionForeground");
selectionBackground = UIManager.getColor("Tree.selectionBackground");
textForeground = UIManager.getColor("Tree.textForeground");
textBackground = UIManager.getColor("Tree.textBackground");
}
///////////////////
/**
* Approach by returning a panel .
*/
public Component getTreeCellRendererComponent(JTree tree, Object value,
boolean isSelected, boolean expanded,
boolean leaf, int row, boolean hasFocus) {
String stringValue = tree.convertValueToText(value, isSelected,
expanded, leaf, row, hasFocus);
panel.setEnabled(true);
if(leaf){
if ((value != null) && (value instanceof DefaultMutableTreeNode)) {
DefaultMutableTreeNode node = (DefaultMutableTreeNode)value;
checkNode = (CheckBoxNode)node.getUserObject();
check.setSelected(checkNode.isSelected());
label.setFont(tree.getFont());
label.setText(value.toString());
label.setIcon(null);
panel.removeAll();
panel.add(check,BorderLayout.WEST);
panel.add(label);
panel.setVisible(true);
}
}
else if(!leaf){
if ((value != null) && (value instanceof DefaultMutableTreeNode) ) {
DefaultMutableTreeNode node = (DefaultMutableTreeNode)value;
Object parent = (Object)node.getUserObject();
System.err.println(parent.toString());
NamedVector parentNode = (NamedVector) parent;
check.setSelected(parentNode.isSelected());
label.setFont(tree.getFont());
label.setText(parentNode.toString());
label.setIcon(UIManager.getIcon("Tree.openIcon"));
panel.removeAll();
panel.add(check,BorderLayout.WEST);
panel.add(label);
panel.setVisible(true);
}
}
return panel;
}
The issue is that you're not listening for the events. In your getTreeCellRendererComponent method, create a listener and then make sure to tell the tree model that you've changed a node via the nodeChanged method. The following should work (you might have to make some variables final to use them in the inner class though):
check.addMouseListener(new MouseAdapter() {
public void mouseClicked(MouseEvent ev) {
checkNode.setSelected(check.isSelected());
DefaultTreeModel dtm = (DefaultTreeModel)tree.getModel();
dtm.nodeChanged(node);
}
});
I am using this below piece of code:
class CountryTreeCellRenderer implements TreeCellRenderer {
private JLabel label;
CountryTreeCellRenderer() {
label = new JLabel();
}
public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) {
Object o = ((DefaultMutableTreeNode) value).getUserObject();
if (o instanceof Country) {
Country country = (Country) o;
label.setIcon(new ImageIcon(country.getFlagIcon()));
label.setText(country.getName());
} else {
label.setIcon(null);
label.setText("" + value);
}
return label;
}
}
Since I am passing/returning a label, so on selecting any component in the JTree, no selection color is coming.
I tried to use:
JComponent comp = (JComponent) super.getTreeCellRendererComponent(tree, value, selected, expanded, leaf, row, hasFocus);
comp.setOpaque(true);
if(selected)
comp.setBackground(Color.RED);
But if I return comp, then the output of the tree is not coming as expected.
How to resolve the same?
I did not implement any Editor for the same.
Please take a look at the source code of the DefaultTreeCellRenderer, which extends JLabel as well and is perfectly capable of setting a background color. I copy-pasted the relevant lines below:
if (selected)
{
super.setBackground(getBackgroundSelectionColor());
setForeground(getTextSelectionColor());
if (hasFocus)
setBorderSelectionColor(UIManager.getLookAndFeelDefaults().
getColor("Tree.selectionBorderColor"));
else
setBorderSelectionColor(null);
}
else
{
super.setBackground(getBackgroundNonSelectionColor());
setForeground(getTextNonSelectionColor());
setBorderSelectionColor(null);
}
Yes it worked as explained by Robin by bit of change basically
if(selected){
label.setBackground(Color.YELLOW);
label.setForeground(Color.GREEN);
}else
{
label.setBackground(Color.WHITE);
label.setForeground(Color.BLACK);
//setBorderSelectionColor(null);
}
enough