2

这是我的问题:

在此处输入图像描述

当右键单击 JTree 中的元素时,我基本上是在创建一个下拉菜单,问题是 JTree 在 JScrollPane 内,并且窗格滚动得越多,我的弹出菜单就越远。

右键单击会触发一个 MouseEvent,该事件在以下代码中被拦截,其结果是创建一个新的弹出菜单。

@Override
public void mouseClicked(MouseEvent e) {
    if (SwingUtilities.isRightMouseButton(e)) {

        int row = tree.getClosestRowForLocation(e.getX(), e.getY());
        tree.setSelectionRow(row);

        TreePath path = tree.getPathForRow(row);  

        DefaultMutableTreeNode node = (DefaultMutableTreeNode) path.getLastPathComponent();  

        // If this object is a Search...
        if(node.getUserObject().getClass() == Search.class) {
            jp = new ItemEditPopUpMenu(tree, row, true);
        } else {
            jp = new ItemEditPopUpMenu(tree, row, false);
        }

        jp.show(this, e.getX(), e.getY());
    }
}

如您所见,我所做的只是从 MouseEvent 中获取位置并将其用作要在以下行中创建弹出菜单的位置:

jp.show(this, e.getX(), e.getY());

现在我注意到这是发送事件的元素的相对位置,这解释了问题,JScrollPanel 在滚动时从 JFrame 偏移,问题是我如何知道多远?

我只是不知道,这就是我需要帮助的地方。提前非常感谢!

4

4 回答 4

7

我似乎没有问题。

如果我使用视图端口“view”作为弹出窗口的引用,弹出窗口会准确显示鼠标单击的位置。

我只能想象你没有将对树的引用传递给弹出窗口......

在此处输入图像描述

我以为这将是一团糟的点转换,但事实证明,它很简单......

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class TestScrollPane {

    public static void main(String[] args) {
        new TestScrollPane();
    }

    public TestScrollPane() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                }

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setLayout(new BorderLayout());
                frame.add(new JScrollPane(new TestPane()));
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class TestPane extends JPanel {

        private List<Point> points;
        private JPopupMenu pm;

        public TestPane() {
            pm = new JPopupMenu();
            pm.add(new JLabel("Suprise"));
            points = new ArrayList<>(3);
            addMouseListener(new MouseAdapter() {
                @Override
                public void mouseClicked(MouseEvent e) {
                    Point p = e.getPoint();
                    points.clear();
                    points.add(p);
                    // View port
                    Container parent = getParent();
                    Point pp = SwingUtilities.convertPoint(TestPane.this, p, parent);
                    points.add(pp);
                    // ScrollPane...
                    parent = parent.getParent();
                    Point ppp = SwingUtilities.convertPoint(TestPane.this, p, parent);
                    points.add(ppp);

                    pm.show(TestPane.this, p.x, p.y);

                    repaint();
                }
            });
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(1000, 1000);
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D) g.create();
            if (points.size() == 3) {
                g2d.setColor(Color.RED);
                Point p = points.get(0);
                g2d.fillOval(p.x - 2, p.y - 2, 4, 4);

                g2d.setColor(Color.GREEN);
                p = points.get(1);
                g2d.fillOval(p.x - 2, p.y - 2, 4, 4);

                g2d.setColor(Color.BLUE);
                p = points.get(2);
                g2d.fillOval(p.x - 2, p.y - 2, 4, 4);
            }
            g2d.dispose();
        }
    }
}

只是为了确保我没有遗漏什么,这是一个使用JTree

在此处输入图像描述

(您可以放弃自定义单元格渲染进行测试,这是我放置的一些废代码)

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.EventQueue;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.IOException;
import java.util.Random;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JTree;
import javax.swing.UIManager;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.DefaultTreeModel;

public class TestCustomTreeNode {

    public static void main(String args[]) {
        new TestCustomTreeNode();
    }
    private JPopupMenu pm;

    public TestCustomTreeNode() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (Exception ex) {
                }

                pm = new JPopupMenu();
                pm.add(new JLabel("Suprise"));

                JFrame f = new JFrame("JTree Sample");
                f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

                JPanel pnlMain = new JPanel(new BorderLayout());
                pnlMain.setBackground(Color.white);

                createTree(pnlMain);

                f.setContentPane(new JScrollPane(pnlMain));

