Вопрос

Я пытаюсь изменить стандартный Swing JTree, чтобы смешивать узлы с флажками и без них.Это пример:

alt text

Когда я пытаюсь установить / снять один из флажков (узел 'User 01' в этом примере), дерево теряет узлы:

alt text

I мой код является адаптацией этого примера: http://forums.sun.com/thread.jspa?threadID=5321084&start=13.

Вместо встраивания JCheckBox в DefaultMutableTreeNode, подобный этому:

new DefaultMutableTreeNode(new CheckBoxNode("Accessibility", true));

Я подумал, что имеет больше смысла создать узел модели, производный от DefaultMutableTreeNode, который я называю JTreeNode.Этот класс автоматически устанавливает UserObject для DefaultMutableTreeNode по умолчанию в JCheckBox.Свойство ShowCheckBox класса используется TreeCellRenderer для определения, используется ли JCheckBox или DefaultTreeCellRenderer .JTreeNode используется следующим образом:

    JTreeNode user01 = new JTreeNode("User 01");
    user01.setShowCheckBox(true);
    user01.setSelected(true);

Я полагаю, что проблема связана с классом, который реализует TreeCellEditor , в частности, в методах getCellEditorValue() или getTreeCellEditorComponent().Я подозреваю, что проблема связана с getCellEditorValue(), возвращающим производную от DefaultMutableTreeNode , а не более простой экземпляр модели.

public Object getCellEditorValue() {

    JCheckBox checkBox = renderer.getCheckBoxRenderer();

    JTreeNode node = new JTreeNode(checkBox.getText());
    node.setShowCheckBox(true);
    node.setSelected(checkBox.isSelected());
    return node;

}

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

    Component editor = renderer.getTreeCellRendererComponent(tree, value, true, expanded, leaf, row, true);

    // editor always selected / focused
    ItemListener itemListener = new ItemListener() {
        public void itemStateChanged(ItemEvent itemEvent) {
            if (stopCellEditing()) {
                fireEditingStopped();
            }
        }
    };

    if (editor instanceof JCheckBox) {
        ((JCheckBox) editor).addItemListener(itemListener);
    }

    return editor;

}

Вот метод treecellrender's getTreeCellRendererComponent() от TreeCellRender:

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

    Component component;

    //if object being passed for rendering is a JTreeNode that should show a JCheckBox, attempt to render it so
    if (((JTreeNode) value).getShowCheckBox()) {

        String stringValue = tree.convertValueToText(value, selected, expanded, leaf, row, false);

        //set default JCheckBox rendering
        checkBoxRenderer.setText(stringValue);
        checkBoxRenderer.setSelected(false);    //not checked
        checkBoxRenderer.setEnabled(tree.isEnabled());

        if (selected) {
            //removed colorization
            //checkBoxRenderer.setForeground(selectionForeground);
            //checkBoxRenderer.setBackground(selectionBackground);
        }
        else {
            checkBoxRenderer.setForeground(textForeground);
            checkBoxRenderer.setBackground(textBackground);
        }

        //DefaultMutableTreeNode
        if ((value != null) && (value instanceof JTreeNode)) {

            //userObject should be a JTreeNode instance
            //DefaultMutableTreeNode
            //Object userObject = ((JTreeNode) value).getUserObject();

            //if it is
            //if (userObject instanceof JTreeNode) {
                //cast as a JTreeNode
                //JTreeNode node = (JTreeNode) userObject;
                JTreeNode node = (JTreeNode) value;

                //set JCheckBox settings to match JTreeNode's settings
                checkBoxRenderer.setText(node.getText());
                checkBoxRenderer.setSelected(node.isSelected());

            //}

        }

        component = checkBoxRenderer;

    }

    //if not, render the default
    else {

        component = defaultRenderer.getTreeCellRendererComponent(tree, value, selected, expanded, leaf, row, hasFocus);

    }

    return component;

}

