1

我在 Java 中使用我创建的“画布”类(它是一个扩展JPanel的 )来绘制动画环形图时遇到了问题。此图表使用 aMouseListener来获取点击事件。

问题是鼠标位置似乎不准确,这意味着它似乎与“画布”无关,而是相对于窗口(在左上角,y 坐标约为 30px)。

这是我的代码:

我创建了一个类,它扩展了 JPanel 并且确实有一个 BufferedImage 作为成员。

public class Canvas extends JPanel {

    public BufferedImage buf;
    private RingChart _parent;

    public Canvas(int width, int height, RingChart parent){
        buf = new BufferedImage(width, height, 1);
    ...

在绘制组件方法中,我只绘制缓冲图像,因此我可以通过在公共的缓冲图像上进行绘制,从“外部”在画布上进行绘制。

public void paintComponent(Graphics g) {
        super.paintComponent(g);

        Graphics2D g2 = (Graphics2D)g; 
        g2.drawImage(buf, null, 0, 0); 

    }

现在有一个包含“画布”的类 RingChart:

public class RingChart extends JFrame{

    public Canvas c;
    ...

我从画布类中的 bufferedImage 创建了一个 Graphics2D。这个 g2d 用于绘画:

public RingChart(){
    c = new Canvas(1500,980,this);
    add(c);
    setSize(1500, 1000);
    setTitle("Hans");
    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    g2d = (Graphics2D)c.buf.createGraphics();
    ...

我现在想要实现的是一个鼠标监听器,它监听画布上发生的鼠标事件。因此,当用户单击画布时,我可以通过事件变量在画布上检索他单击的位置。

所以我创建了一个鼠标监听器:

class MouseHandler implements MouseListener {

    @Override
    public void mouseClicked(MouseEvent e){
        RingChart r = ((Canvas)e.getSource()).getParent();
        r.mouseClick(e);
    }
    ...

...并将此鼠标侦听器添加到 RingChart 类的画布(myChart 是 RingChart 的一个实例,c 是它包含的画布):

        ...
        MouseHandler mouse = new MouseHandler();
        myChart.c.addMouseListener(mouse);
        ...

但正如我上面提到的,调用单击事件时返回的鼠标位置似乎并不准确。我认为错误一定是我创建那个mouseListener的方式,或者可能将它分配给错误的元素或类似的东西。但是我已经尝试了很多东西,但并没有改变。也许有人可以告诉我,我做错了什么?

更新:

作为 RingChart 成员并在鼠标侦听器中调用的函数“mouseClick”的代码:

public void mouseClick(MouseEvent evt){
    //evt = SwingUtilities.convertMouseEvent(this, evt, c);
    if(evt.getButton() == MouseEvent.BUTTON1 && animation == null){
        for(Element e : elements){
            if(e.getShape() != null && e.getShape().contains(evt.getPoint())){
                //do some stuff
            }
        }
    }
}

同样,我的类的层次结构:RingChart --contains a--> Canvas --got a--> MouseListener。此函数中的形状是已在画布 c 上绘制的形状。现在我想检查用户是否点击了其中一个。所以正如我所想,形状应该在画布坐标中,事件位置应该在画布坐标中,一切都应该放在一起。但事实并非如此。现在用户 MadProgrammer 告诉我,使用 ConvertMouseEvent 函数。但我目前不知道应该以哪种方式明智地使用它。

更新:

我找到了一个解决方案:我所要做的就是不直接将画布添加到 the中,JFrame而是添加到 theContentPaneJFrame

所以与其:

public RingChart(){
    c = new Canvas(1500,980,this);
    add(c);
    ...

我愿意:

public RingChart(){
    c = new Canvas(1500,980,this);
    getContentPane().add(c);
    ...

然后我MouseListenerContentPane.

getContentPane().addMouseListener(new MouseHandler());
getContentPane().addMouseMotionListener(new MouseMoveHandler());

我不知道这是否是一个优雅的解决方案,但它确实有效。

4

1 回答 1

5

鼠标事件会自动转换为相对于它发生的组件,即点 0x0 始终是组件的左上角。

通过使用RingChart r = ((Canvas)e.getSource()).getParent(),您有效地更改了引用,这意味着该位置不再有效。

您需要转换位置,使其坐标位于父组件的上下文中。看看SwingUtilities.convertMouseEvent(Component, MouseEvent, Component)

更新图片

让我们举这个例子......

样本

蓝色框与红色框的相对位置为 50px x 50px。如果您单击蓝色框,假设在 25x25,鼠标坐标将相对于蓝色框(0x0 将是蓝色框的左上角)。

如果您随后将此事件传递给红色框并尝试使用其中的坐标,您会发现坐标现在将位于红色框左上角和蓝色框之间的中间,因为坐标是上下文相关的。

为了让它工作,您需要将鼠标事件位置从蓝色框转换为红色框,这将使其成为 75x75

现在,当您将鼠标事件传递给时,我不知道您在做什么,RingChart所以我只是猜测这是您面临的问题。

使用点击代码更新

好吧,假设你有一个Canvas100x100 的图像。Canvas您在 50x50 处单击它。然后,您将该值传递回链。

public void mouseClick(MouseEvent evt){
    //evt = SwingUtilities.convertMouseEvent(this, evt, c);
    if(evt.getButton() == MouseEvent.BUTTON1 && animation == null){
        for(Element e : elements){
            // Here, we are asking the shape if it contains the point 50x50...
            // Not 150x150 which would be the relative position of the click
            // in the context to the RingChart, which is where all your objects
            // are laid out.
            // So even the original Canvas you clicked on will return 
            // false because it's position + size (100x100x width x height) 
            // does not contain the specified point of 50x50...
            if(e.getShape() != null && e.getShape().contains(evt.getPoint())){
                //do some stuff
            }
        }
    }
}

更新

我认为你的参考文献是错误的......

public static MouseEvent convertMouseEvent(Component source,
                       MouseEvent sourceEvent,
                       Component destination)

我认为它应该像

evt = SwingUtilities.convertMouseEvent(evt.getComponent(), evt, this);

更新代码示例

好的,所以,我把这个小例子放在一起......

public class TestMouseClickPoint extends JFrame {

    private ContentPane content;

    public TestMouseClickPoint() throws HeadlessException {

        setSize(600, 600);
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        setLocationRelativeTo(null);

        setLayout(new BorderLayout());

        content = new ContentPane();
        add(content);

    }

    protected void updateClickPoint(MouseEvent evt) {
        content.updateClickPoint(evt);
    }

    protected class ContentPane extends JPanel {

        private Point relativePoint;
        private Point absolutePoint;

        public ContentPane() {
            setPreferredSize(new Dimension(600, 600));
            setLayout(null); // For testing purpose only...

            MousePane mousePane = new MousePane();
            mousePane.setBounds(100, 100, 400, 400);

            add(mousePane);
        }

        protected void updateClickPoint(MouseEvent evt) {
            absolutePoint = new Point(evt.getPoint());
            evt = SwingUtilities.convertMouseEvent(evt.getComponent(), evt, this);
            relativePoint = new Point(evt.getPoint());

            System.out.println(absolutePoint);
            System.out.println(relativePoint);

            repaint();
        }

        protected void paintCross(Graphics2D g2d, Point p) {
            g2d.drawLine(p.x - 5, p.y - 5, p.x + 5, p.y + 5);
            g2d.drawLine(p.x - 5, p.y + 5, p.x + 5, p.y - 5);
        }

        /*
         * This is not recommended, but I want to paint ontop of everything...
         */
        @Override
        public void paint(Graphics g) {
            super.paint(g);

            Graphics2D g2d = (Graphics2D) g;

            if (relativePoint != null) {
                g2d.setColor(Color.BLACK);
                paintCross(g2d, relativePoint);
            }

            if (absolutePoint != null) {
                g2d.setColor(Color.RED);
                paintCross(g2d, absolutePoint);
            }

        }
    }

    protected class MousePane extends JPanel {

        private Point clickPoint;

        public MousePane() {

            addMouseListener(new MouseAdapter() {

                @Override
                public void mouseClicked(MouseEvent e) {
                    clickPoint = e.getPoint();
                    TestMouseClickPoint.this.updateClickPoint(e);
                    repaint();
                }
            });

            setBorder(new LineBorder(Color.RED));

        }

        @Override
        protected void paintComponent(Graphics g) {

            super.paintComponent(g);

            Graphics2D g2d = (Graphics2D) g;
            g2d.setColor(Color.BLUE);

            if (clickPoint != null) {
                g2d.drawLine(clickPoint.x, clickPoint.y - 5, clickPoint.x, clickPoint.y + 5);
                g2d.drawLine(clickPoint.x - 5, clickPoint.y, clickPoint.x + 5, clickPoint.y);
            }

        }
    }

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        try {
            UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
        } catch (ClassNotFoundException ex) {
        } catch (InstantiationException ex) {
        } catch (IllegalAccessException ex) {
        } catch (UnsupportedLookAndFeelException ex) {
        }

        new TestMouseClickPoint().setVisible(true);
    }
}

基本上,它将绘制三个点。鼠标被点击的点(相对于事件源)、父容器中未转换的点和父容器中的转换点。

在此处输入图像描述

您需要做的下一件事是确定鼠标位置实际上已被转换,但失败了。我可能需要查看您的代码的工作示例以确定您实际在做什么。

于 2012-09-17T20:21:51.483 回答