Java MouseEvent position is inaccurate

前端 未结 1 1279
别跟我提以往
别跟我提以往 2021-01-03 17:11

I\'ve got a problem in Java using a \"canvas\" class I created, which is an extended JPanel, to draw an animated ring chart. This chart is using a MouseLi

相关标签:
1条回答
  • 2021-01-03 17:52

    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.

    By using RingChart r = ((Canvas)e.getSource()).getParent(), you've effectively changed the reference, which now means the location is no longer valid.

    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)

    UPDATE with PICTURES

    Lets take this example...

    Sample

    The blue box has a relative position of 50px x 50px to the red box. 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).

    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

    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.

    UPDATED with Click Code

    Okay, lets say, you have a Canvas at 100x100. You click on that Canvas at 50x50. 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.

    enter image description here

    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.

    0 讨论(0)
提交回复
热议问题