java - JTree 节点渲染器和节点编辑器中的多个组件

标签 java swing jtree

我正在尝试创建一个 JTree,其中节点具有多个组件:一个包含 JCheckBox 的 JPanel,后跟一个 JLabel,然后是一个 JComboBox。如果有人想运行它,我已在底部附加了代码。幸运的是,JTree 正确地呈现了组件。但是,当我单击 JComboBox 时,该节点消失了;如果我单击 JCheckBox,它工作正常。看来我对 TreeCellEditor 的设置方式做了一些错误。我该如何解决这个问题?我是否超出了 JTree 的能力?

这是我在下面发布的代码的快速概述。

  • EntityListDialog 类仅创建用户界面。除了 createTree 方法之外,理解它没有任何用处。
  • Node 是保存有关 JTree 中每个节点的信息的数据结构。所有Node都有一个name,但samples可能为null或空数组。通过查看 EntityListDialogcreateTree 方法,这一点应该很明显。 name 用作 JCheckBox 的文本。如果 samples 非空,则将其用作 JCheckBox 的内容。
  • NodeWithSamplesRenderer 渲染 sample 非空的 Node。它使用由 JCheckBox 和 JComboBox 组成的 JPanel 创建复杂的用户界面。
  • NodeWithoutSamplesRenderersamples 为空时仅创建一个 JCheckBox。
  • RendererDispatcher 决定是使用 NodeWithSamplesRenderer 还是 NodeWithoutSamplesRenderer。这完全取决于 Node 是否有非空 samples 成员。它本质上是作为 NodeWith*SamplesRenderer 插入 JTree 的一种手段。

代码 list :

import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;
import javax.swing.tree.*;

public class EntityListDialog {

    final JDialog dialog;
    final JTree entitiesTree;

    public EntityListDialog() {
        dialog = new JDialog((Frame) null, "Test");
        entitiesTree = createTree();
        JScrollPane entitiesTreeScrollPane = new JScrollPane(entitiesTree);
        JCheckBox pathwaysCheckBox = new JCheckBox("Do additional searches");
        JButton sendButton = new JButton("Send");
        JButton cancelButton = new JButton("Cancel");
        JButton selectAllButton = new JButton("All");
        JButton deselectAllButton = new JButton("None");

        dialog.getContentPane().setLayout(new GridBagLayout());
        GridBagConstraints c = new GridBagConstraints();

        JPanel selectPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
        selectPanel.add(new JLabel("Select: "));
        selectPanel.add(selectAllButton);
        selectPanel.add(deselectAllButton);
        c.gridx = 0;
        c.gridy = 0;
        c.weightx = 1.0;
        c.weighty = 0.0;
        c.fill = GridBagConstraints.HORIZONTAL;
        dialog.getContentPane().add(selectPanel, c);

        c.gridx = 0;
        c.gridy = 1;
        c.weightx = 1.0;
        c.weighty = 1.0;
        c.fill = GridBagConstraints.BOTH;
        c.insets = new Insets(0, 5, 0, 5);
        dialog.getContentPane().add(entitiesTreeScrollPane, c);

        c.gridx = 0;
        c.gridy = 2;
        c.weightx = 1.0;
        c.weighty = 0.0;
        c.insets = new Insets(0, 0, 0, 0);
        c.fill = GridBagConstraints.HORIZONTAL;
        dialog.getContentPane().add(pathwaysCheckBox, c);

        JPanel buttonsPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
        buttonsPanel.add(sendButton);
        buttonsPanel.add(cancelButton);
        c.gridx = 0;
        c.gridy = 3;
        c.weightx = 1.0;
        c.weighty = 0.0;
        c.fill = GridBagConstraints.HORIZONTAL;
        dialog.getContentPane().add(buttonsPanel, c);

        dialog.pack();
        dialog.setVisible(true);
    }

    public static void main(String[] args) {
        EntityListDialog dialog = new EntityListDialog();
    }

    private static JTree createTree() {
        DefaultMutableTreeNode root = new DefaultMutableTreeNode(
            new Node("All Entities"));
        root.add(new DefaultMutableTreeNode(
            new Node("Entity 1", "Sample A", "Sample B", "Sample C")));
        root.add(new DefaultMutableTreeNode(
            new Node("Entity 2", "Sample D", "Sample E", "Sample F")));
        root.add(new DefaultMutableTreeNode(
            new Node("Entity 3", "Sample G", "Sample H", "Sample I")));
        JTree tree = new JTree(root);
        RendererDispatcher rendererDispatcher = new RendererDispatcher(tree);
        tree.setCellRenderer(rendererDispatcher);
        tree.setCellEditor(rendererDispatcher);
        tree.setEditable(true);
        return tree;
    }
}

class Node {

    final String name;
    final String[] samples;
    boolean selected;
    int selectedSampleIndex;

