标签归档:TreeCellRenderer

关于JTree的一些碎碎念

本博号称关注Web2.0, Ruby/Rails,Java。但是据本人所知,到目前为止还没有任何一篇关于Java方面的文章和只言片语。作为本博的博主,确实有点大言不惭的感觉,一想到这个心中就颇不宁静啊。

其实作为一名博客,写东西的欲望一定要强烈,很多的时候我们并没有很多的素材可以写,因为生活几乎每天都是Just so so,我们谁都不想书写平淡,总想语不惊人死不休。作为博客的人们可能这种感觉更为强烈,我是一名独立博客耶,我不能人云亦云啊,我得从我的文章里抒发我的思想啊。艹,你娘的思想也就大街上小摊上的胡萝卜包子一般廉价,还装。所以呢,我们还是需要不时的刺激一下自己的神经,同时呢,也给我们伟大的祖国发育不良的国联网添加一些有趣或无趣的素材资料吧,也算是为我国早日实现四个现代化添砖加瓦了。

===========我是分割线=========

public class JTree
extends JComponent
implements Scrollable, Accessible
How to Use Trees 一节。

树中特定的节点可以由 TreePath(封装节点及其所有祖先的对象)标识,或由其显示行(其中显示区域中的每一行都显示一个节点)标识。展开 节点是一个非叶节点(由返回 false 的 TreeModel.isLeaf(node) 标识),当展开 其所有祖先时,该节点将显示其子节点。折叠 节点是隐藏它们的节点。隐藏 节点是位于折叠祖先下面的节点。所有可查看 节点的父节点都是可以展开的,但是可以显示它们,也可以不显示它们。显示 节点是可查看的并且位于可以看到它的显示区域。

这是JDK 5.0非官方中文文档中的描述,我想这个描述给我们的第一个直观印象就是这个控件应该和Windows资源管理器中的树状结构的表现是一致的。确实如此,JTree确实是用来作为树形展示使用的,因为我们在很多的数据管理和业务处理上,秉承了我们自古就袭承的分类方式,所以我们有很多的业务可能用到或者说可以用到JTree。

那么JTree怎么用的呢?这个问题我曾经问过自己很多次,还做过很多相关的工作来学习JTree的使用,记得来到北京实习做的第一个学习阶段作业就是使用公司已有的组件产品做一个叠加分析的Demo。作为一个比较友好的叠加分析工具,就肯定需要一些GIS数据处理的功能,比如打开工作空间,数据源,数据集,地图,同时还应该能简单地做一些图层删除或者地图关闭之类的基础功能。这样一来,主体的工作就是基础功能模块的实现了,只是在基础功能模块的基础上,添加一个叠加分析功能模块,而在基础功能模块中的可视化显示JTree必然是首选控件。

在实现树状加载和显示工作空间,参考公司已有的桌面产品UI设计,需要再JTree的每个节点之前添加一些图标,用于标识当前节点的数据类型,例如线数据集和面数据集的图标是有明显差异的,这样对用户的友好度会提高很多。默认的JTree使用Java默认的meta风格显示文件夹(根节点)和文件(叶节点)的图标样式,那么如何添加自定义的图标显示呢?当时我就在JTree的文档中苦苦找寻,希望自己能找到一个setIcon()的方法,最终无耻的失败鸟。后来才知道之需要自己实现一个类,实现TreeCellRenderer就可以了,重写下面这个方法(Sun就是这么排版的,我觉得这样挺好的,不要怪我占太多行了)

Component getTreeCellRendererComponent(JTree tree,

                                       Object value,
                                       boolean selected,
                                       boolean expanded,
                                       boolean leaf,
                                       int row,
                                       boolean hasFocus)

在这个方法中,它的返回值为一个Component,那么我们完全可以自由发挥了,比如返回一个JLabel。当时欣喜若狂,赶紧将代码巴拉巴拉地敲好,一看效果显著啊,根据类型不同生成不同的Icon设置给将要返回的JLabel对象就OK了。

实习的工作相对简单,当时对JTree的理解也就点到为止了。那么此次项目组需要退出Objects Java的控件,其中就有一些控件需要使用到JTree来实现,功能要求相对就复杂了不少。

需要在一个节点文本前,显示多个图标,除了用于区别节点数据类型的图标外,还需要添加几个操作图标,用于可视化节点数据的操作,那么返回一个什么呢?JPanel无疑是最好的选择,轻量化容器中的万金油。在JPanel中添加几个可视化的Icon并不是难事,只是JLabel多少的问题了,通过对节点数据类型的判断分别设置便好了。问题是,如何判断用户当前点击的位置落在JPanel中的那个操作图标上呢?又如何将这些操作直接反映到实际的数据上来呢?通过JTree.getUI()方法获取当前的JTree的UI,然后调用TreeUI.getPathBounds(JTree tree, TreePath path)方法获取当前节点的绘制区域,之后通过鼠标事件MouseEvent的getX()和getY()方法获取当前鼠标的位置信息,由于控件的编写图标的大小由自己定义,所以可以确定图标的大小和JPanel的布局(我使用的是FlowLayout,并且使用setHgap()方法将水平控件间距设置为0),通过像素坐标的计算便可以得出当前用户鼠标单击事件应该响应哪个对应索引处的动作。

在实现的过程中,还需要使用到编辑节点的功能,那么如何来做呢?首选实现TreeCellEditor接口,完全自己定制,可以避免像继承自DefaultTreeCellEditor的烦恼(因为它能满足一般需求,但是会限制你拳脚)。TreeCellEditor也有一个很嚣张的方法,号称自己要接管TreeCellRenderer的方法,就是这个啦

Component getTreeCellEditorComponent(JTree tree, Object value, boolean isSelected, boolean expanded, boolean leaf, int row)

重写这个方法,用来自定义编辑时的节点渲染,例如我的树形控件在编辑的时候只能编辑节点名称,那么前面的图标还是要保持与未编辑时一致的,编辑时只需要将后面的文本置换为一个JTextField就可以了。然后为该JTextField注册一个键盘事件监听器,当输入Enter的时候,调用TreeCellEditor的stopEditing()方法,触发停止编辑事件。如果我们需要将编辑保存至数据模型中的话,就在stopEditing()方法中编写业务代码即可。

综上JTree这个控件实际上是一个完全遵循MVC架构的设计,甚至比MVC还抽象一些。JTree中只处理数据和显示的一些控制,例如判断当前节点是否能够被编辑的方法isPathEditable(TreePath path)等。数据模型完全可以委托为TreeModel来管理,之需要使用setModel便可以轻松地将JTree和TreeModel关联起来。而在其显示渲染上便是完全由TreeCellRenderer来接管的,只需实现一个类,该类实现TreeCellRenderer接口,重写getTreeCellRendererComponent(…)方法,便可实现完全自定义面板显示节点。抽象最高层的是,当节点为可编辑状态时,其渲染又交由TreeCellEditor接管了,在编辑节点时,TreeCellEditor的getTreeCellRendererComponent(…)方法便将完全接管节点的渲染工作,并且控制编辑时的动作,例如取消编辑和停止编辑之类的种种。这么看来,其实JTree只是提供一个壳子,通过其内部的TreeMod存储数据,外部的TreeCellRenderer和TreeCellEditor来控制其显示渲染和编辑渲染,给开发人员提供了一个可高度定制化的控件接口。

==========我还是分割线=========

本文的形成是如行云流水账一般啊,我也有点不知所云了,众位看官,能看则看,不能看则拉倒吧。JDK API文档在你家里叫你回家翻翻呢。