近来在写idea插件,需要用到界面,只能使用swing。于是开始踩坑……
需求
解析依赖文件,展示依赖的版本信息和更新时间,用户发现依赖有问题可以自己更新。由于一个JLabel不够用,默认的DefaultTreeCellRenderer就不够用了,需要开始研究。
第一个坑
DefaultTreeCellRenderer是继承的JLabel和TreeCellRenderer,那么我新写一个继承JComponent和TreeCellRenderer的类不就可以了?但是事实是不行,或者说很麻烦。UI相关的资源和配置需要使用sun.swing.DefaultLookup这个类进行获取,DefaultTreeCellRenderer在javax.swing.tree包下,可以访问,但是我们不行,sun.swing.DefaultLookup这个类并没有公开。直接引用会出现Symbol is declared in module 'java.desktop' which does not export package 'sun.swing'错误。所以,我们必须使用原来的DefaultTreeCellRenderer类。
第二个坑
JTree本质是调用TreeCellRenderer的getTreeCellRendererComponent方法,然后一行行画出来的,所以只需要构造一个TreeCellRenderer类就够了。每一条数据都会执行一遍,然后画到JTree控件里面去,事件处理也需要注册在JTree上面。也因此,在自定义TreeCellRenderer里面添加交互控件,比如按钮,是无效的。如果需要进行交互,最好用右键菜单(有个editorCellRenderer不确定行不行,没试过,待尝试)。
第三个坑
getTreeCellRendererComponent方法的value是包装后的MutableTreeNode,不是userObject。需要进行转换才能拿到传进去的数据。
示例
package me.small.vision.window
import me.small.vision.data.InfoPO
import java.awt.BorderLayout
import java.awt.Component
import javax.swing.ImageIcon
import javax.swing.JComponent
import javax.swing.JLabel
import javax.swing.JTree
import javax.swing.tree.DefaultMutableTreeNode
import javax.swing.tree.DefaultTreeCellRenderer
import javax.swing.tree.TreeCellRenderer
class MyTreeCellRenderer : JComponent(), TreeCellRenderer {
private var father: DefaultTreeCellRenderer = DefaultTreeCellRenderer()
init {
father.leafIcon = ImageIcon(this.javaClass.classLoader.getResource("leaf.png"))
layout = BorderLayout()
add(JLabel("time"), BorderLayout.EAST)
add(JLabel("info"), BorderLayout.WEST)
}
override fun getTreeCellRendererComponent(
tree: JTree,
value: Any,
selected: Boolean,
expanded: Boolean,
leaf: Boolean,
row: Int,
hasFocus: Boolean
): Component {
value as DefaultMutableTreeNode
remove(1)
if (value.userObject is InfoPO) {
val data = value.userObject!! as InfoPO
(components[0] as JLabel).text = data.time
add(
father.getTreeCellRendererComponent(
tree,
data.name,
selected,
expanded,
leaf,
row,
hasFocus
), BorderLayout.WEST
)
} else {
(components[0] as JLabel).text = ""
add(
father.getTreeCellRendererComponent(tree, value, selected, expanded, leaf, row, hasFocus),
BorderLayout.WEST
)
}
return this
}
}
这里有个小坑,components是私有属性component的引用,直接修改components[1]是不行的,必须删了然后添加。这样渲染出来的效果,就是左边是依赖名称,右边是更新时间,清晰明了。