    public Node(String name, String... samples) {
        this.name = name;
        this.selected = false;
        this.samples = samples;
        if (samples == null) {
            this.selectedSampleIndex = -1;
        } else {
            this.selectedSampleIndex = 0;
        }
    }

    public boolean isSelected() {
        return selected;
    }

    public void setSelected(boolean selected) {
        this.selected = selected;
    }

    public String toString() {
        return name;
    }

    public int getSelectedSampleIndex() {
        return selectedSampleIndex;
    }

    public void setSelectedSampleIndex(int selectedSampleIndex) {
        this.selectedSampleIndex = selectedSampleIndex;
    }

    public String[] getSamples() {
        return samples;
    }
}

interface Renderer {

    public void setForeground(final Color foreground);

    public void setBackground(final Color background);

    public void setFont(final Font font);

    public void setEnabled(final boolean enabled);

    public Component getComponent();

    public Object getContents();
}

class NodeWithSamplesRenderer implements Renderer {

    final DefaultComboBoxModel comboBoxModel = new DefaultComboBoxModel();
    final JPanel panel = new JPanel();
    final JCheckBox checkBox = new JCheckBox();
    final JLabel label = new JLabel("   Samples: ");
    final JComboBox comboBox = new JComboBox(comboBoxModel);
    final JComponent components[] = {panel, checkBox, comboBox, label};

    public NodeWithSamplesRenderer() {
        Boolean drawFocus =
            (Boolean) UIManager.get("Tree.drawsFocusBorderAroundIcon");
        if (drawFocus != null) {
            checkBox.setFocusPainted(drawFocus.booleanValue());
        }
        for (int i = 0; i < components.length; i++) {
            components[i].setOpaque(true);
        }

        panel.add(checkBox);
        panel.add(label);
        panel.add(comboBox);
    }

    public void setForeground(final Color foreground) {
        for (int i = 0; i < components.length; i++) {
            components[i].setForeground(foreground);
        }
    }

    public void setBackground(final Color background) {
        for (int i = 0; i < components.length; i++) {
            components[i].setBackground(background);
        }
    }

    public void setFont(final Font font) {
        for (int i = 0; i < components.length; i++) {
            components[i].setFont(font);
        }
    }

    public void setEnabled(final boolean enabled) {
        for (int i = 0; i < components.length; i++) {
            components[i].setEnabled(enabled);
        }
    }

    public void setContents(Node node) {
        checkBox.setText(node.toString());

        comboBoxModel.removeAllElements();
        for (int i = 0; i < node.getSamples().length; i++) {
            comboBoxModel.addElement(node.getSamples()[i]);
        }
    }

    public Object getContents() {
        String title = checkBox.getText();
        String[] samples = new String[comboBoxModel.getSize()];
        for (int i = 0; i < comboBoxModel.getSize(); i++) {
            samples[i] = comboBoxModel.getElementAt(i).toString();
        }
        Node node = new Node(title, samples);
        node.setSelected(checkBox.isSelected());
        node.setSelectedSampleIndex(comboBoxModel.getIndexOf(
            comboBoxModel.getSelectedItem()));
        return node;
    }

    public Component getComponent() {
        return panel;
    }
}

class NodeWithoutSamplesRenderer implements Renderer {

    final JCheckBox checkBox = new JCheckBox();

    public NodeWithoutSamplesRenderer() {
        Boolean drawFocus =
            (Boolean) UIManager.get("Tree.drawsFocusBorderAroundIcon");
        if (drawFocus != null) {
            checkBox.setFocusPainted(drawFocus.booleanValue());
        }
    }

    public void setForeground(final Color foreground) {
        checkBox.setForeground(foreground);
    }

    public void setBackground(final Color background) {
        checkBox.setBackground(background);
    }

    public void setFont(final Font font) {
        checkBox.setFont(font);
    }

    public void setEnabled(final boolean enabled) {
        checkBox.setEnabled(enabled);
    }

    public void setContents(Node node) {
        checkBox.setText(node.toString());
    }

    public Object getContents() {
        String title = checkBox.getText();
        Node node = new Node(title);
        node.setSelected(checkBox.isSelected());
        return node;
    }

    public Component getComponent() {
        return checkBox;
    }
}

class NoNodeRenderer implements Renderer {

    final JLabel label = new JLabel();

    public void setForeground(final Color foreground) {
        label.setForeground(foreground);
    }

    public void setBackground(final Color background) {
        label.setBackground(background);
    }

    public void setFont(final Font font) {
        label.setFont(font);
    }

    public void setEnabled(final boolean enabled) {
        label.setEnabled(enabled);
    }

    public void setContents(String text) {
        label.setText(text);
    }

    public Object getContents() {
        return label.getText();
    }

    public Component getComponent() {
        return label;
    }
}

