swing标准库,实在有些简陋,很多显然的需求却无法满足,jdk5以前的版本,连表格列的排序功能都没有,swing最大的毛病也在这。最近学习python,同时也学习了一些GUI库,发现基本上tree都支持多列显示,在swing里,你就必须找第三方库了。
废话不说了,还是说说本来的需求。
我是需要在tree的node上显示checkbox。要满足这个需求,最明显的就有两个方法。
1 实现TreeCellRenderer接口,JCheckBox来做渲染,这样就可以,不过这有个问题,就是感观上和默认node实现,有很多差异,一些如选中,焦点,背景色等等,这些在DefaultTreeCellRenderer之类里有很多关于此类的考虑,如果自己再考虑,写一遍,似乎不合适。
2 用icon图片来代替,这是个方法,但是我有个期望是,希望checkbox能跟随look&feel做切换,所以期望是系统采用各个look&feel自己的chekbox。
大家知道,swing是自己画的,这样的话,那么icon也是,也就是说,我可以借用JCheckBox的渲染来渲染treenode的icon。最初想法是,直接用JCheckBox的icon放到DefaultTreeCellRenderer的icon里,但却报错,原因是DefaultTreeCellRenderer父类是JLabel,每个组件的icon的渲染,与组件相关的model是紧密联系的。
不同组件的icon不能直接使用。那么怎么办呢?其实也很简单,保证paintIcon的Component参数与icon一致就行了。
具体代码:
以上代码除了实现checbox外,还实现了选中时,把显示文字增加删除线。(反正没找到简单方法,但在QT里很简单)。
下面是选中节点的判断监听类:
废话不说了,还是说说本来的需求。
我是需要在tree的node上显示checkbox。要满足这个需求,最明显的就有两个方法。
1 实现TreeCellRenderer接口,JCheckBox来做渲染,这样就可以,不过这有个问题,就是感观上和默认node实现,有很多差异,一些如选中,焦点,背景色等等,这些在DefaultTreeCellRenderer之类里有很多关于此类的考虑,如果自己再考虑,写一遍,似乎不合适。
2 用icon图片来代替,这是个方法,但是我有个期望是,希望checkbox能跟随look&feel做切换,所以期望是系统采用各个look&feel自己的chekbox。
大家知道,swing是自己画的,这样的话,那么icon也是,也就是说,我可以借用JCheckBox的渲染来渲染treenode的icon。最初想法是,直接用JCheckBox的icon放到DefaultTreeCellRenderer的icon里,但却报错,原因是DefaultTreeCellRenderer父类是JLabel,每个组件的icon的渲染,与组件相关的model是紧密联系的。
void paintIcon(Component c, Graphics g, int x, int y);
不同组件的icon不能直接使用。那么怎么办呢?其实也很简单,保证paintIcon的Component参数与icon一致就行了。
具体代码:
public class CheckboxTreeNodeRenderer extends DefaultTreeCellRenderer implements
TreeCellRenderer {
private static final long serialVersionUID = 1L;
private JCheckBox checkbox = new JCheckBox();
private Icon checkboxIcon = UIManager.getIcon("CheckBox.icon");
private ProxyCheckBoxIcon proxyIcon = new ProxyCheckBoxIcon();
private Font normal;
private Font disabled;
@SuppressWarnings("unchecked")
public CheckboxTreeNodeRenderer() {
normal = getFont().deriveFont(Font.PLAIN);
Map<TextAttribute, Object> attrs = (Map<TextAttribute, Object>) normal.getAttributes();
attrs.put(TextAttribute.STRIKETHROUGH, TextAttribute.STRIKETHROUGH_ON);
//attrs.put(TextAttribute.FONT, getFont().deriveFont(Font.ITALIC));
disabled = getFont().deriveFont(attrs);
}
public Component getTreeCellRendererComponent(JTree tree, Object value,
boolean selected, boolean expanded, boolean leaf, int row,
boolean hasFocus) {
super.getTreeCellRendererComponent(tree, value, selected, expanded,
leaf, row, hasFocus);
if (value instanceof CheckboxTreeNode) {
CheckboxTreeNode ctn = (CheckboxTreeNode) value;
checkbox.setSelected(ctn.isChecked());
setIcon(proxyIcon);
if (ctn.isChecked()) {
setFont(disabled);
if (!selected)
setForeground(Color.GRAY);
} else {
setFont(normal);
//setForeground(Color.BLACK);
}
} else {
setFont(normal);
}
return this;
}
private class ProxyCheckBoxIcon implements Icon, Serializable {
private static final long serialVersionUID = 1L;
@Override
public void paintIcon(Component c, Graphics g, int x, int y) {
checkboxIcon.paintIcon(checkbox, g, x, y);
}
@Override
public int getIconWidth() {
return checkboxIcon.getIconWidth();
}
@Override
public int getIconHeight() {
return checkboxIcon.getIconHeight();
}
}
}
以上代码除了实现checbox外,还实现了选中时,把显示文字增加删除线。(反正没找到简单方法,但在QT里很简单)。
下面是选中节点的判断监听类:
public class CheckboxNodeCheckedListener extends MouseAdapter implements MouseListener {
@Override
public void mousePressed(MouseEvent e) {
JTree tree = (JTree) e.getSource();
TreePath clickedPath = tree.getPathForLocation(e.getX(), e.getY());
if (e.getClickCount() == 1) {
if (clickedPath != null && !tree.isEditing()) {
Object node = clickedPath.getLastPathComponent();
if (node instanceof CheckboxTreeNode) {
CheckboxTreeNode treeNode = (CheckboxTreeNode) node;
DefaultTreeModel model = (DefaultTreeModel) tree.getModel();
TreeNode[] currPath = model.getPathToRoot(treeNode);
Rectangle rec = tree.getPathBounds(new TreePath(currPath));
if (e.getX() - rec.getX() <= 13) { // 默认为 13x13大小
treeNode.setChecked(!treeNode.isChecked());
model.nodeChanged(treeNode);
}
}
}
}
}
}