树(选择框)

树(选择框)

先是春游然后又开始忙了,都没时间写了,不爽.

JTree的选择框其实也是Renderer的一种表现,单纯实现效果的话很简单,只需要设置Renderer就可以了,但是如果你想实现一个好的JTree选择框就比较难了,因为这里有选择问题、监听问题、选中后的父子关系等,这里主要是参考别人的实现写的.

先看一个简单的例子,从网上看到的,如图:

它只是单纯的实现了树的选择框效果,写的很简单.

首先是TreeNode,我们扩展JavaTreeNode,添加了我们自己的选择属性:

publicclass CheckBoxTreeNode extends DefaultMutableTreeNode {

属性:

    /**

     * is node check

     */

privatebooleanisChecked = false;

然后就是Renderer,这里实现TreeCellRenderer,并继承了JCheckBox

publicclass CheckBoxTreeCellRenderer extends JCheckBox implements

       TreeCellRenderer {

然后实现接口的方法:

    @Override

   public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row,

           boolean hasFocus) {

取得TreeNode

       // get tree node

       CheckBoxTreeNode node = ((CheckBoxTreeNode) value);

       // set check box text

       setText(node.toString());

设置选择状态后返回:

       setSelected(false);

       returnthis;

然后是一个我们自己的JTree,它增加了鼠标监听,实现选择框的勾选效果:

publicclass CheckBoxTree extends JTree {

在构造函数里设置它的Renderer和监听:

    setCellRenderer(new CheckBoxTreeCellRenderer());

    addCheckingListener();

然后是处理监听:

addMouseListener(new MouseAdapter() {

    @Override

    publicvoid mousePressed(MouseEvent e) {

在监听里先取得选择的节点:

        int row = getRowForLocation(e.getX(), e.getY());

       TreePath treePath = getPathForRow(row);

       CheckBoxTreeNode node = ((CheckBoxTreeNode) treePath

           .getLastPathComponent());

然后设置选择状态:

       // if check , will uncheck.

       boolean checking = !node.isChecked();

       node.setChecked(checking);

当然这里可以做额外处理,例如选中节点时同时选择子节点或者父节点:

最后刷新:

       // repaint

       repaint();

然后就是使用了,和一般的JTree基本一致,只是节点是我们自己定义的Node.

    CheckBoxTree tree = new CheckBoxTree();

之后就和一个普通的JTree一样了.

到这里,简单的选择框树就完成了,它基本可以用,但是还是有一些问题的.

因为我们使用JCheckBox作为树的节点,导致我们只能呈现一个选择框和一个文本框的效果,其它复杂效果很难再实现了,简单说就是JCheckBox很难做效果

解决办法就是我们在做Renderer,使用JPanel继承,这样就可以实现更复杂的Node.

要通过鼠标监听和Repaint才能使树选择效果刷新.

通过解决问题一,我们可以在Renderer设置JCheckBox,这样就避免了刷新;同时我们可以额外实现一个单选效果.

选择模式简单(需要鼠标事件),验证数据单一(关联关系不好),封装性不好(使用达不到完全封闭)

这个问题就需要定义接口和数据结构了,本来想自己写呢,后来发现一个老外写了一个,很强大,比我写的好多了,就用它了.

先看我们简单解决12的例子,如图:

TreeNode和前一个例子差不多,我们额外添加了一个选择模式的属性:

publicclass MyTreeNode extends DefaultMutableTreeNode {

两个属性,表示选择和选择模式:

    /** is select or not. */

    privatebooleanisSelected = false;

    /** select model. */

privateintselectionMode = 0;

在设置选择时,如果只允许单选的选择模式,我们设置其它不选择:

    publicvoid setSelected(boolean isSelected) {

        this.isSelected = isSelected;

        if ((selectionMode == TreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION)

             && (children != null)) {

            Enumeration<?> enumTemp = children.elements();

            while (enumTemp.hasMoreElements()) {

                MyTreeNode node = (MyTreeNode) enumTemp.nextElement();

                node.setSelected(isSelected);

            }

        }

    }

然后就是Renderer,这里我们不继承JCheckBox,继承JPanel:

publicclass MyCheckRenderer extends JPanel implements TreeCellRenderer {

JPanel上我们放置了两个组件,当然也可以放置更复杂的:

    /** check box in tree node. */

    private JCheckBox checkBox = null;

    /** label text in tree node. */

    private TreeLabel labelText = null;

其中TreeLabel是我们自己写的:

privateclass TreeLabel extends JLabel {

我们为它添加了焦点状态和选择状态:

    /** is select. */

    privatebooleanisSelected = false;

    /** is have focus. */

    privatebooleanhasFocus = false;

然后复写它的方法和方法,使它的呈现和JTree一致:

    @Override

    public Dimension getPreferredSize() {

    @Override

    publicvoid paint(Graphics g) {

设置颜色和大小:

       g.setColor(UIManager

              .getColor("Tree.selectionBorderColor"));

       g.drawRect(imageOffset, 0, d.width - 1 - imageOffset,

              d.height - 1);

在类里我们实现TreeCellRenderer接口的方法:

    @Override

   public Component getTreeCellRendererComponent(JTree tree, Object value, boolean isSelected, boolean expanded, boolean leaf, int row,

           boolean hasFocus) {

设置JCheckBox的状态:

       checkBox.setSelected(((MyTreeNode) value).isSelected());

设置树节点显示:

       labelText.setFont(tree.getFont());

       labelText.setText(stringValue);

       labelText.setSelected(isSelected);

       labelText.setFocus(hasFocus);

然后复写JPanelgetPreferredSize方法和doLayout方法,使显示合理:

    /**

     * set select node's prefer size.

     */

    @Override

    public Dimension getPreferredSize() {

       Dimension d_check = checkBox.getPreferredSize();

       Dimension d_label = labelText.getPreferredSize();

       returnnew Dimension(d_check.width + d_label.width,

              (d_check.height < d_label.height ? d_label.height

                     : d_check.height));

    }

    /**

     * set tree select node layout.

     */

    @Override

    publicvoid doLayout() {

       Dimension d_check = checkBox.getPreferredSize();

       Dimension d_label = labelText.getPreferredSize();

       int y_check = 0;

       int y_label = 0;

       if (d_check.height < d_label.height) {

           y_check = (d_label.height - d_check.height) / 2;

       } else {

           y_label = (d_check.height - d_label.height) / 2;

       }

       checkBox.setLocation(0, y_check);

        checkBox.setBounds(0, y_check, d_check.width, d_check.height);

       labelText.setLocation(d_check.width, y_label);

       labelText.setBounds(d_check.width, y_label, d_label.width,

              d_label.height);

    }

最后是使用,和前面得差不多,先取得JTree,再设置Renderer,监听鼠标.

        JTree tree = new JTree();

        tree.setCellRenderer(new MyCheckRenderer());

       tree.addMouseListener(new NodeSelectionListener(tree));

处理鼠标监听:

        @Override

        publicvoid mouseClicked(MouseEvent e) {

设置选择:

            MyTreeNode node = (MyTreeNode) path.getLastPathComponent();

            boolean isSelected = !(node.isSelected());

            node.setSelected(isSelected);

            ((DefaultTreeModel) tree.getModel()).nodeChanged(node);

然后和普通的JTree一样使用了.

最后是问题三的解决,这个是一个老外写的,很不错,但是很复杂,如图:

它实现了无关的选中、父选择子全选择、子选择父选择和子单选择父选择四种选择状态.

代码别人写的就不写了,写下它的大概思路:

首先它定义了一个事件: TreeCheckingEvent,这个事件是描绘选择关系和选择路径的,这样就简化了鼠标事件处理;然后是事件的监听器: TreeCheckingListener,它提供监听.

然后定义了一个数据模型:TreeCheckingMode,在模型里它提供了checkPathuncheckPathupdateCheckAfterChildrenInsertedupdateCheckAfterChildrenRemovedupdateCheckAfterStructureChanged的虚方法,供子类实现,这些实现就是树的选择状态的表示,当一个树的节点选择、取消选择、插入、删除和更新之后,节点选择状态的变化.

TreeCheckingModeSimpleTreeCheckingModePropagateTreeCheckingModePropagatePreservingCheckTreeCheckingModePropagatePreservingUncheckTreeCheckingMode四个实现类,代表了四种选择关联状态,通过实现父类的虚方法,当树选择变化或内容变化时,选择节点变化,这样我们的树就可以了四种选择逻辑了,当然你也可以继承TreeCheckingMode实现自己的选择逻辑.

然后还有一个RendererDefaultCheckboxTreeCellRenderer,继承JPanel,实现TreeCellRenderer,来渲染树的节点,这个和我上面写的基本一致.所以大家可以看到,UI最后还是做逻辑,UI呈现也就那么多,还是逻辑多而复杂.

然后是树的CheckModelTreeCheckingModelDefaultTreeCheckingModel,主要是处理树的数据变化和增删改;以及监听和选择状态的记录(树的Model我们还用,在它的基础上添加了新的选择Model,这样就分离了数据和选择状态).
最后是CheckboxTree,它继承JTree,设置ModelTreeModel,设置CheckModelTreeCheckingModel,设置ModeTreeCheckingMode,增加TreeCheckingListener监听,并提供了展开树等事件.

使用很简单,new出来直接使用就可以了,可以设置选择状态:

    tree.getCheckingModel().setCheckingMode(CheckingMode.PROPAGATE);

总之,这个树写的还是不错了,它自己实现了事件和数据模型,这样可以很自由的进行树数据的处理,建议如果大家做大项目的时候,比较常用的组件还是自己实现写,使用自己的事件处理和数据模型甚至UI,这样虽然麻烦,但可以做出更利于自己的效果(当然是很大很复杂的项目,小项目还不够费时间呢).

到此为止,关于树的就写完了,除了一个DND拖拽应该没有漏什么东西了,树的组件并不复杂, 方法不算太多,UI可以重写的也很少,Mdoel因为数据集简单也不复杂,数据处理最多也就是个递归,监听也就是鼠标和树选择、展开事件,因此一般树的UI和事件处理不是我们的重点,我在这里写的可能大多数项目都不会用到,(至少我很少用).因此对树的处理大多还是逻辑,主要是生成树、更新和删除节点.



你可能感兴趣的:(树(选择框))