在使用Java Swing开发微信牛牛房卡开发(h5.fanshubbs.com)程序时,很有可能会遇到使用带复选框的微信牛牛房卡开发的需求,但是Java Swing并没有提供这个组件,因此如果你有这个需求,你就得自己动身实现带复选框的树。
CheckBoxTree与JTree在两个层面上存在差异:
[li]在模型层上,CheckBoxTree的每个结点需要一个成员来保存其是否被选中,但是JTree的结点则不需要。[/li][li]在视图层上,CheckBoxTree的每个结点比JTree的结点多显示一个微信牛牛房卡开发。[/li]
既然存在两个差异,那么只要我们把这两个差异部分通过自己的实现填补上,那么带复选框的微信牛牛房卡开发也就实现了。
现在开始解决第一个差异。为了解决第一个差异,需要定义一个新的结点类CheckBoxTreeNode,该类继承DefaultMutableTreeNode,并增加新的成员isSelected来表示该结点是否被选中。对于一颗CheckBoxTree,如果某一个结点被选中的话,其复选框会勾选上,并且使用CheckBoxTree的动机在于可以一次性地选中一颗微信牛牛房卡开发。那么,在选中或取消一个结点时,其祖先结点和子孙结点应该做出某种变化。在此,我们应用如下递归规则:
[li]如果某个结点被手动选中,那么它的所有子孙结点都应该被选中;如果选中该结点使其父节点的所有子结点都被选中,则选中其父结点。[/li][li]如果某个结点被手动取消选中,那么它的所有子孙结点都应该被取消选中;如果该结点的父结点处于选中状态,则取消选中其父结点。[/li]
注意:上面的两条规则是递归规则,当某个结点发生变化,导致另外的结点发生变化时,另外的结点也会导致其他的结点发生变化。在上面两条规则中,强调手动,是因为手动选中或者手动取消选中一个结点,会导致其他结点发生非手动的选中或者取消选中,这种非手动导致的选中或者非取消选中则不适用于上述规则。
按照上述微信牛牛房卡开发规则实现的CheckBoxTreeNode源代码如下:
[li]package demo;??[/li][li]??[/li][li]import javax.swing.tree.DefaultMutableTreeNode;??[/li][li]??[/li][li]public class CheckBoxTreeNode extends DefaultMutableTreeNode??[/li][li]{??[/li][li]? ? protected boolean isSelected;??[/li][li]? ?? ?[/li][li]? ? public CheckBoxTreeNode()??[/li][li]? ? {??[/li][li]? ?? ???this(null);??[/li][li]? ? }??[/li][li]? ?? ?[/li][li]? ? public CheckBoxTreeNode(Object userObject)??[/li][li]? ? {??[/li][li]? ?? ???this(userObject, true, false);??[/li][li]? ? }??[/li][li]? ?? ?[/li][li]? ? public CheckBoxTreeNode(Object userObject, boolean allowsChildren, boolean isSelected)??[/li][li]? ? {??[/li][li]? ?? ???super(userObject, allowsChildren);??[/li][li]? ?? ???this.isSelected = isSelected;??[/li][li]? ? }??[/li][li]??[/li][li]? ? public boolean isSelected()??[/li][li]? ? {??[/li][li]? ?? ???return isSelected;??[/li][li]? ? }??[/li][li]? ?? ?[/li][li]? ? public void setSelected(boolean _isSelected)??[/li][li]? ? {??[/li][li]? ?? ???this.isSelected = _isSelected;??[/li][li]? ?? ?? ? [/li][li]? ?? ???if(_isSelected)??[/li][li]? ?? ???{??[/li][li]? ?? ?? ?? ?// 如果选中,则将其所有的子结点都选中??[/li][li]? ?? ?? ?? ?if(children != null)??[/li][li]? ?? ?? ?? ?{??[/li][li]? ?? ?? ?? ?? ? for(Object obj : children)??[/li][li]? ?? ?? ?? ?? ? {??[/li][li]? ?? ?? ?? ?? ?? ???CheckBoxTreeNode node = (CheckBoxTreeNode)obj;??[/li][li]? ?? ?? ?? ?? ?? ???if(_isSelected != node.isSelected())??[/li][li]? ?? ?? ?? ?? ?? ?? ?? ?node.setSelected(_isSelected);??[/li][li]? ?? ?? ?? ?? ? }??[/li][li]? ?? ?? ?? ?}??[/li][li]? ?? ?? ?? ?// 向上检查,如果父结点的所有子结点都被选中,那么将父结点也选中??[/li][li]? ?? ?? ?? ?CheckBoxTreeNode pNode = (CheckBoxTreeNode)parent;??[/li][li]? ?? ?? ?? ?// 开始检查pNode的所有子节点是否都被选中??[/li][li]? ?? ?? ?? ?if(pNode != null)??[/li][li]? ?? ?? ?? ?{??[/li][li]? ?? ?? ?? ?? ? int index = 0;??[/li][li]? ?? ?? ?? ?? ? for(; index < pNode.children.size(); ++ index)??[/li][li]? ?? ?? ?? ?? ? {??[/li][li]? ?? ?? ?? ?? ?? ???CheckBoxTreeNode pChildNode = (CheckBoxTreeNode)pNode.children.get(index);??[/li][li]? ?? ?? ?? ?? ?? ???if(!pChildNode.isSelected())??[/li][li]? ?? ?? ?? ?? ?? ?? ?? ?break;??[/li][li]? ?? ?? ?? ?? ? }??[/li][li]? ?? ?? ?? ?? ? /??[/li][li]? ?? ?? ?? ?? ??? 表明pNode所有子结点都已经选中,则选中父结点, [/li][li]? ?? ?? ?? ?? ??? 该方法是一个递归方法,因此在此不需要进行迭代,因为 [/li][li]? ?? ?? ?? ?? ??? 当选中父结点后,父结点本身会向上检查的。 [/li][li]? ?? ?? ?? ?? ???/??[/li][li]? ?? ?? ?? ?? ? if(index == pNode.children.size())??[/li][li]? ?? ?? ?? ?? ? {??[/li][li]? ?? ?? ?? ?? ?? ???if(pNode.isSelected() != _isSelected)??[/li][li]? ?? ?? ?? ?? ?? ?? ?? ?pNode.setSelected(_isSelected);??[/li][li]? ?? ?? ?? ?? ? }??[/li][li]? ?? ?? ?? ?}??[/li][li]? ?? ???}??[/li][li]? ?? ???else? ?[/li][li]? ?? ???{??[/li][li]? ?? ?? ?? ?/ [/li][li]? ?? ?? ?? ? 如果是取消父结点导致子结点取消,那么此时所有的子结点都应该是选择上的; [/li][li]? ?? ?? ?? ? 否则就是子结点取消导致父结点取消,然后父结点取消导致需要取消子结点,但 [/li][li]? ?? ?? ?? ? 是这时候是不需要取消子结点的。 [/li][li]? ?? ?? ?? ? /??[/li][li]? ?? ?? ?? ?if(children != null)??[/li][li]? ?? ?? ?? ?{??[/li][li]? ?? ?? ?? ?? ? int index = 0;??[/li][li]? ?? ?? ?? ?? ? for(; index < children.size(); ++ index)??[/li][li]? ?? ?? ?? ?? ? {??[/li][li]? ?? ?? ?? ?? ?? ???CheckBoxTreeNode childNode = (CheckBoxTreeNode)children.get(index);??[/li][li]? ?? ?? ?? ?? ?? ???if(!childNode.isSelected())??[/li][li]? ?? ?? ?? ?? ?? ?? ?? ?break;??[/li][li]? ?? ?? ?? ?? ? }??[/li][li]? ?? ?? ?? ?? ? // 从上向下取消的时候??[/li][li]? ?? ?? ?? ?? ? if(index == children.size())??[/li][li]? ?? ?? ?? ?? ? {??[/li][li]? ?? ?? ?? ?? ?? ???for(int i = 0; i < children.size(); ++ i)??[/li][li]? ?? ?? ?? ?? ?? ???{??[/li][li]? ?? ?? ?? ?? ?? ?? ?? ?CheckBoxTreeNode node = (CheckBoxTreeNode)children.get(i);??[/li][li]? ?? ?? ?? ?? ?? ?? ?? ?if(node.isSelected() != _isSelected)??[/li][li]? ?? ?? ?? ?? ?? ?? ?? ?? ? node.setSelected(_isSelected);??[/li][li]? ?? ?? ?? ?? ?? ???}??[/li][li]? ?? ?? ?? ?? ? }??[/li][li]? ?? ?? ?? ?}??[/li][li]? ?? ?? ?? ???[/li][li]? ?? ?? ?? ?// 向上取消,只要存在一个子节点不是选上的,那么父节点就不应该被选上。??[/li][li]? ?? ?? ?? ?CheckBoxTreeNode pNode = (CheckBoxTreeNode)parent;??[/li][li]? ?? ?? ?? ?if(pNode != null ;; pNode.isSelected() != _isSelected)??[/li][li]? ?? ?? ?? ?? ? pNode.setSelected(_isSelected);??[/li][li]? ?? ???}??[/li][li]? ? }??[/li][li]}??[/li]
package demo;import javax.swing.tree.DefaultMutableTreeNode;public class CheckBoxTreeNode extends DefaultMutableTreeNode{protected boolean isSelected;public CheckBoxTreeNode(){this(null);}public CheckBoxTreeNode(Object userObject){this(userObject, true, false);}public CheckBoxTreeNode(Object userObject, boolean allowsChildren, boolean isSelected){super(userObject, allowsChildren);this.isSelected = isSelected;}public boolean isSelected(){return isSelected;}public void setSelected(boolean _isSelected){this.isSelected = _isSelected;if(_isSelected){// 如果选中,则将其所有的子结点都选中if(children != null){for(Object obj : children){CheckBoxTreeNode node = (CheckBoxTreeNode)obj;if(_isSelected != node.isSelected())node.setSelected(_isSelected);}}// 向上检查,如果父结点的所有子结点都被选中,那么将父结点也选中CheckBoxTreeNode pNode = (CheckBoxTreeNode)parent;// 开始检查pNode的所有子节点是否都被选中if(pNode != null){int index = 0;for(; index < pNode.children.size(); ++ index){CheckBoxTreeNode pChildNode = (CheckBoxTreeNode)pNode.children.get(index);if(!pChildNode.isSelected())break;}/* 表明pNode所有子结点都已经选中,则选中父结点, 该方法是一个递归方法,因此在此不需要进行迭代,因为 当选中父结点后,父结点本身会向上检查的。/if(index == pNode.children.size()){if(pNode.isSelected() != _isSelected)pNode.setSelected(_isSelected);}}}else{/* 如果是取消父结点导致子结点取消,那么此时所有的子结点都应该是选择上的; 否则就是子结点取消导致父结点取消,然后父结点取消导致需要取消子结点,但 是这时候是不需要取消子结点的。/if(children != null){int index = 0;for(; index < children.size(); ++ index){CheckBoxTreeNode childNode = (CheckBoxTreeNode)children.get(index);if(!childNode.isSelected())break;}// 从上向下取消的时候if(index == children.size()){for(int i = 0; i < children.size(); ++ i){CheckBoxTreeNode node = (CheckBoxTreeNode)children.get(i);if(node.isSelected() != _isSelected)node.setSelected(_isSelected);}}}// 向上取消,只要存在一个子节点不是选上的,那么父节点就不应该被选上。CheckBoxTreeNode pNode = (CheckBoxTreeNode)parent;if(pNode != null ;; pNode.isSelected() != _isSelected)pNode.setSelected(_isSelected);}}}
第一个差异通过继承DefaultMutableTreeNode定义CheckBoxTreeNode解决了,接下来需要解决第二个差异。第二个差异是外观上的差异,JTree的每个结点是通过TreeCellRenderer进行显示的。为了解决第二个差异,我们定义一个新的类CheckBoxTreeCellRenderer,该类实现了TreeCellRenderer接口。CheckBoxTreeRenderer的源代码如下:
[li]package demo;??[/li][li]??[/li][li]import java.awt.Color;??[/li][li]import java.awt.Component;??[/li][li]import java.awt.Dimension;??[/li][li]??[/li][li]import javax.swing.JCheckBox;??[/li][li]import javax.swing.JPanel;??[/li][li]import javax.swing.JTree;??[/li][li]import javax.swing.UIManager;??[/li][li]import javax.swing.plaf.ColorUIResource;??[/li][li]import javax.swing.tree.TreeCellRenderer;??[/li][li]??[/li][li]public class CheckBoxTreeCellRenderer extends JPanel implements TreeCellRenderer??[/li][li]{??[/li][li]? ? protected JCheckBox check;??[/li][li]? ? protected CheckBoxTreeLabel label;??[/li][li]? ?? ?[/li][li]? ? public CheckBoxTreeCellRenderer()??[/li][li]? ? {??[/li][li]? ?? ???setLayout(null);??[/li][li]? ?? ???add(check = new JCheckBox());??[/li][li]? ?? ???add(label = new CheckBoxTreeLabel());??[/li][li]? ?? ???check.setBackground(UIManager.getColor("Tree.textBackground"));??[/li][li]? ?? ???label.setForeground(UIManager.getColor("Tree.textForeground"));??[/li][li]? ? }??[/li][li]? ?? ?[/li][li]? ? /* [/li][li]? ??? 返回的是一个<code>JPanel</code>对象,该对象中包含一个<code>JCheckBox</code>对象 [/li][li]? ??? 和一个<code>JLabel</code>对象。并且根据每个结点是否被选中来决定<code>JCheckBox</code> [/li][li]? ??? 是否被选中。 [/li][li]? ???*/??[/li][li]? ? @Override??[/li][li]? ? public Component getTreeCellRendererComponent(JTree tree, Object value,??[/li][li]? ?? ?? ?? ?boolean selected, boolean expanded, boolean leaf, int row,??[/li][li]? ?? ?? ?? ?boolean hasFocus)??[/li][li]? ? {??[/li][li]? ?? ???String stringValue = tree.convertValueToText(value, selected, expanded, leaf, row, hasFocus);??[/li][li]? ?? ???setEnabled(tree.isEnabled());??[/li][li]? ?? ???check.setSelected(((CheckBoxTreeNode)value).isSelected());??[/li][li]? ?? ???label.setFont(tree.getFont());??[/li][li]? ?? ???label.setText(stringValue);??[/li][li]? ?? ???label.setSelected(selected);??[/li][li]? ?? ???label.setFocus(hasFocus);??[/li][li]? ?? ???if(leaf)??[/li][li]? ?? ?? ?? ?label.setIcon(UIManager.getIcon("Tree.leafIcon"));??[/li][li]? ?? ???else if(expanded)??[/li][li]? ?? ?? ?? ?label.setIcon(UIManager.getIcon("Tree.openIcon"));??[/li][li]? ?? ???else??[/li][li]? ?? ?? ?? ?label.setIcon(UIManager.getIcon("Tree.closedIcon"));??[/li][li]? ?? ?? ?? ???[/li][li]? ?? ???return this;??[/li][li]? ? }??[/li][li]??[/li][li]? ? @Override??[/li][li]? ? public Dimension getPreferredSize()??[/li][li]? ? {??[/li][li]? ?? ???Dimension dCheck = check.getPreferredSize();??[/li][li]? ?? ???Dimension dLabel = label.getPreferredSize();??[/li][li]? ?? ???return new Dimension(dCheck.width + dLabel.width, dCheck.height < dLabel.height ? dLabel.height: dCheck.height);??[/li][li]? ? }??[/li][li]? ?? ?[/li][li]? ? @Override??[/li][li]? ? public void doLayout()??[/li][li]? ? {??[/li][li]? ?? ???Dimension dCheck = check.getPreferredSize();??[/li][li]? ?? ???Dimension dLabel = label.getPreferredSize();??[/li][li]? ?? ???int yCheck = 0;??[/li][li]? ?? ???int yLabel = 0;??[/li][li]? ?? ???if(dCheck.height < dLabel.height)??[/li][li]? ?? ?? ?? ?yCheck = (dLabel.height - dCheck.height) / 2;??[/li][li]? ?? ???else??[/li][li]? ?? ?? ?? ?yLabel = (dCheck.height - dLabel.height) / 2;??[/li][li]? ?? ???check.setLocation(0, yCheck);??[/li][li]? ?? ???check.setBounds(0, yCheck, dCheck.width, dCheck.height);??[/li][li]? ?? ???label.setLocation(dCheck.width, yLabel);??[/li][li]? ?? ???label.setBounds(dCheck.width, yLabel, dLabel.width, dLabel.height);??[/li][li]? ? }??[/li][li]? ?? ?[/li][li]? ? @Override??[/li][li]? ? public void setBackground(Color color)??[/li][li]? ? {??[/li][li]? ?? ???if(color instanceof ColorUIResource)??[/li][li]? ?? ?? ?? ?color = null;??[/li][li]? ?? ???super.setBackground(color);??[/li][li]? ? }??[/li][li]}??[/li]
package demo;import java.awt.Color;import java.awt.Component;import java.awt.Dimension;import javax.swing.JCheckBox;import javax.swing.JPanel;import javax.swing.JTree;import javax.swing.UIManager;import javax.swing.plaf.ColorUIResource;import javax.swing.tree.TreeCellRenderer;public class CheckBoxTreeCellRenderer extends JPanel implements TreeCellRenderer{protected JCheckBox check;protected CheckBoxTreeLabel label;public CheckBoxTreeCellRenderer(){setLayout(null);add(check = new JCheckBox());add(label = new CheckBoxTreeLabel());check.setBackground(UIManager.getColor("Tree.textBackground"));label.setForeground(UIManager.getColor("Tree.textForeground"));}/** 返回的是一个<code>JPanel</code>对象,该对象中包含一个<code>JCheckBox</code>对象 和一个<code>JLabel</code>对象。并且根据每个结点是否被选中来决定<code>JCheckBox</code> 是否被选中。/@Overridepublic 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).isSelected());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"));elselabel.setIcon(UIManager.getIcon("Tree.closedIcon"));return this;}@Overridepublic Dimension getPreferredSize(){Dimension dCheck = check.getPreferredSize();Dimension dLabel = label.getPreferredSize();return new Dimension(dCheck.width + dLabel.width, dCheck.height < dLabel.height ? dLabel.height: dCheck.height);}@Overridepublic 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;elseyLabel = (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);}@Overridepublic void setBackground(Color color){if(color instanceof ColorUIResource)color = null;super.setBackground(color);}}在CheckBoxTreeCellRenderer的实现中,为了处理背景色等问题,我们重新实现了微信牛牛房卡开发一个JLabel的子类CheckBoxTreeLabel,其源代码如下:
[li]package demo;??[/li][li]??[/li][li]import java.awt.Color;??[/li][li]import java.awt.Dimension;??[/li][li]import java.awt.Graphics;??[/li][li]??[/li][li]import javax.swing.Icon;??[/li][li]import javax.swing.JLabel;??[/li][li]import javax.swing.UIManager;??[/li][li]import javax.swing.plaf.ColorUIResource;??[/li][li]??[/li][li]public class CheckBoxTreeLabel extends JLabel??[/li][li]{??[/li][li]? ? private boolean isSelected;??[/li][li]? ? private boolean hasFocus;??[/li][li]? ?? ?[/li][li]? ? public CheckBoxTreeLabel()??[/li][li]? ? {??[/li][li]? ? }??[/li][li]? ?? ?[/li][li]? ? @Override??[/li][li]? ? public void setBackground(Color color)??[/li][li]? ? {??[/li][li]? ?? ???if(color instanceof ColorUIResource)??[/li][li]? ?? ?? ?? ?color = null;??[/li][li]? ?? ???super.setBackground(color);??[/li][li]? ? }??[/li][li]? ?? ?[/li][li]? ? @Override??[/li][li]? ? public void paint(Graphics g)??[/li][li]? ? {??[/li][li]? ?? ???String str;??[/li][li]? ?? ???if((str = getText()) != null)??[/li][li]? ?? ???{??[/li][li]? ?? ?? ?? ?if(0 < str.length())??[/li][li]? ?? ?? ?? ?{??[/li][li]? ?? ?? ?? ?? ? if(isSelected)??[/li][li]? ?? ?? ?? ?? ?? ???g.setColor(UIManager.getColor("Tree.selectionBackground"));??[/li][li]? ?? ?? ?? ?? ? else??[/li][li]? ?? ?? ?? ?? ?? ???g.setColor(UIManager.getColor("Tree.textBackground"));??[/li][li]? ?? ?? ?? ?? ? Dimension d = getPreferredSize();??[/li][li]? ?? ?? ?? ?? ? int imageOffset = 0;??[/li][li]? ?? ?? ?? ?? ? Icon currentIcon = getIcon();??[/li][li]? ?? ?? ?? ?? ? if(currentIcon != null)??[/li][li]? ?? ?? ?? ?? ?? ???imageOffset = currentIcon.getIconWidth() + Math.max(0, getIconTextGap() - 1);??[/li][li]? ?? ?? ?? ?? ? g.fillRect(imageOffset, 0, d.width - 1 - imageOffset, d.height);??[/li][li]? ?? ?? ?? ?? ? if(hasFocus)??[/li][li]? ?? ?? ?? ?? ? {??[/li][li]? ?? ?? ?? ?? ?? ???g.setColor(UIManager.getColor("Tree.selectionBorderColor"));??[/li][li]? ?? ?? ?? ?? ?? ???g.drawRect(imageOffset, 0, d.width - 1 - imageOffset, d.height - 1);??[/li][li]? ?? ?? ?? ?? ? }??[/li][li]? ?? ?? ?? ?}??[/li][li]? ?? ???}??[/li][li]? ?? ???super.paint(g);??[/li][li]? ? }??[/li][li]? ?? ?[/li][li]? ? @Override??[/li][li]? ? public Dimension getPreferredSize()??[/li][li]? ? {??[/li][li]? ?? ???Dimension retDimension = super.getPreferredSize();??[/li][li]? ?? ???if(retDimension != null)??[/li][li]? ?? ?? ?? ?retDimension = new Dimension(retDimension.width + 3, retDimension.height);??[/li][li]? ?? ???return retDimension;??[/li][li]? ? }??[/li][li]? ?? ?[/li][li]? ? public void setSelected(boolean isSelected)??[/li][li]? ? {??[/li][li]? ?? ???this.isSelected = isSelected;??[/li][li]? ? }??[/li][li]? ?? ?[/li][li]? ? public void setFocus(boolean hasFocus)??[/li][li]? ? {??[/li][li]? ?? ???this.hasFocus = hasFocus;??[/li][li]? ? }??[/li][li]}??[/li]
package demo;import java.awt.Color;import java.awt.Dimension;import java.awt.Graphics;import javax.swing.Icon;import javax.swing.JLabel;import javax.swing.UIManager;import javax.swing.plaf.ColorUIResource;public class CheckBoxTreeLabel extends JLabel{private boolean isSelected;private boolean hasFocus;public CheckBoxTreeLabel(){}@Overridepublic void setBackground(Color color){if(color instanceof ColorUIResource)color = null;super.setBackground(color);}@Overridepublic void paint(Graphics g){String str;if((str = getText()) != null){if(0 < str.length()){if(isSelected)g.setColor(UIManager.getColor("Tree.selectionBackground"));elseg.setColor(UIManager.getColor("Tree.textBackground"));Dimension d = getPreferredSize();int imageOffset = 0;Icon currentIcon = getIcon();if(currentIcon != null)imageOffset = currentIcon.getIconWidth() + Math.max(0, getIconTextGap() - 1);g.fillRect(imageOffset, 0, d.width - 1 - imageOffset, d.height);if(hasFocus){g.setColor(UIManager.getColor("Tree.selectionBorderColor"));g.drawRect(imageOffset, 0, d.width - 1 - imageOffset, d.height - 1);}}}super.paint(g);}@Overridepublic Dimension getPreferredSize(){Dimension retDimension = super.getPreferredSize();if(retDimension != null)retDimension = new Dimension(retDimension.width + 3, retDimension.height);return retDimension;}public void setSelected(boolean isSelected){this.isSelected = isSelected;}public void setFocus(boolean hasFocus){this.hasFocus = hasFocus;}}
通过定义CheckBoxTreeNode和CheckBoxTreeCellRenderer。我们解决了CheckBoxTree和JTree的两个根本差异,但是还有一个细节问题需要解决,就是CheckBoxTree可以响应用户事件决定是否选中某个结点。为此,我们为CheckBoxTree添加一个响应用户鼠标事件的监听器CheckBoxTreeNodeSelectionListener,该类的源代码如下:
[li]package demo;??[/li][li]??[/li][li]import java.awt.event.MouseAdapter;??[/li][li]import java.awt.event.MouseEvent;??[/li][li]??[/li][li]import javax.swing.JTree;??[/li][li]import javax.swing.tree.TreePath;??[/li][li]import javax.swing.tree.DefaultTreeModel;??[/li][li]??[/li][li]public class CheckBoxTreeNodeSelectionListener extends MouseAdapter??[/li][li]{??[/li][li]? ? @Override??[/li][li]? ? public void mouseClicked(MouseEvent event)??[/li][li]? ? {??[/li][li]? ?? ???JTree tree = (JTree)event.getSource();??[/li][li]? ?? ???int x = event.getX();??[/li][li]? ?? ???int y = event.getY();??[/li][li]? ?? ???int row = tree.getRowForLocation(x, y);??[/li][li]? ?? ???TreePath path = tree.getPathForRow(row);??[/li][li]? ?? ???if(path != null)??[/li][li]? ?? ???{??[/li][li]? ?? ?? ?? ?CheckBoxTreeNode node = (CheckBoxTreeNode)path.getLastPathComponent();??[/li][li]? ?? ?? ?? ?if(node != null)??[/li][li]? ?? ?? ?? ?{??[/li][li]? ?? ?? ?? ?? ? boolean isSelected = !node.isSelected();??[/li][li]? ?? ?? ?? ?? ? node.setSelected(isSelected);??[/li][li]? ?? ?? ?? ?? ? ((DefaultTreeModel)tree.getModel()).nodeStructureChanged(node);??[/li][li]? ?? ?? ?? ?}??[/li][li]? ?? ???}??[/li][li]? ? }??[/li][li]}??[/li]
package demo;import java.awt.event.MouseAdapter;import java.awt.event.MouseEvent;import javax.swing.JTree;import javax.swing.tree.TreePath;import javax.swing.tree.DefaultTreeModel;public class CheckBoxTreeNodeSelectionListener extends MouseAdapter{@Overridepublic void mouseClicked(MouseEvent event){JTree tree = (JTree)event.getSource();int x = event.getX();int y = event.getY();int row = tree.getRowForLocation(x, y);TreePath path = tree.getPathForRow(row);if(path != null){CheckBoxTreeNode node = (CheckBoxTreeNode)path.getLastPathComponent();if(node != null){boolean isSelected = !node.isSelected();node.setSelected(isSelected);((DefaultTreeModel)tree.getModel()).nodeStructureChanged(node);}}}}到此为止,CheckBoxTree所需要的所有组件都已经完成了,接下来就是如何使用这些组件。下面给出了使用微信牛牛房卡开发这些组件的源代码:
[li]package demo;??[/li][li]??[/li][li]import javax.swing.JFrame;??[/li][li]import javax.swing.JScrollPane;??[/li][li]import javax.swing.JTree;??[/li][li]import javax.swing.tree.DefaultTreeModel;??[/li][li]??[/li][li]public class DemoMain? ?[/li][li]{??[/li][li]? ? public static void main(String[] args)??[/li][li]? ? {??[/li][li]? ?? ???JFrame frame = new JFrame("CheckBoxTreeDemo");??[/li][li]? ?? ???frame.setBounds(200, 200, 400, 400);??[/li][li]? ?? ???JTree tree = new JTree();??[/li][li]? ?? ???CheckBoxTreeNode rootNode = new CheckBoxTreeNode("root");??[/li][li]? ?? ???CheckBoxTreeNode node1 = new CheckBoxTreeNode("node_1");??[/li][li]? ?? ???CheckBoxTreeNode node1_1 = new CheckBoxTreeNode("node_1_1");??[/li][li]? ?? ???CheckBoxTreeNode node1_2 = new CheckBoxTreeNode("node_1_2");??[/li][li]? ?? ???CheckBoxTreeNode node1_3 = new CheckBoxTreeNode("node_1_3");??[/li][li]? ?? ???node1.add(node1_1);??[/li][li]? ?? ???node1.add(node1_2);??[/li][li]? ?? ???node1.add(node1_3);??[/li][li]? ?? ???CheckBoxTreeNode node2 = new CheckBoxTreeNode("node_2");??[/li][li]? ?? ???CheckBoxTreeNode node2_1 = new CheckBoxTreeNode("node_2_1");??[/li][li]? ?? ???CheckBoxTreeNode node2_2 = new CheckBoxTreeNode("node_2_2");??[/li][li]? ?? ???node2.add(node2_1);??[/li][li]? ?? ???node2.add(node2_2);??[/li][li]? ?? ???rootNode.add(node1);??[/li][li]? ?? ???rootNode.add(node2);??[/li][li]? ?? ???DefaultTreeModel model = new DefaultTreeModel(rootNode);??[/li][li]? ?? ???tree.addMouseListener(new CheckBoxTreeNodeSelectionListener());??[/li][li]? ?? ???tree.setModel(model);??[/li][li]? ?? ???tree.setCellRenderer(new CheckBoxTreeCellRenderer());??[/li][li]? ?? ???JScrollPane scroll = new JScrollPane(tree);??[/li][li]? ?? ???scroll.setBounds(0, 0, 300, 320);??[/li][li]? ?? ???frame.getContentPane().add(scroll);??[/li][li]? ?? ?? ? [/li][li]? ?? ???frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);??[/li][li]? ?? ???frame.setVisible(true);??[/li][li]? ? }??[/li][li]}??[/li]
package demo;import javax.swing.JFrame;import javax.swing.JScrollPane;import javax.swing.JTree;import javax.swing.tree.DefaultTreeModel;public class DemoMain{public static void main(String[] args){JFrame frame = new JFrame("CheckBoxTreeDemo");frame.setBounds(200, 200, 400, 400);JTree tree = new JTree();CheckBoxTreeNode rootNode = new CheckBoxTreeNode("root");CheckBoxTreeNode node1 = new CheckBoxTreeNode("node_1");CheckBoxTreeNode node1_1 = new CheckBoxTreeNode("node_1_1");CheckBoxTreeNode node1_2 = new CheckBoxTreeNode("node_1_2");CheckBoxTreeNode node1_3 = new CheckBoxTreeNode("node_1_3");node1.add(node1_1);node1.add(node1_2);node1.add(node1_3);CheckBoxTreeNode node2 = new CheckBoxTreeNode("node_2");CheckBoxTreeNode node2_1 = new CheckBoxTreeNode("node_2_1");CheckBoxTreeNode node2_2 = new CheckBoxTreeNode("node_2_2");node2.add(node2_1);node2.add(node2_2);rootNode.add(node1);rootNode.add(node2);DefaultTreeModel model = new DefaultTreeModel(rootNode);tree.addMouseListener(new CheckBoxTreeNodeSelectionListener());tree.setModel(model);tree.setCellRenderer(new CheckBoxTreeCellRenderer());JScrollPane scroll = new JScrollPane(tree);scroll.setBounds(0, 0, 300, 320);frame.getContentPane().add(scroll);frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);frame.setVisible(true);}}其执行微信牛牛房卡开发结果:
原文地址:http://blog.51cto.com/13566796/2058685