简体   繁体   English

Java MouseEvent位置不正确

[英]Java MouseEvent position is inaccurate

I've got a problem in Java using a "canvas" class I created, which is an extended JPanel , to draw an animated ring chart. 我在Java中使用创建的“ canvas”类(扩展的JPanel )绘制动画圆环图时遇到了问题。 This chart is using a MouseListener to fetch click events. 此图表使用MouseListener来获取点击事件。

The problem is that the mouse position does not seem to be accurate, meaning it does not seem to be relative to the "canvas" but instead relative to the window (in the left, upper corner I got about 30px for y coord). 问题在于鼠标的位置似乎不准确,这意味着它似乎并不相对于“画布”,而是相对于窗口(在左上角,y坐标为30px)。

This is my code: 这是我的代码:

I created a class, that extends JPanel and does have a BufferedImage as member. 我创建了一个类,该类扩展了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);
    ...

In the paint component method I just draw the buffered image, so I am able to paint on the canvas from 'outside' by painting on the buffered image, which is public. 在paint component方法中,我只绘制了缓冲的图像,因此我可以通过在缓冲的图像(公开的)上从“外部”在画布上进行绘画。

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

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

    }

Now there's a class RingChart which contains a "canvas": 现在有一个包含“画布”的类RingChart:

public class RingChart extends JFrame{

    public Canvas c;
    ...

And I create a Graphics2D from the bufferedImage in the canvas class. 然后从canvas类中的bufferedImage创建一个Graphics2D。 This g2d is used for painting: 此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();
    ...

What I now was trying to achieve, was a mouse listener that listened to mouse events happening on the canvas. 我现在想要实现的是一个鼠标侦听器,它侦听画布上发生的鼠标事件。 So when the user clicks on the canvas I could retrieve the position he clicked on, upon the canvas, through the event variable. 因此,当用户单击画布时,我可以通过事件变量检索其在画布上单击的位置。

So I created a mouse listener: 因此,我创建了一个鼠标侦听器:

class MouseHandler implements MouseListener {

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

...and added this mouse listener to the canvas of the RingChart class (myChart is an instance of RingChart and c is the canvas it contains): ...并将此鼠标侦听器添加到RingChart类的画布(myChart是RingChart的实例,c是它包含的画布):

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

But as I mentioned above, the mouse position, that's returned when the click event is called, does not seem to be accurate. 但是如上所述,在单击事件被调用时返回的鼠标位置似乎并不准确。 I think the mistake must be somehow in the way I created that mouseListener or maybe assigned it to the wrong element or something like that. 我认为错误一定是由于我创建mouseListener或将其分配给错误的元素或类似的东西造成的。 But I've tried quite a couple of things and it didn't change. 但是我尝试了很多事情,但并没有改变。 Can maybe someone tell me, what I've done wrong? 也许有人可以告诉我,我做错了吗?

UPDATE: 更新:

The code of the function "mouseClick" that is a member of RingChart and is called in the mouse listener: 作为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
            }
        }
    }
}

Again, the hierarchy of my classes: RingChart --contains a--> Canvas --got a--> MouseListener. 同样,我的类的层次结构是:RingChart-包含一个-> Canvas --got一个-> MouseListener。 The shapes in this function are shapes that have been painted on the canvas c. 此功能中的形状是已在画布c上绘制的形状。 Now I want to check, if the user has clicked on one of them. 现在,我想检查用户是否单击了其中之一。 So as I thought, the shapes should be in canvas-coordinates and the event position should be in canvas-coordinates and everything should fit together. 因此,正如我认为的那样,形状应该在画布坐标中,事件位置应该在画布坐标中,并且所有内容都应该放在一起。 But it doesn't. 但事实并非如此。 Now user MadProgrammer told me, to use the ConvertMouseEvent function. 现在,用户MadProgrammer告诉我要使用ConvertMouseEvent函数。 But I currently don't see which exact way I should use this sensibly. 但是我目前不知道应该明智地使用哪种确切方法。