class RendererDispatcher extends AbstractCellEditor
    implements TreeCellRenderer, TreeCellEditor {

    final static Color selectionForeground =
        UIManager.getColor("Tree.selectionForeground");
    final static Color selectionBackground =
        UIManager.getColor("Tree.selectionBackground");
    final static Color textForeground =
        UIManager.getColor("Tree.textForeground");
    final static Color textBackground =
        UIManager.getColor("Tree.textBackground");
    final JTree tree;
    final NodeWithSamplesRenderer nodeWithSamplesRenderer =
        new NodeWithSamplesRenderer();
    final NodeWithoutSamplesRenderer nodeWithoutSamplesRenderer =
        new NodeWithoutSamplesRenderer();
    final NoNodeRenderer noNodeRenderer = new NoNodeRenderer();
    final Renderer[] renderers = {
        nodeWithSamplesRenderer, nodeWithoutSamplesRenderer, noNodeRenderer
    };
    Renderer renderer = null;

    public RendererDispatcher(JTree tree) {
        this.tree = tree;
        Font font = UIManager.getFont("Tree.font");
        if (font != null) {
            for (int i = 0; i < renderers.length; i++) {
                renderers[i].setFont(font);
            }
        }
    }

    public Component getTreeCellRendererComponent(JTree tree,
        Object value, boolean selected, boolean expanded,
        boolean leaf, int row, boolean hasFocus) {
        final Node node = extractNode(value);
        if (node == null) {
            renderer = noNodeRenderer;
            noNodeRenderer.setContents(tree.convertValueToText(
                value, selected, expanded, leaf, row, false));
        } else {
            if (node.getSamples() == null || node.getSamples().length == 0) {
                renderer = nodeWithoutSamplesRenderer;
                nodeWithoutSamplesRenderer.setContents(node);
            } else {
                renderer = nodeWithSamplesRenderer;
                nodeWithSamplesRenderer.setContents(node);
            }
        }

        renderer.setEnabled(tree.isEnabled());
        if (selected) {
            renderer.setForeground(selectionForeground);
            renderer.setBackground(selectionBackground);
        } else {
            renderer.setForeground(textForeground);
            renderer.setBackground(textBackground);
        }

        renderer.getComponent().repaint();
        renderer.getComponent().invalidate();
        renderer.getComponent().validate();

        return renderer.getComponent();
    }

    public Component getTreeCellEditorComponent(
        JTree tree, Object value, boolean selected,
        boolean expanded, boolean leaf, int row) {
        return getTreeCellRendererComponent(
            tree, value, true, expanded, leaf, row, true);
    }

    public Object getCellEditorValue() {
        return renderer.getContents();
    }

    public boolean isCellEditable(final EventObject event) {
        if (!(event instanceof MouseEvent)) {
            return false;
        }

        final MouseEvent mouseEvent = (MouseEvent) event;
        final TreePath path = tree.getPathForLocation(
            mouseEvent.getX(), mouseEvent.getY());
        if (path == null) {
            return false;
        }

        Object node = path.getLastPathComponent();
        if (node == null || (!(node instanceof DefaultMutableTreeNode))) {
            return false;
        }

        DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode) node;
        Object userObject = treeNode.getUserObject();

        return (userObject instanceof Node);
    }

    private static Node extractNode(Object value) {
        if ((value != null) && (value instanceof DefaultMutableTreeNode)) {
            DefaultMutableTreeNode node = (DefaultMutableTreeNode) value;
            Object userObject = node.getUserObject();
            if ((userObject != null) && (userObject instanceof Node)) {
                return (Node) userObject;
            }
        }

        return null;
    }
}

最佳答案

您忘记通过 JTree.cancelEditing()JTree.stopEditing() 告诉 JTree 编辑 session 已结束。其次,您应该只在编辑 session 中显示 JComboBox。使用普通的 JLabel 来标准呈现当前选择。我不会给你代码,因为你的例子太冗长,无法真正表达我的观点。

编辑:此外,我想指出,使用相同的组件进行编辑和渲染是一个非常糟糕的主意。我们想一下吧。

  1. 您开始编辑节点。
  2. JComboBox 在下面的节点上方打开。
  3. 下面的节点必须重新渲染。
  4. 现在将使用当前用于编辑的完全相同的对象进行渲染。
  5. 您的 JTree 显示了所描述的问题。

最后,如果将 TreeCellRendererTreeCellEditor 分开,效果会更好。

关于java - JTree 节点渲染器和节点编辑器中的多个组件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2539960/

相关文章:

java - 将线程与 Google Places API 结合使用

java - java中如何计算两个String方法之间的值

java - CubicCurve2D 连接两个 JInternalFrame 实例

java - paint() 不会在 JLabel 动画中被调用

java - 具有包含 HTML 代码的 JLabel 节点的 JTree 在展开节点时抛出异常

java - Swing , JTree 遍历 , 枚举器警告

java - Selenium 使用 JavascriptExecutor 等待页面完全加载

java - 如何在玻璃板上画线?

java - 使用 java swing 中的线程加载内容时显示加载 gif Logo

java - JTree 节点未更新(在 Scala 中)