我正在使用实现拖放的自定义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;
};
}
}
就我而言,我更新了所有代码,下面的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事件在成功拖放到拖放处理程序TreeDropTarget
方法drop
中后被附加,对于复选框,因为D&D使用复制Jtree模式,因此您可以将事件逻辑注入到构造函数。最终,这个JTree在较小的上下文中表现出了我的需求,这对于大型数据集来说不是一个好主意(很多事件总是在重绘树)]