2010-03-29 48 views
1

我正在嘗試創建一個節點有幾個組件的JTree:一個JPanel,它包含一個JCheckBox,後面跟着一個JLabel,然後是一個JComboBox。如果希望運行它,我已將代碼附在底部。幸運的是,JTree正確地呈現組件。但是,當我點擊JComboBox時,節點消失;如果我點擊JCheckBox,它工作正常。似乎我在設置TreeCellEditor的方式上做錯了什麼。我怎麼能解決這個問題?我超越了JTree的功能嗎?JTree節點渲染器和節點編輯器中的多個組件

下面是我在下面發佈的代碼的簡要概述。

  • EntityListDialog只是創建用戶界面。除了createTree方法以外,瞭解它是沒有用的。
  • Node是保存有關JTree中每個節點的信息的數據結構。所有Node都有一個name,但samples可能爲空或一個空數組。這應該通過查看EntityListDialogcreateTree方法來證明。 name被用作JCheckBox的文本。如果samples非空,則將其用作JCheckBox的內容。
  • NodeWithSamplesRenderer呈現Node s其sample s是非空的。它使用由JCheckBox和JComboBox組成的JPanel創建複雜的用戶界面。
  • NodeWithoutSamplesRenderersamples爲空時只創建一個JCheckBox。
  • RendererDispatcher決定是否使用NodeWithSamplesRendererNodeWithoutSamplesRenderer。這完全取決於Node是否有非空的samples成員。它基本上用作插入JTree的手段。

代碼:

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; 
    } 
} 
+0

我重新格式化你的代碼;如果不正確,則還原。 – trashgod 2010-03-29 18:12:42

+0

感謝重新格式化。我不明白爲什麼代碼在嵌套滾動窗格中顯示。問題是我把大括號放在他們自己的路線上嗎? – 2010-03-30 15:30:27

回答

3

你已經忘記通過JTree.cancelEditing()JTree.stopEditing(),一個編輯會話結束告訴JTree。其次,您應該只在編輯會話中顯示JComboBox。使用正常的JLabel進行當前選擇的標準渲染。我不會給你代碼,因爲你的例子太冗長了,不能真正顯示我的觀點。

編輯:此外我想指出,使用相同的組件編輯和渲染是一個非常糟糕的主意。我們只是想一想。

  1. 您開始編輯節點。
  2. JComboBox在下面的節點上方打開。
  3. 下面的節點必須重新渲染。
  4. 使用完全相同的對象,目前正在使用的編輯現在將被拿走進行渲染。
  5. 您的JTree顯示了描述的問題。

如果您將TreeCellRendererTreeCellEditor分開,最後會更好。

2
RendererDispatcher rendererDispatcher = new RendererDispatcher(tree); 
RendererDispatcher editorDispatcher = new RendererDispatcher(tree); 
tree.setCellRenderer(rendererDispatcher); 
tree.setCellEditor(editorDispatcher);