Любые мысли очень ценятся.

Это было полезно?

Решение

Я смог решить эту проблему.

alt text

Я создал класс модели (TreeNodeModel) для хранения соответствующих данных узла:ключ, значение, флажок установлен, выбран:

public class TreeNodeModel {

    int key;
    String value;
    boolean isSelected=false;
    boolean hasCheckBox=false;

    public TreeNodeModel() {
    }
    public TreeNodeModel(int key, String value) {
        this.key=key;
        this.value = value;
    }
    public TreeNodeModel(int key, String value, boolean hasCheckBox) {
        this.key=key;
        this.value = value;
        this.hasCheckBox = hasCheckBox;
    }
    public TreeNodeModel(int key, String value, boolean hasCheckBox, boolean isSelected) {
        this.key=key;
        this.value = value;
        this.hasCheckBox=hasCheckBox;
        this.isSelected = isSelected;
    }

    public boolean isSelected() {
        return this.isSelected;
    }
    public void setSelected(boolean newValue) {
        this.isSelected = newValue;
    }

    public boolean hasCheckBox() {
        return this.hasCheckBox;
    }
    public void setCheckBox(boolean newValue) {
        this.hasCheckBox=newValue;
    }

    public int getKey() {
        return this.key;
    }
    public void setKey(int newValue) {
        this.key = newValue;
    }

    public String getValue() {
        return this.value;
    }
    public void setValue(String newValue) {
        this.value = newValue;
    }

    @Override
    public String toString() {
        return getClass().getName() + "[" + this.value + "/" + this.isSelected + "]";
//        return this.text;
    }

}

Я создал реализацию интерфейса TreeCellEditor:

public class TreeNodeEditor  extends AbstractCellEditor implements TreeCellEditor {

    private JTree tree;
    private TreeNodeModel editedModel = null;

    TreeNodeRenderer renderer = new TreeNodeRenderer();

    public TreeNodeEditor(JTree tree) {
        this.tree=tree;
    }

    @Override
    public boolean isCellEditable(EventObject event) {

        boolean editable=false;

        if (event instanceof MouseEvent) {

            MouseEvent mouseEvent = (MouseEvent) event;
            TreePath path = tree.getPathForLocation(mouseEvent.getX(),mouseEvent.getY());

            if (path != null) {

                Object node = path.getLastPathComponent();

                if ((node != null) && (node instanceof DefaultMutableTreeNode)) {

                    DefaultMutableTreeNode editedNode = (DefaultMutableTreeNode) node;
                    TreeNodeModel model = (TreeNodeModel) editedNode.getUserObject();
                    editable = model.hasCheckBox;

                }   //if (node)
            }   //if (path)
        }   //if (MouseEvent)

        return editable;

    }

    public Object getCellEditorValue() {

        JCheckBox checkbox = renderer.getCheckBoxRenderer();

        TreeNodeModel model = new TreeNodeModel(editedModel.getKey(), checkbox.getText(), true, checkbox.isSelected());
        return model;

    }

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

        if (((DefaultMutableTreeNode) value).getUserObject() instanceof TreeNodeModel) {
            editedModel = (TreeNodeModel) ((DefaultMutableTreeNode) value).getUserObject();
        }

        Component editor = renderer.getTreeCellRendererComponent(tree, value, true, expanded, leaf, row, true);

        // editor always selected / focused
        ItemListener itemListener = new ItemListener() {
            public void itemStateChanged(ItemEvent itemEvent) {
                if (stopCellEditing())
                    fireEditingStopped();
            }
        };

        if (editor instanceof JCheckBox) {
            ((JCheckBox) editor).addItemListener(itemListener);
        }

        return editor;
    }

}

Ключом был захват модели в методе getTreeCellEditorComponent() и использование ее ключа в методе getCellEditorValue().

Построить дерево было легко:

