这是一个web版本的树管理示例
swing使用jTree实现树节点的增/删/改
实现树功能的起步
- 了解树结构的组成概念 ,
节点,枝,根,叶,度,层/深度/高度,双亲/孩子/兄弟等 - 了解树的展开、收起算法
- 了解树的路径知识
- 了解子节点与父节点之间的存在的逻辑关系等等
增删改查的思路
- 从选择的树节点开始, 获得选择的树对象,可以进行修改和删除操作
- 从选择的树节点开始,可以加入逻辑判断,是添加节点至父节点作为 不可扩展的叶节点,还是修改当前节点为父节点
拖拽的思路
- 拖拽节点作为初始节点,目标节点作为终止节点,移动根对象位置,比如从1-1-3 移动至 1-3-1等
查找的思路
- 递归所有的节点,找到符合搜索条件的节点,算法上的优化,可以融入节点直接特定的某些特征作为筛选
jTree增删改查 示例代码
缕清思路最重要,代码没几行
swing的JTree实现节点增删改查,如果不带图标样式 还是相当简单的,这是上图的示例代码
- 功能按钮: 用到了枚举的小技巧
- 左侧树: 了解树结构后,便可以轻松的构建初始树
- 增加主要方法: DefaultTreeModel ==》insertNodeInto
- 删除主要方法: DefaultTreeModel ==》removeNodeFromParent
- 更新主要方法: DefaultTreeModel ==》reload
import cn.hutool.core.util.RandomUtil;
import cn.note.swing.core.ui.theme.ButtonFactory;
import cn.note.swing.core.ui.theme.CoreFlatLaf;
import cn.note.swing.core.ui.theme.PngIconFactory;
import cn.note.swing.core.util.FrameUtil;
import cn.note.swing.core.util.WinUtil;
import cn.note.swing.core.view.AbstractMigView;
import cn.note.swing.core.view.Row;
import com.google.common.base.Enums;
import net.miginfocom.swing.MigLayout;
import org.jdesktop.swingx.JXTree;
import org.jdesktop.swingx.icon.EmptyIcon;
import javax.swing.*;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
/**
* @description: jTree测试
*/
public class JTreeTest extends AbstractMigView {
private JXTree tree;
private DefaultTreeModel defaultTreeModel;
/**
* 定义migLayout布局
*
* @return migLayout布局
*/
@Override
protected MigLayout defineMigLayout() {
return new MigLayout("insets 0,gapy 0,wrap 1");
}
/**
* 初始化成员对象
*/
@Override
protected void init() {
tree = new JXTree(treeData());
tree.setCellRenderer(new CustomTreeCellRenderer());
tree.setLeafIcon(new EmptyIcon());
tree.setOpenIcon(new EmptyIcon());
tree.setClosedIcon(new EmptyIcon());
}
/**
* render视图
*/
@Override
protected void render() {
addActionTypeEvents();
view.add(new JScrollPane(tree), "w 300,h 100%");
}
private DefaultMutableTreeNode treeData() {
DefaultMutableTreeNode treeModel = new DefaultMutableTreeNode("root");
for (int i = 1; i < 4; i++) {
DefaultMutableTreeNode secondTree = new DefaultMutableTreeNode("dir-" + i);
treeModel.add(secondTree);
for (int j = 1; j < 5; j++) {
DefaultMutableTreeNode nodeTree = new DefaultMutableTreeNode("node-" + j);
secondTree.add(nodeTree);
}
}
return treeModel;
}
private void addActionTypeEvents() {
Row row = new Row();
ActionTypeListener actionTypeListener = new ActionTypeListener();
for (ActionType actionType : ActionType.values()) {
JButton btn = actionType.toBtn();
row.add(btn);
btn.addActionListener(actionTypeListener);
}
view.add(row, "w 100%");
}
enum ActionType {
reset, expand, collapse, rename, delete, createParentNode, createNode;
public JButton toBtn() {
return ButtonFactory.primaryButton(this.name());
}
}
private class ActionTypeListener implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
String command = e.getActionCommand();
ActionType type = Enums.getIfPresent(ActionType.class, command).orNull();
defaultTreeModel = (DefaultTreeModel) tree.getModel();
DefaultMutableTreeNode selectNode = (DefaultMutableTreeNode) tree.getLastSelectedPathComponent();
switch (type) {
case reset:
tree.setModel(new DefaultTreeModel(treeData()));
break;
case expand:
tree.expandAll();
break;
case collapse:
tree.collapseAll();
break;
case rename:
selectNode.setUserObject("rename" + RandomUtil.randomNumbers(8));
defaultTreeModel.reload(selectNode);
// 局部刷新
// Rectangle r = tree.getUI().getPathBounds(tree, tree.getSelectionPath());
// tree.repaint(r);
break;
case delete:
defaultTreeModel.removeNodeFromParent(selectNode);
break;
case createParentNode:
createTreeNode(true);
break;
case createNode:
createTreeNode(false);
break;
default:
WinUtil.alert("未知的命令:{}", command);
break;
}
}
private void createTreeNode(boolean isParent) {
String label = "node:";
Icon icon = PngIconFactory.ICON_EXAMPLE;
if (isParent) {
label = "p-node:";
icon = PngIconFactory.ICON_TOPIC;
}
DefaultMutableTreeNode selectNode = (DefaultMutableTreeNode) tree.getLastSelectedPathComponent();
int childCount = selectNode.getChildCount();
CategoryNode categoryNode = new CategoryNode();
categoryNode.setIcon(icon);
categoryNode.setTitle(label + RandomUtil.randomNumbers(5));
DefaultMutableTreeNode newNode = new DefaultMutableTreeNode(categoryNode);
newNode.setAllowsChildren(isParent);
if (selectNode.getAllowsChildren()) {
defaultTreeModel.insertNodeInto(newNode, selectNode, childCount);
} else {
if (childCount == 0) {
DefaultMutableTreeNode pNode = (DefaultMutableTreeNode) selectNode.getParent();
int addIndex = pNode.getIndex(selectNode);
defaultTreeModel.insertNodeInto(newNode, pNode, addIndex + 1);
} else {
defaultTreeModel.insertNodeInto(newNode, selectNode, childCount);
}
}
}
}
public static void main(String[] args) {
CoreFlatLaf.install();
FrameUtil.launchTest(JTreeTest.class);
}
}
这是动态添加图标的render类
public class CustomTreeCellRenderer extends DefaultTreeCellRenderer {
@Override
public Component getTreeCellRendererComponent(JTree tree, Object value,
boolean sel, boolean expanded, boolean leaf, int row,
boolean hasFocus) {
super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf,
row, hasFocus);
DefaultMutableTreeNode node = (DefaultMutableTreeNode) value;
Object userObject = node.getUserObject();
if (userObject instanceof CategoryNode) {
CategoryNode categoryNode = (CategoryNode) userObject;
super.setText(categoryNode.getTitle());
super.setIcon(categoryNode.getIcon());
}
return this;
}
}