                f.setSize(300, 200);
                f.setVisible(true);
            }
        });
    }

    private void createTree(JPanel pnlMain) {
        Employee bigBoss = new Employee(Employee.randomName(), true);
        Employee[] level1 = new Employee[5];
        bigBoss.employees = level1;

        for (int i = 0; i < level1.length; i++) {
            level1[i] = new Employee(Employee.randomName(), true);
        }


        for (int i = 0; i < level1.length; i++) {
            Employee employee = level1[i];
            if (employee.isBoss) {
                int count = 5;
                employee.employees = new Employee[count];

                for (int j = 0; j < employee.employees.length; j++) {
                    employee.employees[j] = new Employee(Employee.randomName(), false);
                }
            }
        }

        CustomTreeNode root = new CustomTreeNode(loadResource("/pirate.png"), bigBoss);
        root.setUserObject("Root");
        DefaultTreeModel model = new DefaultTreeModel(root);

        for (Employee employee : bigBoss.employees) {
            CustomTreeNode boss = new CustomTreeNode(loadResource("/angel.png"), employee);
            root.add(boss);
            if (employee.isBoss) {
                for (Employee employee1 : employee.employees) {
                    CustomTreeNode emp = new CustomTreeNode(loadResource("/devil.png"), employee1);
                    boss.add(emp);
                }
            }
        }

        JTree tree = new JTree(model);
        tree.setCellRenderer(new CustomeTreeCellRenderer());
        pnlMain.add(tree, BorderLayout.CENTER);

        tree.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseReleased(MouseEvent e) {
                if (e.isPopupTrigger()) {
                    pm.show(e.getComponent(), e.getX(), e.getY());
                }
            }
        });

    }

    protected ImageIcon loadResource(String name) {

        ImageIcon image = null;
        try {
            image = new ImageIcon(ImageIO.read(getClass().getResource(name)));
            System.out.println(name + " - " + image);
        } catch (IOException ex) {
            ex.printStackTrace();
        }

        return image;

    }

    public static class Employee {

        public String name;
        public int id;
        public boolean isBoss;
        public Employee[] employees;

        public Employee(String name, boolean isBoss) {
            this.name = name;
            this.isBoss = isBoss;
            this.id = new Random(System.currentTimeMillis()).nextInt(Integer.MAX_VALUE);
        }

        @Override
        public String toString() {
            return this.name;
        }

        static String randomName() {
            String chars = "abcdefghijklmnopqrstuvwxyz";
            StringBuilder builder = new StringBuilder();
            Random r = new Random(System.currentTimeMillis());
            int length = r.nextInt(10) + 1;
            for (int i = 0; i < length; i++) {
                builder.append(chars.charAt(r.nextInt(chars.length())));
            }

            return builder.toString();
        }
    }

    public class CustomTreeNode extends DefaultMutableTreeNode {

        /**
         * The icon which is displayed on the JTree object. open, close, leaf icon.
         */
        private ImageIcon icon;

        public CustomTreeNode(ImageIcon icon) {
            this.icon = icon;
        }

        public CustomTreeNode(ImageIcon icon, Object userObject) {
            super(userObject);
            this.icon = icon;
        }

        public CustomTreeNode(ImageIcon icon, Object userObject, boolean allowsChildren) {
            super(userObject, allowsChildren);
            this.icon = icon;
        }

        public ImageIcon getIcon() {
            return icon;
        }

        public void setIcon(ImageIcon icon) {
            this.icon = icon;
        }
    }

    class CustomeTreeCellRenderer extends DefaultTreeCellRenderer {

        public CustomeTreeCellRenderer() {
        }

        @Override
        public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) {

//            if (!leaf) {
            CustomTreeNode node = (CustomTreeNode) value;

            if (node.getIcon() != null) {
                System.out.println(node + " - " + node.getIcon());
                setClosedIcon(node.getIcon());
                setOpenIcon(node.getIcon());
                setLeafIcon(node.getIcon());
            } else {
                System.out.println(node + " - default");
                setClosedIcon(getDefaultClosedIcon());
                setLeafIcon(getDefaultLeafIcon());
                setOpenIcon(getDefaultOpenIcon());
            }
//            }

            super.getTreeCellRendererComponent(tree, value, selected, expanded, leaf, row, hasFocus);

            return this;
        }
    }
}

注意:您可能需要更改鼠标事件,因为不同平台的弹出触发器可能不同,这在 Windows 上运行良好

由此,我只能得出结论,您传递给的组件引用JPopupMenu#show不是生成鼠标事件的组件引用。

于 2013-03-06T06:53:47.790 回答
3

我用了:

PointerInfo a = MouseInfo.getPointerInfo();
Point b = a.getLocation();
SwingUtilities.convertPointFromScreen(b, component);

组件是我的 JTree。这似乎抓住了鼠标屏幕的位置,并使用了隐藏在 Swing 中的转换实用函数来解决问题。希望这可以帮助某人!

于 2013-03-06T04:31:14.010 回答
3

我逐字逐句地遇到了这个问题JTree。虽然我发现接受的答案很有用,但仍然需要一些挖掘才能找出问题所在以及修复的样子。

因此,这是问题代码的样子:

final Component parent = ...
final JTree tree = ...
final JPopupMenu menu = ...

parent.add(tree);

tree.addMouseListener(new MouseAdapter() {
    @Override
    public void mousePressed(MouseEvent e) {
        if (e.getButton() == MouseEvent.BUTTON3) // Right click
            menu.show(parent, e.getX(), e.getY());
    }
});

错误现在应该很明显了!

theMouseEventtree从 added生成的MouseListener,但我们parent作为invokerto传递menu.show(...)。这会在您的屏幕截图中产生不良行为。

修复方法是传递tree如下invokerto menu.show(...)

final Component parent = ...
final JTree tree = ...
final JPopupMenu menu = ...

parent.add(tree);

tree.addMouseListener(new MouseAdapter() {
    @Override
    public void mousePressed(MouseEvent e) {
        if (e.getButton() == MouseEvent.BUTTON3) // Right click
            menu.show(tree, e.getX(), e.getY());
    }
});
于 2016-06-03T00:40:13.337 回答
1

这可能不是一个好的解决方案,但它会解决您的问题。

这里的问题是当你说e.getX()or时e.getY(),返回的坐标是相对于 JTree 的。正如我从给定的屏幕截图中看到的那样,您已经向下滚动了 JTree,您需要从“Y”坐标中减去垂直滚动条值的差异。

int verticalScrollValue = scrollPane.getVerticalScrollBar().getValue();
jp.show(this, e.getX(), e.getY() - verticalScrollValue);
于 2013-03-06T04:42:14.310 回答