Java JTree的右键上下文菜单?

29

我正在尝试在Java JTree中实现弹出式菜单。我已经子类化了DefaultTreeCellRenderer(以更改节点外观)和DefaultTreeCellEditor(创建组件以附加事件监听器,因为显然DefaultTreeCellRenderer.getTreeCellRendererComponent()返回的组件无法执行此操作?)。我不想真正“编辑”节点,只想在右键单击节点时能够弹出菜单,但这是我现在能想到的唯一方法...

以下是到目前为止我已经写的代码--我只是试图找出如何捕获MouseEvents。它有点工作,但效果不佳。有什么更好的方法来完成我正在尝试做的事情吗?

private class My_TreeCellRenderer extends DefaultTreeCellRenderer { 
    My_TreeCellRenderer() {
        super ();
    }   

    public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) {
        super.getTreeCellRendererComponent(tree, value, selected, expanded, leaf, row, hasFocus);

        // set label text and tool tips
        setText(((My_Object)value).getTreeLabel());
        setToolTipText(((My_Object)value).getTreeToolTip());

        return this;
    }
}

private class My_TreeCellEditor extends DefaultTreeCellEditor { 
    private MouseAdapter ma;

    My_TreeCellEditor(JTree tree, DefaultTreeCellRenderer renderer) {
        super (tree, renderer);
        ma = new MouseAdapter() {
            public void mousePressed(MouseEvent e) {
                if (e.isPopupTrigger()) {
                    System.out.println("My Popup");
                }
            }
            public void mouseReleased(MouseEvent e) {
                if (e.isPopupTrigger()) {
                    System.out.println("My Popup");
                }
            }
        };
    }

    public Component getTreeCellEditorComponent(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row) {
        String src_filename = null;

        // return non-editing component
        Component c = renderer.getTreeCellRendererComponent(tree, value, selected, expanded, leaf, row, true);

        // add mouse listener if it's not listening already
        MouseListener mouseListeners[] = c.getMouseListeners();
        int i;
        for (i=0; i < mouseListeners.length && mouseListeners[i] != ma; i++);
        if (i >= mouseListeners.length)
            c.addMouseListener(ma);

        return c;
    }

    protected boolean canEditImmediately(EventObject event) {
        if (event instanceof MouseEvent && ((MouseEvent)event).getClickCount() == 1)
            return true;
        else
            return false;
    }
}
6个回答

32

这是一个简单的任务,你需要以下所有内容:

//create a class which implements the MouseListener interface and
//implement the following in your overridden mouseClicked method

@Override
public void mouseClicked(MouseEvent e) {

    if (SwingUtilities.isRightMouseButton(e)) {

        int row = tree.getClosestRowForLocation(e.getX(), e.getY());
        tree.setSelectionRow(row);
        popupMenu.show(e.getComponent(), e.getX(), e.getY());
    }
}

你可以将这个自定义监听器添加到你想要的树上。


20

这段话是从JTree API中摘录而来的。

 // If you are interested in detecting either double-click events or when a user clicks on a node, regardless of whether or not it was selected, we recommend you do the following:

 final JTree tree = ...;

 MouseListener ml = new MouseAdapter() {
     public void mousePressed(MouseEvent e) {
         int selRow = tree.getRowForLocation(e.getX(), e.getY());
         TreePath selPath = tree.getPathForLocation(e.getX(), e.getY());
         if(selRow != -1) {
             if(e.getClickCount() == 1) {
                 mySingleClick(selRow, selPath);
             }
             else if(e.getClickCount() == 2) {
                 myDoubleClick(selRow, selPath);
             }
         }
     }
 };
 tree.addMouseListener(ml);

当然,您需要稍微修改它,以便实现右键单击而不是左键单击。


13

谢谢大家。当我花费那么多精力来实现一个简单的弹出窗口时,我知道肯定有问题。

一开始我并没有考虑这种方法,因为使用 x 和 y 坐标来查找我要找的节点感觉很奇怪,但我想这就是解决问题的方法。

    // add MouseListener to tree
    MouseAdapter ma = new MouseAdapter() {
        private void myPopupEvent(MouseEvent e) {
            int x = e.getX();
            int y = e.getY();
            JTree tree = (JTree)e.getSource();
            TreePath path = tree.getPathForLocation(x, y);
            if (path == null)
                return; 

            tree.setSelectionPath(path);

            My_Obj obj = (My_Obj)path.getLastPathComponent();

            String label = "popup: " + obj.getTreeLabel();
            JPopupMenu popup = new JPopupMenu();
            popup.add(new JMenuItem(label));
            popup.show(tree, x, y);
        }
        public void mousePressed(MouseEvent e) {
            if (e.isPopupTrigger()) myPopupEvent(e);
        }
        public void mouseReleased(MouseEvent e) {
            if (e.isPopupTrigger()) myPopupEvent(e);
        }
    };

    (...)

    JTree tree = new JTree();
    tree.addMouseListener(ma);

2
你每次都在创建一个 JPopupMenu。将创建操作移到其他地方,在事件触发时只需进行一次修改即可。 - Gene De Lisa
3
这是“过早优化”的一个好例子。在有必要之前(例如,资源初始化缓慢),没有必要创建长期存在的对象。 - serg.nechaev

2
我认为你把事情想得比必要的还要困难。
JTree上有几个“add_foo_Listener”方法。实现其中一个(TreeSelectionListener看起来很合适),然后您就可以获得当前选定的节点。
实现MouseListener以便您可以检测右键单击事件(并将其添加到JTree中,因为JTree是Component),然后您应该拥有发布上下文敏感菜单所需的所有内容。
查看此教程以获取更多详细信息。

1
渲染器只是一个短暂的“橡皮图章”,因此在其上添加输入监听器并不特别有帮助。正如您所指出的,编辑器仅在您手势编辑时才存在。因此,您需要向JTree(假设它没有实现为组合组件)添加侦听器。

0
调用addRightClickListener()将右键单击上下文菜单监听器添加到您的JTree中。这两个重写是为了正确的跨平台功能(WindowsLinux在此处不同)。
private void addRightClickListener()
{
    MouseListener mouseListener = new MouseAdapter()
    {
        @Override
        public void mousePressed(MouseEvent mouseEvent)
        {
            handleContextMenu(mouseEvent);
        }

        @Override
        public void mouseReleased(MouseEvent mouseEvent)
        {
            handleContextMenu(mouseEvent);
        }
    };

    tree.addMouseListener(mouseListener);
}

private void handleContextMenu(MouseEvent mouseEvent)
{
    if (mouseEvent.isPopupTrigger())
    {
        MyContextMenu contextMenu = new MyContextMenu();

        contextMenu.show(mouseEvent.getComponent(),
                mouseEvent.getX(),
                mouseEvent.getY());
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接