I am using a JTree
, which is using a DefaultTreeModel
. This tree model has some nodes inside, and when I click on a node, I get the information of the node and I change the background color to show that this node is selected.
It is possible to call the tree to clear the selection when I click on any place out of the tree? By clearing the selection I will be able to change the background color again, but I don't know how or where to use the clearSelection()
method of the tree when I click out of the tree.
Here is the code I am using:
The example:
import javax.swing.*;
import javax.swing.tree.*;
import java.awt.*;
public class JTreeSelectDeselect {
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
JPanel panel = new JPanel(new BorderLayout());
JTree tree = new JTree();
tree.setCellRenderer(new DeselectTreeCellRenderer());
panel.add(tree, BorderLayout.LINE_START);
panel.add(new JScrollPane(new JTextArea(10, 30)));
frame.add(panel);
frame.pack();
frame.setVisible(true);
}
}
class DeselectTreeCellRenderer extends DefaultTreeCellRenderer {
@Override
public Color getBackgroundSelectionColor() {
return new Color(86, 92, 160);
}
@Override
public Color getBackground() {
return (null);
}
@Override
public Color getBackgroundNonSelectionColor() {
return new Color(23, 27, 36);
}
@Override
public Component getTreeCellRendererComponent(JTree tree, Object value,
boolean sel, boolean exp, boolean leaf, int row, boolean hasFocus) {
super.getTreeCellRendererComponent(tree, value, sel, exp, leaf, row, hasFocus);
setForeground(new Color(225, 225, 221, 255));
setOpaque(false);
return this;
}
}
I am showing here how I create the nodes and add it to the tree using a tree model and how I set my custom TreeCellRenderer
.
In the cell renderer I paint the selected node with a specific color, and if the node is deselected, I paint it using another color. When I change the selection of the nodes, their background is painting correctly, but when I click outside the tree, the selected node is not deselected, so it is not painted with the specific color established in the cell renderer.
There is a way to deselect the node when I click outside the tree?
And just if someone knows, there is a way to change some of the leafs by check boxes from the TreeCellRenderer
? To have some children as labels and some others as check boxes. Because when I try to add check boxes it says (as I expected) that check boxes are not DefaultMutableTreeNode
objects and I can't add them to the tree model.
First of all you don't need to subclass the DefaultTreeCellRenderer if you just want to change some colors. You can create a new one, set the colors as you wish and set it to the tree. In the below code sample I've done this in the getDefaultTreeCellRenderer().
Your panel contains two elements the tree and the text area. To achieve what you needed I added a mouse listener and a focus listener to the tree:
We needed both listeners because the first one gets triggered only when you click on the tree and around it but not it the Text area and the second one gets triggered when you focus out of the tree area and focus on the text area.
import javax.swing.*;
import javax.swing.tree.DefaultTreeCellRenderer;
import java.awt.*;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
public class JTreeSelectDeselect {
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
JPanel panel = new JPanel(new BorderLayout());
JTree tree = new JTree();
tree.setCellRenderer(getDefaultTreeCellRenderer());
tree.addMouseListener(getMouseListener(tree));
tree.addFocusListener(getFocusListener(tree));
panel.add(tree, BorderLayout.LINE_START);
panel.add(new JScrollPane(new JTextArea(10, 30)));
frame.add(panel);
frame.pack();
frame.setVisible(true);
}
private static DefaultTreeCellRenderer getDefaultTreeCellRenderer() {
DefaultTreeCellRenderer defaultTreeCellRenderer = new DefaultTreeCellRenderer();
defaultTreeCellRenderer.setBackgroundSelectionColor(new Color(86, 92, 160));
defaultTreeCellRenderer.setBackgroundNonSelectionColor(new Color(135, 151, 53));
defaultTreeCellRenderer.setBackground(new Color(225, 225, 221, 255));
defaultTreeCellRenderer.setForeground(new Color(225, 225, 221, 255));
return defaultTreeCellRenderer;
}
private static FocusListener getFocusListener(final JTree tree) {
return new FocusListener() {
@Override
public void focusGained(FocusEvent e) {
}
@Override
public void focusLost(FocusEvent e) {
System.out.println("focus lost");
tree.clearSelection();
}
};
}
private static MouseListener getMouseListener(final JTree tree) {
return new MouseListener() {
@Override
public void mouseClicked(MouseEvent e) {
System.out.println("mouse clicked");
if(tree.getRowForLocation(e.getX(),e.getY()) == -1) {
System.out.println("clicked outside a specific cell");
tree.clearSelection();
}
}
@Override
public void mousePressed(MouseEvent e) {
}
@Override
public void mouseReleased(MouseEvent e) {
}
@Override
public void mouseEntered(MouseEvent e) {
}
@Override
public void mouseExited(MouseEvent e) {
}
};
}
}
If you want to deselect all selected nodes in the tree on mouse click somewhere else, you need to get notified when user clicks somewhere else. To get it you can use the global event listener (Toolkit.getDefaultToolkit().addAWTEventListener()
).
If you want to display some checkboxes in your tree you need a custom cell renderer, which for a condition returns a checkbox. Also you need a data container to hold whether the node is selected and finally you need a routine to update your data model, when the node is clicked. The last thing is usually provided by cell editors (more about in the article about editors and renderes), but in your case it's easier to do using the global mouse listener, that we've installed before.
Of course my example contains a little bit "Swing magic", so when you don't understand something, feel free to ask me about ;)
import java.awt.AWTEvent;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Toolkit;
import java.awt.event.AWTEventListener;
import java.awt.event.MouseEvent;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTree;
import javax.swing.SwingUtilities;
import javax.swing.WindowConstants;
import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreePath;
/**
* <code>JTreeDeselected</code>.
*/
public class JTreeSelectDeselect {
private final JTree tree = new JTree();
// model to hold nodes that must be presented as check boxes
// and whether the check boxes are selected
private final Map<Object, Boolean> checkMap = new HashMap<>();
// listener as method reference
private AWTEventListener awtListener = this::mouseClicked;
public static void main(String[] args) {
SwingUtilities.invokeLater(new JTreeSelectDeselect()::start);
}
private void start() {
checkMap.put("football", true);
checkMap.put("soccer", false);
checkMap.put("violet", true);
checkMap.put("yellow", false);
checkMap.put("pizza", true);
checkMap.put("ravioli", false);
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
JPanel panel = new JPanel(new BorderLayout());
// JTree tree = new JTree();
tree.setCellRenderer(new DeselectTreeCellRenderer(checkMap));
panel.add(new JScrollPane(tree), BorderLayout.LINE_START);
panel.add(new JScrollPane(new JTextArea(10, 30)));
frame.add(panel);
// register global listener
Toolkit.getDefaultToolkit().addAWTEventListener(awtListener, AWTEvent.MOUSE_EVENT_MASK);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
private void mouseClicked(AWTEvent evt) {
if (evt instanceof MouseEvent) {
MouseEvent me = (MouseEvent) evt;
if (me.getID() == MouseEvent.MOUSE_PRESSED) {
if (me.getComponent().equals(tree)) {
TreePath path = tree.getPathForLocation(me.getX(), me.getY());
if (path == null) {
tree.clearSelection();
} else {
// update check box value
String pathString = Objects.toString(path.getLastPathComponent(), "");
Boolean val = checkMap.get(pathString);
if (val != null) {
checkMap.put(pathString, !val);
((DefaultTreeModel) tree.getModel()).valueForPathChanged(path, pathString);
}
}
} else {
tree.clearSelection();
}
}
}
}
}
class DeselectTreeCellRenderer extends DefaultTreeCellRenderer {
private final JCheckBox checkBox = new JCheckBox();
private final Map<Object, Boolean> checkMap;
public DeselectTreeCellRenderer(Map<Object, Boolean> checkMap) {
this.checkMap = checkMap;
}
@Override
public Color getBackgroundSelectionColor() {
return new Color(86, 92, 160);
}
@Override
public Color getBackground() {
return (null);
}
@Override
public Color getBackgroundNonSelectionColor() {
return new Color(23, 27, 36);
}
@Override
public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean exp, boolean leaf, int row,
boolean hasFocus) {
super.getTreeCellRendererComponent(tree, value, sel, exp, leaf, row, hasFocus);
setForeground(new Color(225, 225, 221, 255));
setOpaque(false);
// if our "check model" contains an entry for the node,
// present the node as check box
if (checkMap.containsKey(Objects.toString(value))) {
checkBox.setOpaque(true);
checkBox.setSelected(Boolean.TRUE == checkMap.get(Objects.toString(value)));
checkBox.setBackground(sel ? getBackgroundSelectionColor() : getBackgroundNonSelectionColor());
checkBox.setForeground(getForeground());
checkBox.setFont(getFont());
checkBox.setBorder(getBorder());
checkBox.setText(getText());
return checkBox;
}
return this;
}
}
The Robot class in the Java AWT package is used to generate native system input events for the purposes of test automation, self-running demos, and other applications where control of the mouse and keyboard is needed. The primary purpose of Robot is to facilitate automated testing of Java platform implementations. In simple terms, the class provides control over the mouse and keyboard devices. In the below snippet "Robot" has been used to capture the event and clear selection from the tree.
import java.awt.AWTException;
import java.awt.Robot;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.tree.TreePath;
public class TreeDeselectionTest {
JTree createdTreeInstance = new JTree();
TreePath pathSelectionInstance;
Robot robotInstance;
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new TreeDeselectionTest().createTreeAndCaptureEvents();
}
});
}
public void createTreeAndCaptureEvents() {
try {
robotInstance = new Robot();
} catch (AWTException exceptionInstance) {
exceptionInstance.printStackTrace();
}
createdTreeInstance.setShowsRootHandles(false);
createdTreeInstance.addMouseListener(new MouseAdapter() {
@Override
public void mousePressed(MouseEvent eventMousePressInstance) {
if (robotInstance != null && pathSelectionInstance != null && eventMousePressInstance.getButton() == MouseEvent.BUTTON1) {
createdTreeInstance.clearSelection();
robotInstance.mousePress(InputEvent.BUTTON1_MASK);
robotInstance.mouseRelease(InputEvent.BUTTON1_MASK);
}
pathSelectionInstance = createdTreeInstance.getSelectionPath();
}
});
JFrame frameConetnds = new JFrame();
frameConetnds.setContentPane(new JScrollPane(createdTreeInstance));
frameConetnds.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frameConetnds.setSize(400, 400);
frameConetnds.setLocationRelativeTo(null);
frameConetnds.setVisible(true);
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With