我有一个JTree,其节点由JLabel和JComboBox组成。我想要的是,当单击时,选定的JComboBox会展开,但似乎第一次单击是JTree本身(?)占用的,因此我必须单击两次。
这是我的代码:
import java.awt.*; import java.awt.event.*; import java.util.*; import javax.swing.*; import javax.swing.UIManager.LookAndFeelInfo; import javax.swing.plaf.ColorUIResource; import javax.swing.tree.*; public class JComboBoxJTree extends JPanel { final JTree entitiesTree; public JComboBoxJTree() { entitiesTree = createTree(); JScrollPane entitiesTreeScrollPane = new JScrollPane(entitiesTree); this.setLayout(new GridBagLayout()); GridBagConstraints c = new GridBagConstraints(); 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); this.add(entitiesTreeScrollPane, c); entitiesTree.setBackground(Color.black); entitiesTreeScrollPane.setBackground(Color.black); this.setBackground(Color.black); } public static void main(String[] args) { try { for (LookAndFeelInfo info : UIManager.getInstalledLookAndFeels()) { if ("Nimbus".equals(info.getName())) { UIManager.setLookAndFeel(info.getClassName()); UIManager.getLookAndFeelDefaults().put("Button.background", Color.BLACK); UIManager.getLookAndFeelDefaults().put( "TextField.background", Color.BLACK); UIManager.getLookAndFeelDefaults().put( "ComboBox.background", Color.BLACK); UIManager.getLookAndFeelDefaults().put( "TextField.foreground", Color.WHITE); UIManager.getLookAndFeelDefaults().put("List.foreground", Color.WHITE); UIManager.getLookAndFeelDefaults().put("List.background", Color.BLACK); UIManager.put("textForeground", Color.white); UIManager.put("info", new ColorUIResource(Color.BLACK)); UIManager.put("ScrollPane.background", Color.black); break; } } } catch (Exception e) { e.printStackTrace(); } JComboBoxJTree dialog = new JComboBoxJTree(); JDialog jd = new JDialog(); jd.add(dialog); jd.pack(); jd.setVisible(true); } private static JTree createTree() { DefaultMutableTreeNode root = new DefaultMutableTreeNode(new Node( "Plugins")); root.add(new DefaultMutableTreeNode(new Node("Plugin 1", "Running", "Paused", "Stopped"))); root.add(new DefaultMutableTreeNode(new Node("Plugin 2", "Running", "Paused", "Stopped"))); DefaultMutableTreeNode leaf = new DefaultMutableTreeNode(new Node( "Plugin 3", "Running", "Paused", "Stopped")); root.add(leaf); leaf.add(new DefaultMutableTreeNode(new Node("Plugin 3A", "Running", "Paused", "Stopped"))); leaf.add(new DefaultMutableTreeNode(new Node("Plugin 3B", "Running", "Paused", "Stopped"))); JTree tree = new JTree(root); RendererDispatcher rendererDispatcher = new RendererDispatcher(tree); RendererDispatcher editorDispatcher = new RendererDispatcher(tree); tree.setCellRenderer(rendererDispatcher); tree.setCellEditor(editorDispatcher); tree.setEditable(true); return tree; } } class Node { final String name; final String[] plugins; boolean selected; int selectedPluginIndex; public Node(String name, String... plugins) { this.name = name; this.selected = false; this.plugins = plugins; if (plugins == null) { this.selectedPluginIndex = -1; } else { this.selectedPluginIndex = 0; } } public boolean isSelected() { return selected; } public void setSelected(boolean selected) { this.selected = selected; } public String toString() { return name; } public int getSelectedPluginIndex() { return selectedPluginIndex; } public void setSelectedPluginIndex(int selectedPluginIndex) { this.selectedPluginIndex = selectedPluginIndex; } public String[] getPlugins() { return plugins; } } 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 DefaultComboBoxModel comboBoxModel = new DefaultComboBoxModel(); final JPanel panel = new JPanel(); JLabel pluginName = new JLabel(); final JComboBox comboBox = new JComboBox(comboBoxModel); final JComponent components[] = { panel, pluginName, comboBox }; public RendererDispatcher(JTree tree) { this.tree = tree; Font font = UIManager.getFont("Tree.font"); panel.add(pluginName); setForeground(Color.white); setBackground(Color.BLACK); if (font != null) { this.setFont(font); } } public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) { final Node node = extractNode(value); this.setContents(node); this.setEnabled(tree.isEnabled()); this.setForeground(Color.white); this.setBackground(Color.BLACK); return this.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 this.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; } 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()); pluginName.setText(node.toString()); comboBoxModel.removeAllElements(); if (node.getPlugins().length > 0) { panel.add(comboBox); for (int i = 0; i < node.getPlugins().length; i++) { comboBoxModel.addElement(node.getPlugins()[i]); } } else { panel.remove(comboBox); } } public Object getContents() { String title = pluginName.getText(); String[] plugins = new String[comboBoxModel.getSize()]; for (int i = 0; i < comboBoxModel.getSize(); i++) { plugins[i] = comboBoxModel.getElementAt(i).toString(); } Node node = new Node(title, plugins); node.setSelectedPluginIndex(comboBoxModel.getIndexOf(comboBoxModel .getSelectedItem())); return node; } public Component getComponent() { return panel; } }
Java 1.8.0
import java.awt.*; import java.awt.event.*; import java.util.*; import javax.swing.*; import javax.swing.tree.*; public class JComboBoxJTree2 { public JComponent makeUI() { String[] m = {"Running", "Paused", "Stopped"}; DefaultMutableTreeNode root = new DefaultMutableTreeNode(new Node("Plugins")); root.add(new DefaultMutableTreeNode(new Node("Plugin 1", m))); root.add(new DefaultMutableTreeNode(new Node("Plugin 2", m))); DefaultMutableTreeNode leaf = new DefaultMutableTreeNode(new Node("Plugin 3", m)); root.add(leaf); leaf.add(new DefaultMutableTreeNode(new Node("Plugin 3A", m))); leaf.add(new DefaultMutableTreeNode(new Node("Plugin 3B", m))); JTree tree = new JTree(root); RendererDispatcher rendererDispatcher = new RendererDispatcher(new JComboBox<String>()); RendererDispatcher editorDispatcher = new RendererDispatcher(new JComboBox<String>()); tree.setCellRenderer(rendererDispatcher); tree.setCellEditor(editorDispatcher); tree.setEditable(true); return new JScrollPane(tree); } public static void main(String[] args) { EventQueue.invokeLater(new Runnable() { @Override public void run() { createAndShowGUI(); } }); } public static void createAndShowGUI() { JFrame f = new JFrame(); f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); f.getContentPane().add(new JComboBoxJTree2().makeUI()); f.setSize(320, 240); f.setLocationRelativeTo(null); f.setVisible(true); } } class Node { private final String name; private final String[] plugins; private int selectedPluginIndex; public Node(String name, String... plugins) { this.name = name; this.plugins = plugins; } public String toString() { return name; } public int getSelectedPluginIndex() { return selectedPluginIndex; } public void setSelectedPluginIndex(int selectedPluginIndex) { this.selectedPluginIndex = selectedPluginIndex; } public String[] getPlugins() { return plugins; } } class RendererDispatcher extends DefaultCellEditor implements TreeCellRenderer { private final JPanel panel = new JPanel(); private final JLabel pluginName = new JLabel(); private final JComboBox<String> comboBox; private Node node; public RendererDispatcher(JComboBox<String> comboBox) { super(comboBox); this.comboBox = comboBox; panel.setOpaque(false); panel.add(pluginName); panel.add(comboBox); } @Override public Component getTreeCellRendererComponent( JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) { Node node = extractNode(value); setContents(node); return panel; } @Override public Component getTreeCellEditorComponent( JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row) { Node node = extractNode(value); setContents(node); this.node = node; return panel; } @Override public Object getCellEditorValue() { Object o = super.getCellEditorValue(); DefaultComboBoxModel<String> m = (DefaultComboBoxModel<String>) comboBox.getModel(); Node n = new Node(pluginName.getText(), node.getPlugins()); n.setSelectedPluginIndex(m.getIndexOf(o)); return n; } @Override public boolean isCellEditable(final EventObject event) { Object source = event.getSource(); if (!(source instanceof JTree) || !(event instanceof MouseEvent)) { return false; } final JTree tree = (JTree) source; 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; } Rectangle r = tree.getPathBounds(path); if (r == null) { return false; } Dimension d = panel.getPreferredSize(); r.setSize(new Dimension(d.width, r.height)); if (r.contains(mouseEvent.getX(), mouseEvent.getY())) { EventQueue.invokeLater(new Runnable() { @Override public void run() { Point pt = SwingUtilities.convertPoint(tree, mouseEvent.getPoint(), panel); //System.out.println(pt); Object o = SwingUtilities.getDeepestComponentAt(panel, pt.x, pt.y); if (o instanceof JComboBox) { comboBox.showPopup(); } else if (o instanceof Component) { Object oo = SwingUtilities.getAncestorOfClass(JComboBox.class, (Component) o); if (oo instanceof JComboBox) { comboBox.showPopup(); } } } }); return true; } return delegate.isCellEditable(event); } private static Node extractNode(Object value) { if (value instanceof DefaultMutableTreeNode) { DefaultMutableTreeNode node = (DefaultMutableTreeNode) value; Object userObject = node.getUserObject(); if (userObject instanceof Node) { return (Node) userObject; } } return null; } private void setContents(Node node) { if (node == null) { return; } pluginName.setText(node.toString()); DefaultComboBoxModel<String> model = (DefaultComboBoxModel<String>) comboBox.getModel(); model.removeAllElements(); if (node.getPlugins().length > 0) { panel.add(comboBox); for (String s : node.getPlugins()) { model.addElement(s); } comboBox.setSelectedIndex(node.getSelectedPluginIndex()); } else { panel.remove(comboBox); } } }