jtree一般的用法是:
1. 展示电脑中文件的层次结构,如图所示.
具体的代码:
package jtree; import java.io.File; import javax.swing.JTree; import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.DefaultTreeModel; public class ZJtree extends JTree { private static final long serialVersionUID = -581164150235777786L; private static final String ROOT_NAME = "我的电脑"; private static final int levelUp = 3; public ZJtree() { this.setModel(new DefaultTreeModel(createRootNode())); } public DefaultMutableTreeNode createRootNode(){ DefaultMutableTreeNode treeNode = null; DefaultMutableTreeNode rootNode = new DefaultMutableTreeNode(ROOT_NAME); for(int i = 0; i < File.listRoots().length ; i++){ if(File.listRoots()[i].isDirectory()){ String rootPath = File.listRoots()[i].getPath(); treeNode = creatDefaultMutableTreeNode(rootPath,0); rootNode.add(treeNode); treeNode = null; } } return rootNode; } private DefaultMutableTreeNode creatDefaultMutableTreeNode(String nodePath,int level) { DefaultMutableTreeNode node = new DefaultMutableTreeNode(nodePath); DefaultMutableTreeNode treeNode = null; level = level+1; File file = new File(nodePath); if(file.isDirectory() && file.listFiles() != null){ for(int i = 0; i < file.listFiles().length && level < levelUp; i++){ if(file.listFiles()[i].isDirectory()){ String rootPath = file.listFiles()[i].getPath(); treeNode = creatDefaultMutableTreeNode(rootPath,level); node.add(treeNode); treeNode = null; } } } return node; } }
说明:限制层次的原因是因为电脑中文件过多,一直加载会比较的慢,以后我们会在处理这个问题。
看到上面的那个界面的第一个反应,就是好丑啊,我们慢慢的优化。在树节点的选择上面,增加combox,一步一步的来。
首先我们新建一个扩展自Jtree的自定义类:
public class ZTreeCheckBox extends JTree { private static final long serialVersionUID = -581164150235777786L; private static final String ROOT_NAME = "p"; private static final int levelUp = 3; public ZTreeCheckBox() { this.setModel(new DefaultTreeModel(createRootNode())); this.setCellRenderer(new ChectBoxTreeCellRender()); this.addCheckSelectListender(); } private void addCheckSelectListender() { this.addMouseListener(new CheckBoxTreeNodeListender(this)); } public CheckBoxTreeNode createRootNode(){ CheckBoxTreeNode treeNode = null; CheckBoxTreeNode rootNode = new CheckBoxTreeNode(ROOT_NAME); for(int i = 0; i < File.listRoots().length ; i++){ if(File.listRoots()[i].isDirectory()){ String rootPath = File.listRoots()[i].getPath(); treeNode = creatDefaultMutableTreeNode(rootPath,0); rootNode.add(treeNode); treeNode = null; } } return rootNode; } private CheckBoxTreeNode creatDefaultMutableTreeNode(String nodePath,int level) { CheckBoxTreeNode node = new CheckBoxTreeNode(nodePath); CheckBoxTreeNode treeNode = null; level = level+1; File file = new File(nodePath); if(file.isDirectory() && file.listFiles() != null){ for(int i = 0; i < file.listFiles().length && level < levelUp; i++){ if(file.listFiles()[i].isDirectory()){ String rootPath = file.listFiles()[i].getPath(); treeNode = creatDefaultMutableTreeNode(rootPath,level); node.add(treeNode); treeNode = null; } } } return node; } }
tree 的数据的组织形式,还和原来一样,但是我们需要tree的节点的形式是Combox形式的,最起码前面要是一个可选的样子,所以我们自定义一个TreeCellRenderer,我们在显示的时候可以是一个Jpanel,其中包含一个combox,一个Jlabel代码如下:
public class ChectBoxTreeCellRender extends JPanel implements TreeCellRenderer { private static final long serialVersionUID = 4676667399191240255L; protected JCheckBox check; protected CheckBoxTreeLabel label; // protected JLabel label; public ChectBoxTreeCellRender() { setLayout(null); add(check = new JCheckBox()); add(label = new CheckBoxTreeLabel()); // add(label = new JLabel()); check.setBackground(UIManager.getColor("Tree.textBackground")); label.setForeground(UIManager.getColor("Tree.textForeground")); this.setPreferredSize(new Dimension(100, 20)); } /* (non-Javadoc) * @see javax.swing.tree.TreeCellRenderer#getTreeCellRendererComponent(javax.swing.JTree, java.lang.Object, boolean, boolean, boolean, int, boolean) */ @Override public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) { String stringValue = tree.convertValueToText(value, selected, expanded, leaf, row, hasFocus); setEnabled(tree.isEnabled()); check.setSelected(((CheckBoxTreeNode) value).isSelect()); label.setFont(tree.getFont()); label.setText(stringValue); label.setSelected(selected); label.setFocus(hasFocus); if (leaf) label.setIcon(UIManager.getIcon("Tree.leafIcon")); else if (expanded) label.setIcon(UIManager.getIcon("Tree.openIcon")); else label.setIcon(UIManager.getIcon("Tree.closedIcon")); return this; } @Override public void doLayout() { Dimension dCheck = check.getPreferredSize(); Dimension dLabel = label.getPreferredSize(); int yCheck = 0; int yLabel = 0; if (dCheck.height < dLabel.height) yCheck = (dLabel.height - dCheck.height) / 2; else yLabel = (dCheck.height - dLabel.height) / 2; check.setLocation(0, yCheck); check.setBounds(0, yCheck, dCheck.width, dCheck.height); label.setLocation(dCheck.width, yLabel); label.setBounds(dCheck.width, yLabel, dLabel.width, dLabel.height); } @Override public void setBackground(Color color) { if (color instanceof ColorUIResource) color = null; super.setBackground(color); } }
关于选中了节点以后,label是否出现阴影表示选择,在这里不是重点,这个扩展CheckBoxTreeLabel,会放在git上,现在我们关注的是Jlabel和Combox的结合而成的JPanel。
现在我们还缺少一个选择以后,Combox能够显示选中,所以我们还需要增加增加一个节点选择的监听:
public class CheckBoxTreeNodeListender extends MouseAdapter { private ZTreeCheckBox zTreeCheckBox = null; public CheckBoxTreeNodeListender(ZTreeCheckBox zTreeCheckBox) { this.zTreeCheckBox = zTreeCheckBox; } // 被选中事件 @Override public void mousePressed(MouseEvent event) { if(event.getSource() instanceof ZTreeCheckBox){ Point p = event.getPoint(); int row = zTreeCheckBox.getRowForLocation(p.x, p.y); TreePath path = zTreeCheckBox.getPathForRow(row); if (path != null) { CheckBoxTreeNode node = (CheckBoxTreeNode) path .getLastPathComponent(); if (node != null) { boolean isSelected = !node.isSelect(); node.setSelected(isSelected); ((DefaultTreeModel) zTreeCheckBox.getModel()).nodeStructureChanged(node); } } } // else{ // String comm = ((JButton)event.getSource()).getActionCommand(); // } // } }
最后我们得到是这个样子的:
左边的按钮,表示的选择的操作:
① 全部的选中
② 全部的不选中
③ 选择一个子树
④ 去除某一个子树
一种实现的思路,是在选择的事件分为四类,然后实现对应的逻辑,也就是对每一个节点的选择做出操作。在CheckBoxTreeNodeListender 的监听响应中添加:
// 被选中事件 @Override public void mousePressed(MouseEvent event) { Point p = event.getPoint(); int row = zTreeCheckBox.getRowForLocation(p.x, p.y); TreePath path = zTreeCheckBox.getPathForRow(row); if(event.getSource() instanceof ZTreeCheckBox){ if (path != null) { CheckBoxTreeNode node = (CheckBoxTreeNode) path .getLastPathComponent(); if (node != null) { boolean isSelected = !node.isSelect(); node.setSelected(isSelected); ((DefaultTreeModel) zTreeCheckBox.getModel()).nodeStructureChanged(node); } } } else{ String comm = ((JButton)event.getSource()).getActionCommand(); if("SelectAll".equals(comm)){ CheckBoxTreeNode node = (CheckBoxTreeNode) path.getLastPathComponent(); selectAllNode(node,true); ((DefaultTreeModel) zTreeCheckBox.getModel()).nodeStructureChanged(node); }else if("DeselectAll".equals(comm)){ CheckBoxTreeNode node = (CheckBoxTreeNode) path.getLastPathComponent(); selectAllNode(node,false); ((DefaultTreeModel) zTreeCheckBox.getModel()).nodeStructureChanged(node); } } // } private void selectAllNode(CheckBoxTreeNode node,boolean select) { if (node != null) { node.setSelected(select); if(node.getChildCount() > 0 ){ for (int i = 0; i < node.getChildCount(); i++) { CheckBoxTreeNode child = (CheckBoxTreeNode) node.getChildAt(i); selectAllNode(child,select); } } } }
类似的实现,即可满足条件,封装性不是很好,有时间在进行重构,抽成一个借口,和对应的实现。
下一篇我们的实现成这样:
改变树节点的图片,变得好看一点。