JCheckBoxTree 初始状态问题
Posted
技术标签:
【中文标题】JCheckBoxTree 初始状态问题【英文标题】:JCheckBoxTree initial state issue 【发布时间】:2020-03-27 20:15:13 【问题描述】:我在使用一个自定义的JTree,实现了拖放,单元格渲染器是一个Jcheckbox
问题是我的复选框状态与我的 jtree 选择状态不同,因为我正在更新单元格渲染器中的复选框选择状态
@Override
public TreeCellRenderer getCellRenderer()
return (tree, value, selected, expanded, leaf, row, hasFocus) ->
selected = GridManager.getInstance().getDisplayableSources().containsKey(value+"");
final String key = value + "";
final JCheckBoxMenuItem jCheckBoxMenuItem = new JCheckBoxMenuItem(key);
jCheckBoxMenuItem.setSelected(selected);
return jCheckBoxMenuItem;
;
我很清楚,selected
lambda 参数反映了单击事件触发(触发)的某种内部选择状态,这解释了我的拖放错误行为
那么如何更新 JTree 选择以便我可以像这样盲目地使用我的渲染器
@Override
public TreeCellRenderer getCellRenderer()
return (tree, value, selected, expanded, leaf, row, hasFocus) ->
final String key = value + "";
final JCheckBoxMenuItem jCheckBoxMenuItem = new JCheckBoxMenuItem(key);
jCheckBoxMenuItem.setSelected(selected);
return jCheckBoxMenuItem;
;
这是我的完整实现
import java.util.HashMap;
import javax.swing.DropMode;
import javax.swing.JCheckBoxMenuItem;
import javax.swing.JTree;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeCellRenderer;
import javax.swing.tree.TreePath;
import com.wanasis.vlc.control.GridManager;
import com.wanasis.vlc.domain.SourceNode;
public class JCheckBoxDragableTree extends JTree
/**
*
*/
private static final long serialVersionUID = -7768978192373533316L;
private HashMap<String, SourceNode> configurableSources;
private static HashMap<String, String> displaySources = new HashMap<>();
public JCheckBoxDragableTree(HashMap<String, SourceNode> configurableSources,
HashMap<String, String> displaySources)
this.configurableSources = configurableSources;
this.displaySources = displaySources;
final DefaultTreeModel dtm = new DefaultTreeModel(new DefaultMutableTreeNode());
final DefaultMutableTreeNode root = (DefaultMutableTreeNode) dtm.getRoot();
configurableSources.values().stream().filter(s ->
return s.getParent().isEmpty();
).forEachOrdered(child ->
dtm.insertNodeInto(new DefaultMutableTreeNode(child.getId()), root, root.getChildCount());
);
setModel(dtm);
setDragEnabled(true);
setDropMode(DropMode.ON);
setTransferHandler(new TreeTransferHandler());
setSelectionModel(new SomativeSelectionModel());
addTreeSelectionListener(new TreeSelectionListener()
@Override
public void valueChanged(TreeSelectionEvent e)
String key = ""+e.getPath().getLastPathComponent();
DefaultMutableTreeNode dmtn = (DefaultMutableTreeNode) e.getPath().getLastPathComponent();
System.out.println(dmtn.getUserObject());
System.out.println(key);
GridManager gm = GridManager.getInstance();
boolean isDisplayed = gm.getDisplayableSources().containsKey(key);
if (isDisplayed)
System.out.println("hiding selected element");
gm.getDisplayableSources().remove(key);
System.out.println(gm.getDisplayableSources().keySet());
else
System.out.println("showing selected element");
gm.getDisplayableSources().put(key, gm.getConfigurableSources().get(key).getSource());
System.out.println(gm.getDisplayableSources().keySet());
gm.prepareDisplay();
);
@Override
public void setSelectionPath(TreePath path)
addSelectionPath(path);
return;
@Override
public void setSelectionPaths(TreePath[] paths)
addSelectionPaths(paths);
return;
@Override
public void setSelectionRow(int row)
addSelectionRow(row);
return;
@Override
public void setSelectionRows(int[] rows)
addSelectionRows(rows);
return;
@Override
public TreeCellRenderer getCellRenderer()
return (tree, value, selected, expanded, leaf, row, hasFocus) ->
selected = GridManager.getInstance().getDisplayableSources().containsKey(value+"");
final String key = value + "";
final JCheckBoxMenuItem jCheckBoxMenuItem = new JCheckBoxMenuItem(key);
jCheckBoxMenuItem.setSelected(selected);
return jCheckBoxMenuItem;
;
【问题讨论】:
【参考方案1】:在我的例子中,我更新了我的所有代码,下面的 jtree 是拖放的,带有复选框作为单元格渲染器
我还在 D&D 和复选框选择事件中附加了一些事件逻辑,您所要做的就是根据需要更新它
希望这会有所帮助
import java.awt.Color;
import java.awt.Component;
import java.awt.Font;
import java.awt.Insets;
import java.awt.Point;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.awt.dnd.DnDConstants;
import java.awt.dnd.DragGestureEvent;
import java.awt.dnd.DragGestureListener;
import java.awt.dnd.DragGestureRecognizer;
import java.awt.dnd.DragSource;
import java.awt.dnd.DragSourceDragEvent;
import java.awt.dnd.DragSourceDropEvent;
import java.awt.dnd.DragSourceEvent;
import java.awt.dnd.DragSourceListener;
import java.awt.dnd.DropTarget;
import java.awt.dnd.DropTargetContext;
import java.awt.dnd.DropTargetDragEvent;
import java.awt.dnd.DropTargetDropEvent;
import java.awt.dnd.DropTargetEvent;
import java.awt.dnd.DropTargetListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.MouseEvent;
import java.io.IOException;
import java.util.EventObject;
import java.util.HashMap;
import javax.swing.AbstractCellEditor;
import javax.swing.Icon;
import javax.swing.JCheckBox;
import javax.swing.JTree;
import javax.swing.UIManager;
import javax.swing.border.LineBorder;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeCellEditor;
import javax.swing.tree.TreeCellRenderer;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;
import com.wanasis.vlc.control.GridManager;
import com.wanasis.vlc.domain.SourceNode;
public class JCheckBoxDragableTree extends JTree
/**
*
*/
private static final long serialVersionUID = -776897819237333316L;
private HashMap<String, SourceNode> configurableSources;
private static HashMap<String, String> displaySources = new HashMap<>();
private static DefaultMutableTreeNode root = new DefaultMutableTreeNode("wanaDVR");
TreeDragSource ds;
TreeDropTarget dt;
public JCheckBoxDragableTree(HashMap<String, SourceNode> configurableSources, HashMap<String, String> displaySources)
super(root);
System.out.println("JCheckBoxDragableTree");
setupTreeElements(configurableSources);
this.configurableSources = configurableSources;
this.displaySources = displaySources;
setCellRenderer(new CheckBoxTreeNodeRenderer());
setCellEditor(new CheckBoxTreeNodeEditor(this));
setEditable(true);
ds = new TreeDragSource(this, DnDConstants.ACTION_COPY_OR_MOVE);
dt = new TreeDropTarget(this);
addTreeSelectionListener(new TreeSelectionListener()
@Override
public void valueChanged(TreeSelectionEvent e)
GridManager gm = GridManager.getInstance();
String key = "" + e.getPath().getLastPathComponent();
DefaultMutableTreeNode dmtn = (DefaultMutableTreeNode) e.getPath().getLastPathComponent();
System.out.println(dmtn.getUserObject());
System.out.println(key);
);
public void setupTreeElements(HashMap<String, SourceNode> configurableSources)
final HashMap<String, DefaultMutableTreeNode> parents = new HashMap<>();
final HashMap<String, SourceNode> reste = new HashMap<>();
configurableSources.values().stream().filter(s ->
return s.getParent().isEmpty()||s.getParent().trim().toLowerCase().equals("wanadvr");
).forEachOrdered(child ->
String key = child.getId();
DefaultMutableTreeNode newChild = new DefaultMutableTreeNode(key);
parents.put(key, newChild);
root.add(newChild);
);
configurableSources.values().stream().filter(s ->
return !s.getParent().isEmpty() && !s.getParent().trim().toLowerCase().equals("wanadvr");
).forEachOrdered(child ->
String parentKey = child.getParent();
String key = child.getId();
DefaultMutableTreeNode newChild = new DefaultMutableTreeNode(key);
if (parents.keySet().contains(parentKey))
parents.get(parentKey).add(newChild);
parents.put(key, newChild);
else
reste.put(key, child);
);
while (reste.size() > 0)
reste.values().stream().forEachOrdered(child ->
String parentKey = child.getParent();
String key = child.getId();
DefaultMutableTreeNode newChild = new DefaultMutableTreeNode(key);
if (parents.keySet().contains(parentKey))
parents.get(parentKey).add(newChild);
parents.put(key, newChild);
reste.remove(key);
);
class TreeNodeCheckBox extends JCheckBox
public TreeNodeCheckBox()
this("", false);
public TreeNodeCheckBox(final String text, final boolean selected)
this(text, null, selected);
public TreeNodeCheckBox(final String text, final Icon icon, final boolean selected)
super(text, icon, selected);
setMargin(new Insets(1, 1, 1, 1));
System.out.println(text);
System.out.println(isSelected());
GridManager instance = GridManager.getInstance();
if (isSelected())
instance.getDisplayableSources().put(text, instance.getConfigurableSources().get(text).getSource());
else
instance.getDisplayableSources().remove(text);
instance.prepareDisplay();
class CheckBoxTreeNodeRenderer implements TreeCellRenderer
Color selectionBorderColor, selectionForeground, selectionBackground, textForeground, textBackground;
private TreeNodeCheckBox treeNodeCheckBox = new TreeNodeCheckBox();
private DefaultTreeCellRenderer defaultRenderer = new DefaultTreeCellRenderer();
public CheckBoxTreeNodeRenderer()
Font fontValue;
fontValue = UIManager.getFont("Tree.font");
if (fontValue != null)
treeNodeCheckBox.setFont(fontValue);
Boolean booleanValue = (Boolean) UIManager.get("Tree.drawsFocusBorderAroundIcon");
treeNodeCheckBox.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");
protected TreeNodeCheckBox getCheckBoxRenderer()
return treeNodeCheckBox;
public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected, boolean expanded,
boolean leaf, int row, boolean hasFocus)
Component component;
if (leaf)
String stringValue = tree.convertValueToText(value, selected, expanded, leaf, row, false);
treeNodeCheckBox.setText(stringValue);
treeNodeCheckBox.setSelected(GridManager.getInstance().getDisplayableSources().keySet().contains(stringValue));
treeNodeCheckBox.setEnabled(tree.isEnabled());
if (selected)
treeNodeCheckBox.setBorder(new LineBorder(selectionBorderColor));
treeNodeCheckBox.setForeground(selectionForeground);
treeNodeCheckBox.setBackground(selectionBackground);
else
treeNodeCheckBox.setBorder(null);
treeNodeCheckBox.setForeground(textForeground);
treeNodeCheckBox.setBackground(textBackground);
if ((value != null) && (value instanceof DefaultMutableTreeNode))
Object userObject = ((DefaultMutableTreeNode) value).getUserObject();
if (userObject instanceof TreeNodeCheckBox)
TreeNodeCheckBox node = (TreeNodeCheckBox) userObject;
treeNodeCheckBox.setText(node.getText());
treeNodeCheckBox.setSelected(node.isSelected());
component = treeNodeCheckBox;
else
component = defaultRenderer.getTreeCellRendererComponent(tree, value, selected, expanded, leaf, row,
hasFocus);
return component;
class CheckBoxTreeNodeEditor extends AbstractCellEditor implements TreeCellEditor
CheckBoxTreeNodeRenderer renderer = new CheckBoxTreeNodeRenderer();
JTree tree;
public CheckBoxTreeNodeEditor(JTree tree)
this.tree = tree;
public Object getCellEditorValue()
TreeNodeCheckBox checkBox = renderer.getCheckBoxRenderer();
TreeNodeCheckBox checkBoxNode = new TreeNodeCheckBox(checkBox.getText(), checkBox.isSelected());
return checkBoxNode;
public boolean isCellEditable(EventObject event)
boolean editable = 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;
editable = treeNode.isLeaf();
return editable;
public Component getTreeCellEditorComponent(JTree tree, Object value, boolean selected, boolean expanded,
boolean leaf, int row)
Component editor = renderer.getTreeCellRendererComponent(tree, value, true, expanded, leaf, row, true);
if (editor instanceof TreeNodeCheckBox)
((TreeNodeCheckBox) editor).addItemListener(new ItemListener()
public void itemStateChanged(ItemEvent itemEvent)
if (stopCellEditing())
fireEditingStopped();
);
return editor;
class TreeDragSource implements DragSourceListener, DragGestureListener
DragSource source;
DragGestureRecognizer recognizer;
TransferableTreeNode transferable;
DefaultMutableTreeNode oldNode;
JTree sourceTree;
public TreeDragSource(JTree tree, int actions)
sourceTree = tree;
source = new DragSource();
recognizer = source.createDefaultDragGestureRecognizer(sourceTree, actions, this);
/*
* Drag Gesture Handler
*/
public void dragGestureRecognized(DragGestureEvent dge)
TreePath path = sourceTree.getSelectionPath();
if ((path == null) || (path.getPathCount() <= 1))
// We can't move the root node or an empty selection
return;
oldNode = (DefaultMutableTreeNode) path.getLastPathComponent();
transferable = new TransferableTreeNode(path);
// source.startDrag(dge, DragSource.DefaultMoveNoDrop, transferable, this);
// If you support dropping the node anywhere, you should probably
// start with a valid move cursor:
source.startDrag(dge, DragSource.DefaultMoveDrop, transferable, this);
/*
* Drag Event Handlers
*/
public void dragEnter(DragSourceDragEvent dsde)
public void dragExit(DragSourceEvent dse)
public void dragOver(DragSourceDragEvent dsde)
public void dropActionChanged(DragSourceDragEvent dsde)
System.out.println("Action: " + dsde.getDropAction());
System.out.println("Target Action: " + dsde.getTargetActions());
System.out.println("User Action: " + dsde.getUserAction());
public void dragDropEnd(DragSourceDropEvent dsde)
/*
* to support move or copy, we have to check which occurred:
*/
System.out.println("Drop Action: " + dsde.getDropAction());
if (dsde.getDropSuccess() && (dsde.getDropAction() == DnDConstants.ACTION_MOVE))
((DefaultTreeModel) sourceTree.getModel()).removeNodeFromParent(oldNode);
/*
* to support move only... if (dsde.getDropSuccess())
* ((DefaultTreeModel)sourceTree.getModel()).removeNodeFromParent(oldNode);
*/
// TreeDropTarget.java
// A quick DropTarget that's looking for drops from draggable JTrees.
//
class TreeDropTarget implements DropTargetListener
DropTarget target;
JTree targetTree;
public TreeDropTarget(JTree tree)
targetTree = tree;
target = new DropTarget(targetTree, this);
/*
* Drop Event Handlers
*/
private TreeNode getNodeForEvent(DropTargetDragEvent dtde)
Point p = dtde.getLocation();
DropTargetContext dtc = dtde.getDropTargetContext();
JTree tree = (JTree) dtc.getComponent();
TreePath path = tree.getClosestPathForLocation(p.x, p.y);
return (TreeNode) path.getLastPathComponent();
public void dragEnter(DropTargetDragEvent dtde)
TreeNode node = getNodeForEvent(dtde);
if (node.isLeaf())
dtde.rejectDrag();
else
// start by supporting move operations
// dtde.acceptDrag(DnDConstants.ACTION_MOVE);
dtde.acceptDrag(dtde.getDropAction());
public void dragOver(DropTargetDragEvent dtde)
TreeNode node = getNodeForEvent(dtde);
if (node.isLeaf())
dtde.rejectDrag();
else
// start by supporting move operations
// dtde.acceptDrag(DnDConstants.ACTION_MOVE);
dtde.acceptDrag(dtde.getDropAction());
public void dragExit(DropTargetEvent dte)
public void dropActionChanged(DropTargetDragEvent dtde)
public void drop(DropTargetDropEvent dtde)
Point pt = dtde.getLocation();
DropTargetContext dtc = dtde.getDropTargetContext();
JTree tree = (JTree) dtc.getComponent();
TreePath parentpath = tree.getClosestPathForLocation(pt.x, pt.y);
DefaultMutableTreeNode parent = (DefaultMutableTreeNode) parentpath.getLastPathComponent();
if (parent.isLeaf())
dtde.rejectDrop();
return;
try
Transferable tr = dtde.getTransferable();
DataFlavor[] flavors = tr.getTransferDataFlavors();
for (int i = 0; i < flavors.length; i++)
if (tr.isDataFlavorSupported(flavors[i]))
dtde.acceptDrop(dtde.getDropAction());
TreePath p = (TreePath) tr.getTransferData(flavors[i]);
DefaultMutableTreeNode node = (DefaultMutableTreeNode) p.getLastPathComponent();
DefaultTreeModel model = (DefaultTreeModel) tree.getModel();
model.insertNodeInto(node, parent, 0);
dtde.dropComplete(true);
System.out.println("dragged node : "+node);
System.out.println("dropped in parent : "+parent);
GridManager.getInstance().getConfigurableSources().get(""+node).setParent(""+parent);
System.out.println(GridManager.getInstance().getConfigurableSources().values());
return;
dtde.rejectDrop();
catch (Exception e)
e.printStackTrace();
dtde.rejectDrop();
// TransferableTreeNode.java
// A Transferable TreePath to be used with Drag & Drop applications.
//
class TransferableTreeNode implements Transferable
public static DataFlavor TREE_PATH_FLAVOR = new DataFlavor(TreePath.class, "Tree Path");
DataFlavor flavors[] = TREE_PATH_FLAVOR ;
TreePath path;
public TransferableTreeNode(TreePath tp)
path = tp;
public synchronized DataFlavor[] getTransferDataFlavors()
return flavors;
public boolean isDataFlavorSupported(DataFlavor flavor)
return (flavor.getRepresentationClass() == TreePath.class);
public synchronized Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException
if (isDataFlavorSupported(flavor))
return (Object) path;
else
throw new UnsupportedFlavorException(flavor);
NB 我的 D&D 事件在成功放入 drop 处理程序 TreeDropTarget
方法 drop
后附加,因为 D&D 使用的是复制 Jtree 模式,因此您可以注入您的构造函数中的事件逻辑。最后,这个 JTree 在一个小的上下文中表现得像我需要的那样,这对于大数据集来说不是一个好主意(很多事件总是在重绘树)
【讨论】:
以上是关于JCheckBoxTree 初始状态问题的主要内容,如果未能解决你的问题,请参考以下文章