DefaultMutableTreeNode server = new DefaultMutableTreeNode(new TreeNodeModel(0,"Server 01", false));

DefaultMutableTreeNode userFolder = new DefaultMutableTreeNode(new TreeNodeModel(1, "User Folders", false));
server.add(userFolder);

DefaultMutableTreeNode user01 =  new DefaultMutableTreeNode(new TreeNodeModel(2, "User 01", true, true));
userFolder.add(user01);

DefaultMutableTreeNode clientA = new DefaultMutableTreeNode(new TreeNodeModel(3, "Client A", true, true));
user01.add(clientA);

DefaultMutableTreeNode clientB = new DefaultMutableTreeNode(new TreeNodeModel(4, "Client B", true, true));
user01.add(clientB);

DefaultMutableTreeNode publicFolder = new DefaultMutableTreeNode(new TreeNodeModel(5, "Public Folders", false));
server.add(publicFolder);

DefaultMutableTreeNode clientC = new DefaultMutableTreeNode(new TreeNodeModel(6, "Client C", true));
publicFolder.add(clientC);
        Tree_Nodes.setCellRenderer(new TreeNodeRenderer());
        Tree_Nodes.setCellEditor(new TreeNodeEditor(Tree_Nodes));
Tree_Nodes.setModel(new DefaultTreeModel(server);

Наконец, определение того, какие узлы были проверены, было вопросом изучения коллекции узлов модели (с помощью кнопки).:

private Map<Integer, String> checked = new HashMap<Integer, String>();

private void Button_CheckedActionPerformed(java.awt.event.ActionEvent evt) {

    DefaultTableModel tableModel = ((DefaultTableModel) Table_Nodes.getModel());
    tableModel.getDataVector().removeAllElements();
    tableModel.fireTableDataChanged();

    checked.clear();

    DefaultTreeModel treeModel = (DefaultTreeModel) Tree_Nodes.getModel();
    DefaultMutableTreeNode root = (DefaultMutableTreeNode) treeModel.getRoot();

    getChildNodes(root);

    for (Iterator it=checked.entrySet().iterator(); it.hasNext(); ) {
        Map.Entry entry = (Map.Entry)it.next();
        tableModel.addRow(new Object[] {entry.getKey(), entry.getValue()});
    }

    Button_Checked.requestFocus();


}

private void getChildNodes(DefaultMutableTreeNode parentNode) {

    for (Enumeration e=parentNode.children(); e.hasMoreElements();) {

        DefaultMutableTreeNode childNode = (DefaultMutableTreeNode) e.nextElement();
        TreeNodeModel model = (TreeNodeModel) childNode.getUserObject();

        if (model.hasCheckBox && model.isSelected()) {
            checked.put(model.getKey(), model.getValue());
        }

        //recurse
        if (childNode.getChildCount()>0) getChildNodes(childNode);

    }

}

Другие советы

Вот несколько "подводных камней", которые вызвали у меня проблемы с рендерингом:

  1. TreeCellEditor может не использовать экземпляр TreeCellRenderer, переданный в JTree.setCellRenderer().Если TreeCellEditor.getTreeCellEditorComponent() возвращает тот же экземпляр, что и у дерева TreeCellRenderer.getTreeCellRendererComponent() в конечном итоге у вас возникнут проблемы с рендерингом.Вы попытаетесь отредактировать одну ячейку, но средство визуализации запускается для 5 несвязанных ячеек.Когда редактор попытается восстановить состояние JCheckBox, он получит значение из последней отображенной ячейки (в отличие от последней отредактированной ячейки), что явно неверно.

  2. Использование TreeCellEditor.getCellEditorValue() чтобы изменить значение ячейки, вместо добавления прослушивателей мыши непосредственно на флажок.Этот метод вызывается только в том случае, если значение сохраняется.Если вы немедленно отреагируете на события мыши, значение не будет откатываться назад. CellEditor.cancelCellEditing().

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top