复合JTree节点允许事件传递到下面的对象

我正在尝试创建一个JTree,其中一些节点是包含JLabel和JButton的复合对象。 Node表示JLabel显示的服务器和端口,JButton将使用Desktop API打开默认浏览器并转到URL。

我已经阅读了以下内容,并尽可能地密切关注它们。 节点显示我想要的方式(主要是 – 我可以处理以后更好)但当我尝试点击按钮时,JTree正在响应事件,而不是按钮。

java swing:为JTree项添加自定义图形按钮

http://www.java2s.com/Code/Java/Swing-JFC/TreeCellRenderer.htm

https://stackoverflow.com/a/3769158/1344282

我需要知道如何允许事件通过JTree,以便它们由下面的对象(JButton或JLabel)处理。

这是我的TreeCellEditor:

public class UrlValidationCellEditor extends DefaultTreeCellEditor { public UrlValidationCellEditor(JTree tree, DefaultTreeCellRenderer renderer) { super(tree, renderer); } @Override public Component getTreeCellEditorComponent(JTree tree, Object value, boolean isSelected, boolean expanded, boolean leaf, int row) { return renderer.getTreeCellRendererComponent(tree, value, true, expanded, leaf, row, true); } @Override public boolean isCellEditable(EventObject anEvent) { return true; // Or make this conditional depending on the node } } 

这是TreeCellRenderer:

 public class UrlValidationRenderer extends DefaultTreeCellRenderer implements TreeCellRenderer { JLabel titleLabel; UrlGoButton goButton; JPanel renderer; DefaultTreeCellRenderer defaultRenderer = new DefaultTreeCellRenderer(); public UrlValidationRenderer() { renderer = new JPanel(new GridLayout(1, 2)); titleLabel = new JLabel(" "); titleLabel.setForeground(Color.blue); renderer.add(titleLabel); goButton = new UrlGoButton(); renderer.add(goButton); renderer.setBorder(BorderFactory.createLineBorder(Color.black)); backgroundSelectionColor = defaultRenderer .getBackgroundSelectionColor(); backgroundNonSelectionColor = defaultRenderer .getBackgroundNonSelectionColor(); } @Override public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) { Component returnValue = null; if ((value != null) && (value instanceof DefaultMutableTreeNode)) { Object userObject = ((DefaultMutableTreeNode) value) .getUserObject(); if (userObject instanceof UrlValidation) { UrlValidation validationResult = (UrlValidation) userObject; titleLabel.setText(validationResult.getServer()+":"+validationResult.getPort()); goButton.setUrl(validationResult.getUrl()); if (selected) { renderer.setBackground(backgroundSelectionColor); } else { renderer.setBackground(backgroundNonSelectionColor); } renderer.setEnabled(tree.isEnabled()); returnValue = renderer; } } if (returnValue == null) { returnValue = defaultRenderer.getTreeCellRendererComponent(tree, value, selected, expanded, leaf, row, hasFocus); } return returnValue; } } 

我将不胜感激任何见解或建议。 谢谢!

渲染器不会以这种方式工作。 它们被用作橡皮图章,这意味着在绘制JList时,实际上只有一个渲染器实例被绘制在整个地方。 所以它无法处理鼠标输入,因为对象实际上并不存在 – 它们只是被绘制。

为了将鼠标事件传递给下面的对象,您需要实现单元格编辑器。 有时,编辑器看起来与渲染器不同(String渲染器是标签,编辑器是文本字段,例如)。 遵循此逻辑,编辑器必须使用组件的另一个实例来实现。

现在,您将要渲染按钮并使用它们进行操作(即编辑)。 然后编辑器必须是JButton的另一个实例,与渲染器不同。 将按钮实现为渲染器很容易,但棘手的部分是作为编辑器实现。 您需要扩展AbstractCellEditor并实现TreeCellEditor 和ActionListener 。 然后该按钮是编辑器类的一个字段。 在编辑器类的构造函数中,初始化按钮并将其添加为按钮的新动作侦听器 。 在getTreeCellEditorComponent方法中,您只需返回按钮。 在actionPerformed中,您可以在按下按钮时调用您需要执行的任何代码,然后调用stopCellEditing()

这样它对我有用。

我做了一个SSCCE,演示了String Tree上的用法

 public class Start { public static class ButtonCellEditor extends AbstractCellEditor implements TreeCellEditor, ActionListener, MouseListener { private JButton button; private JLabel label; private JPanel panel; private Object value; public ButtonCellEditor(){ panel = new JPanel(new BorderLayout()); button = new JButton("Press me!"); button.addActionListener(this); label = new JLabel(); label.addMouseListener(this); panel.add(button, BorderLayout.EAST); panel.add(label); } @Override public Object getCellEditorValue(){ return value.toString(); } @Override public void actionPerformed(ActionEvent e){ String val = value.toString(); System.out.println("Pressed: " + val); stopCellEditing(); } @Override public Component getTreeCellEditorComponent(JTree tree, Object value, boolean isSelected, boolean expanded, boolean leaf, int row){ this.value = value; label.setText(value.toString()); return panel; } @Override public void mouseClicked(MouseEvent e){ } @Override public void mousePressed(MouseEvent e){ String val = value.toString(); System.out.println("Clicked: " + val); stopCellEditing(); } @Override public void mouseReleased(MouseEvent e){ } @Override public void mouseEntered(MouseEvent e){ } @Override public void mouseExited(MouseEvent e){ } } public static class ButtonCellRenderer extends JPanel implements TreeCellRenderer { JButton button; JLabel label; ButtonCellRenderer(){ super(new BorderLayout()); button = new JButton("Press me!"); label = new JLabel(); add(button, BorderLayout.EAST); add(label); } @Override public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus){ label.setText(value.toString()); return this; } } public static void main(String[] args){ JTree tree = new JTree(); tree.setEditable(true); tree.setCellRenderer(new ButtonCellRenderer()); tree.setCellEditor(new ButtonCellEditor()); JFrame test = new JFrame(); test.add(new JScrollPane(tree)); test.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); test.setSize(500, 500); test.setLocationRelativeTo(null); test.setVisible(true); } } 

节点应该有2个部分,一个标签和一个按钮。 当用户单击标签时,有关该节点的一些详细信息应出现在GUI的不同部分。 当用户单击该按钮时,它将导致浏览器窗口打开。 ..

不要这样做。 相反,只有树中的标签。 将按钮添加到显示“节点详细信息”的同一GUI中。