我正在尝试创建一个 JTree,其中一些节点是包含 JLabel 和 JButton 的复合对象。节点代表 JLabel 显示的服务器和端口,JButton 将使用桌面 API 打开默认浏览器并转到 URL。
我已经阅读了以下内容,并尽可能地仔细阅读了它们。节点以我想要的方式显示(主要是 - 我可以稍后处理让它变得更好)但是当我尝试单击按钮时,JTree 正在响应事件,而不是按钮。
java swing: add custom graphical button to JTree item
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 时,实际上只有一个渲染器实例被绘制到各处。因此它无法处理鼠标输入,因为对象并不真正存在 - 它们只是被绘制出来。
为了将鼠标事件传递给下面的对象,您需要实现一个单元格编辑器。有时,编辑器看起来与渲染器不同(例如,字符串渲染器是标签,编辑器是文本字段)。按照这个逻辑,必须使用组件的另一个实例来实现编辑器。
现在您将渲染按钮并使用它们进行操作(即编辑)。编辑器必须是 JButton 的另一个实例,与呈现器不同。将按钮作为渲染器实现很容易,但棘手的部分是作为编辑器实现。您需要扩展 AbstractCellEditor 并实现 TreeCellEditor 和 ActionListener。该按钮是编辑器类的一个字段。在编辑器类的构造函数中,您初始化按钮并添加this 作为按钮的新 Action 监听器。在 getTreeCellEditorComponent 方法中,您只需返回按钮。在 actionPerformed 中,您调用按钮按下时需要执行的任何代码 ,然后调用 stopCellEditing()。
这种方式适合我。
我制作了一个 SSCCE,演示了在字符串树上的用法
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);
}
}
关于java - 复合 JTree 节点允许事件传递到下面的对象,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10264469/