跟随鼠标位置的圆圈直到被点击

Circle following the mouse position until clicked

我想做一个项目,包括在 Java GUI 上画一个圆。单击圆圈或圆圈周围的区域时,圆圈应 "stick" 到光标并跟随它,直到再次单击鼠标为止。然后圆圈应该停留在您点击的位置。

我做了一切,直到程序检测到您点击了圆圈。这里的圆是g2g2.fillOval方法做的图

有两个类:

MainClass.java

public class MainClass {

    public static void main(String[] args){

        ExampleGUI g = new ExampleGUI();

    }
}

ExampleGUI.java

import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;

public class ExampleGUI extends JFrame {

    Graphics2D g2;

    Point point = new Point(150,150);

    ExampleGUI() {
        MouseListener ml = new MouseListener() {

            @Override
            public void mouseClicked(MouseEvent e) {

            }

            @Override
            public void mousePressed(MouseEvent e) {
                Point clicked = new Point(e.getLocationOnScreen().x - getX(),e.getLocationOnScreen().y - getY());

                if(clickedaroundpoint(clicked)){
                    System.out.println("Clicked on Point");
                }
            }

            @Override
            public void mouseReleased(MouseEvent e) {

            }

            @Override
            public void mouseEntered(MouseEvent e) {

            }

            @Override
            public void mouseExited(MouseEvent e) {

            }
        };

        this.addMouseListener(ml);
        setTitle("FlamingoBall");
        setSize(300,300);
        setResizable(false);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setLocationRelativeTo(null);
        this.setVisible(true);
    }

    private boolean clickedaroundpoint(Point clicked) {
        if(Point.distance(point.x+2,point.y+2,clicked.x,clicked.y)<=5){
            return true;
        }
        return false;
    }

    public void paint(Graphics g) {
        super.paintComponents(g);
        g2 = (Graphics2D) g;
        g2.setColor(Color.RED);
        g2.fillOval(point.x,point.y,7,7);
    }
}

请告诉我继续前进的最佳方法是什么。

您需要添加 MouseMotionListener 并实施 mouseMoved(),或者如果您愿意,可以实施 mouseDragged()

有多种方法可以做到这一点。这取决于您是要单击移动还是拖动。区别是:

  • 点击-移动:按下、松开、移动、按下、松开
  • 拖动:按下、移动、释放

点击移动

在那种情况下你需要实施

  • MouseListener 观察者的 mouseClicked() 处理程序切换 boolean 并记住移动的开始位置。
  • MouseMotionListener观察者的mouseMoved()执行实际移动。

像这样:

class Mover implements MouseListener, MouseMotionListener {
    private boolean moving;
    private Point movementOrigin;
    public void mouseClicked(MouseEvent e) {
        if (moving = !moving)
            movementOrigin = e.getPoint();
    }
    public void mouseMoved(MouseEvent e) {
        if (!moving) return;
        Point pos = e.getPoint();
        Point delta = new Point(pos.getX() - movementOrigin.getX(), pos.getY() - movementOrigin.getY());
        // TODO Relocate the circle with that delta
        repaint();
    }
}

拖动

在这种情况下,您需要实施 * 拖动开始位置的 MouseListener 观察者的 mousePressed() 处理程序。 * MouseMotionListener 观察者的 mouseDragged() 处理程序,用于跟随拖动运动。

与之前代码的唯一区别是您不需要那个布尔切换。

关于原始答案的注释

在我原来的回答中,我建议在 MouseListener 的相应事件中动态 add/remove MouseMotionListener。我不再认为这是一个好主意,因为没有 "cheap" 检测观察者是否已经注册的方法,因此无论如何都需要一个布尔值。

代码注意事项

我认为从 paint() 方法初始化 Graphics2D 类型的字段不是一个好主意。屏幕上 Graphics 对象的有效性可能绑定到 repaint() 调用树。在 repaint() 调用树之外调用它的方法可能会导致未定义的行为。 Graphics 对象的生命周期是 repaint() 调用树,而不是 ExampleGUI 对象,代码应该通过不在字段中缓存它来反映这一点。

扩展 UI 类(您的 extends JFrame)使用是一种反模式,违反了 Liskov 替换原则。继承(仍然)被过度使用。考虑使用委托而不是继承。