Визуализация JTree с помощью узлов JCheckBox - PullRequest
11 голосов
/ 13 января 2010

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

alt text

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

alt text

Мой код является адаптацией этого примера: 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;

}

Вот метод 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;

}

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

Ответы [ 2 ]

6 голосов
/ 21 января 2010

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

alt text

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

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 голос
/ 13 декабря 2011

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

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

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

...