UPDATE: 更新:

I found a solution: All I had to do is adding the canvas not directly to the JFrame but to the ContentPane of the JFrame instead: 我找到了解决办法:我不得不这样做是不是直接添加画布到JFrame ,但到ContentPane的的JFrame ,而不是:

So instead: 所以与其:

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

I do: 我做:

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

Then I give the MouseListener to the ContentPane . 然后,将MouseListener赋予ContentPane

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

I don't know, if this is an elegant solution, but it works. 我不知道这是否是一种优雅的解决方案,但是它可以工作。

The mouse event is automatically converted to be relative to the component that it occurred in that is, point 0x0 is always the top left corner of the component. 鼠标事件将自动转换为相对于其发生的组件,即,点0x0始终是组件的左上角。

By using RingChart r = ((Canvas)e.getSource()).getParent() , you've effectively changed the reference, which now means the location is no longer valid. 通过使用RingChart r = ((Canvas)e.getSource()).getParent() ,您已经有效地更改了引用,这意味着该位置不再有效。

You need to convert the location so that its coordinates are in the context of the parent component. 您需要转换位置,以便其坐标位于父组件的上下文中。 Take a look at SwingUtilities.convertMouseEvent(Component, MouseEvent, Component) 看看SwingUtilities.convertMouseEvent(Component,MouseEvent,Component)

UPDATE with PICTURES 图片更新

Lets take this example... 让我们以这个例子为例...

样品

The blue box has a relative position of 50px x 50px to the red box. 蓝色框相对于红色框的相对位置为50px x 50px。 If you click in the blue box, lets say at 25x25, the mouse coordinates will be relative to the blue box (0x0 will be the top left of the blue box). 如果单击蓝色框,可以说是25x25,则鼠标坐标将相对于蓝色框(0x0将位于蓝色框的左上方)。

If you then pass this event to the red box and try and use the coordinates from it, you will find that the coordinates will now be half way between the top left of the red box and the blue box, because the coordinates are context sensitive. 然后,如果将此事件传递给红色框并尝试使用它的坐标,您会发现坐标现在位于红色框左上方和蓝色框的中间,因为坐标是上下文相关的。

In order to get it to work, you need to translate the mouse events location from the blue box to the red box, which would make it 75x75 为了使其正常工作,您需要将鼠标事件的位置从蓝色框转换为红色框,这使其变为75x75

Now, I don't know what you're doing when you pass the mouse event to the RingChart so I'm only guessing that this is the issue you're facing. 现在,当您将鼠标事件传递给RingChart时,我不知道您在做什么,因此我只是在猜测这就是您面临的问题。

UPDATED with Click Code 点击代码已更新

Okay, lets say, you have a Canvas at 100x100. 好的,假设您有一个100x100的Canvas You click on that Canvas at 50x50. 您以50x50单击该Canvas You then pass that value back up the chain. 然后,您将该值传递回链中。

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
            }
        }
    }
}

UPDATED 更新

I think you have your references around the wrong way... 我认为您的参考文献有误...

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

I think it should read something like 我认为它应该读类似

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

UPDATE with Code Example 使用代码示例进行更新

Okay, so, I put this little example together... 好吧,我把这个小例子放在一起...

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);
    }
}

Basically, it will paint three points. 基本上,它将绘制三个点。 The point that the mouse was clicked (relative to the source of the event), the unconverted point in the parent container and the converted point with the parent container. 单击鼠标的点(相对于事件的源),父容器中的未转换点以及父容器中的转换点。

在此处输入图片说明

The next thing you need to do is determine the mouse location is actually been converted, failing that. 您需要做的下一件事是确定鼠标位置实际上已转换,但失败了。 I'd probably need to see a working example of your code to determine what it is you're actually doing. 我可能需要查看您的代码的工作示例,才能确定您实际上在做什么。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM