一、文件拖拽相关API的实现:
1.1 DnD的实现(DropTargetListener)
保留JTreeDnD添加节点方法,继承JTree不变而去掉DropTargetListener的实现改为JTreeUtils和JPanelDnD继承DropTargetListener的drop方法(说明:标有【*】则表示该Class已弃用)
* public class JTreeDnD extends JTree implements DropTargetListener {}
public class JTreeUtils extends JTree {}
public class JTreeDnD extends JPanel implements DropTargetListener {}
@SuppressWarnings("unchecked")
@Override
public void drop(DropTargetDropEvent dtde) {
DataFlavor dataFlavor = DataFlavor.javaFileListFlavor;
if (dtde.isDataFlavorSupported(dataFlavor)) {
dtde.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
List<File> list = null;
Transferable transferable = dtde.getTransferable();
try {
list = (List<File>) transferable.getTransferData(dataFlavor);
} catch (UnsupportedFlavorException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
addNewUserObjects((DefaultMutableTreeNode) this.getModel().getRoot(), list);
dtde.dropComplete(true);
}
}
二、文件树相关API的实现:
2.1 JTree
jTreeDnD更新为JTreeUtils
jTreeDnD = new JTreeDnD();
jTreeDnD.setShowsRootHandles(true);
jTreeDnD.setDragEnabled(true);
jTreeDnD.setEditable(true);
2.2 DefaultMutableTreeNode
DefaultMutableTreeNode dmTreeNodeRoot = new DefaultMutableTreeNode();
dmTreeNodeRoot.setUserObject("图层");
2.3 DefaultTreeModel
DefaultTreeModel treeModelRoot = new DefaultTreeModel(dmTreeNodeRoot);
jTreeDnD.setModel(treeModelRoot);
三、子节点的增删,拖拽(增删改查)
3.1 文件读取
private void loadResources(File[] fileArray) {
jTreeDnD.addNewUserObjects((DefaultMutableTreeNode) ((DefaultTreeModel) jTreeDnD.getModel()).getRoot(),
fileUtils.toList(fileArray));
}
前面第一部分有介绍,拖拽使用了list<File>来读取文件,此处沿用了以上方法。
3.2 添加子节点
DefaultTreeModel dtm = (DefaultTreeModel) this.getModel();
for (Iterator<File> iterator = list.iterator(); iterator.hasNext();) {
File file = (File) iterator.next();
DefaultMutableTreeNode dmtn = new DefaultMutableTreeNode();
if (file.isDirectory()) {
dmtn.setAllowsChildren(true);
} else {
dmtn.setAllowsChildren(false);
}
dmtn.setUserObject(file);
dtm.insertNodeInto(dmtn, parent, 0);
}
MutableTreeNode提供的方法:
public void insertNodeInto(MutableTreeNode newChild, MutableTreeNode parent, int index){}
3.3 文件夹展开
如以上代码片段,对于文件夹须有展开的操作,所以调用了setAllowsChildren(true)方法。
private void expandDirectory(int selRow, TreePath selPath) {
DefaultMutableTreeNode treeNodeSelected = (DefaultMutableTreeNode) selPath.getLastPathComponent();
if (null == treeNodeSelected) {
return;
}
Object object = treeNodeSelected.getUserObject();
if (object instanceof File) {
File file = (File) object;
if (file.isDirectory()) {
jTreeDnD.addNewUserObjects(treeNodeSelected, fileUtils.toList(file.listFiles()));
}
}
}
对JTreeUtils进行MouseListener监听:
public void mouseClicked(MouseEvent e) {
int selRow = jTreeUtils.getRowForLocation(e.getX(), e.getY());
TreePath selPath = jTreeUtils.getPathForLocation(e.getX(), e.getY());
if (null != selPath) {
if (selRow != -1) {
if (e.getButton() == MouseEvent.BUTTON3) {
myRightClick(selRow, selPath);
} else if (e.getClickCount() == 2) {
myDoubleClick(selRow, selPath);
}
}
}
}
(关于选择节点的获取,用上了TreePath,官方API文档介绍了一个监听器,可选用:)
MouseListener ml = new MouseAdapter() {
public void mousePressed(MouseEvent e) {
int selRow = jTreeDnD.getRowForLocation(e.getX(), e.getY());
TreePath selPath = jTreeDnD.getPathForLocation(e.getX(), e.getY());
if (selRow != -1) {
if (e.getClickCount() == 1) {
mySingleClick(selRow, selPath);
} else if (e.getClickCount() == 2) {
myDoubleClick(selRow, selPath);
}
}
}
};
jTreeDnD.addMouseListener(ml);
int selRow = jTreeDnD.getRowForLocation(e.getX(), e.getY());
TreePath selPath = jTreeDnD.getPathForLocation(e.getX(), e.getY());
TreePath selPath = jTreeDnD.getPathForRow(selRow);
获取当前选择的节点:
DefaultMutableTreeNode treeNodeSelected = (DefaultMutableTreeNode) selPath.getLastPathComponent();
3.4 删除节点
DefaultTreeModel dtm = (DefaultTreeModel) jTreeUtils.getModel();
dtm.removeNodeFromParent(treeNodeSelected);
DefaultTreeModel提供的方法:
public void removeNodeFromParent(MutableTreeNode node){}
3.5 拖拽节点
对JTreeUtils进行MouseMotionListener监听:
MouseMotionListener mml = new MouseMotionListener() {
private TreePath draggedPath;
private TreePath movedPath;
@Override
public void mouseDragged(MouseEvent e) {
// int selRow = jTreeUtils.getRowForLocation(e.getX(), e.getY());
TreePath selPath = jTreeUtils.getPathForLocation(e.getX(), e.getY());
if (null != selPath) {
draggedPath = selPath;
}
}
@Override
public void mouseMoved(MouseEvent e) {
int selRow = jTreeUtils.getRowForLocation(e.getX(), e.getY());
TreePath selPath = jTreeUtils.getPathForLocation(e.getX(), e.getY());
if (null != selPath) {
movedPath = selPath;
myNodeMoveTo(selRow, selPath);
}
}
}
获得抓取的节点TreePath,以及最后移动停止的节点TreePath,再调用DefaultTreeModel提供的节点增删方法:
dtm.removeNodeFromParent(treeNodeSelected);
dtm.insertNodeInto(treeNodeSelected, parent, 0);
movedPath = null;
注意:
1.关于树状结构节点拖动,个人认为根节点禁止更改,父或祖先节点禁止移动向子节点。
2.节点抓取或移动状态时,未接触或未抓取节点所获取row值为-1,treePath值为null,此处可以根据需要自行用作删除节点,或默认将节点移动并设为根节点的子节点的操作,此处我并未判断为可使用,节点更改位置后我加入了这个方法:
dtm.nodeChanged(treeNodeParent);
3.6 查找节点
暂未进行研究,待编辑。。。
以下为项目演示效果图
Java图形化界面:
读取文件:
文件拖拽:
文件夹节点展开:
节点拖动:
节点删除:
后记:
关于JTree相关的一系列操作,很多大佬推荐使用DefaultTreeModel来操作节点(节点修改后实时更新显示);
之前做了个基于ImageIO的Java小工具,集合了图层合并切片分割等功能的小工具。这段时间无聊想整个文件拖拽,类似可编辑图层的升级版本小工具,考虑了许久就盯上了JTree的相关信息。
对于以上内容介绍还有问题的可以留言交流,或者跳转到本项目的源码仓库GitHub直接体验用法。
完整代码同步更新在GitHub仓库,想看可以点击下面链接跳转阅读。