fihdi
fihdi

Reputation: 155

Circle following the mouse position until clicked

I want to make a project that includes drawing a circle on a Java GUI. When the circle or an area around the circle is clicked, the circle should "stick" to the cursor and follow it until a mouse click happens again. Then the circle should stay where you clicked.

I made everything up to the point where the program detects that you clicked onto the circle. Circle here is a graphic made by g2 with the g2.fillOval method.

There are two classes:

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

Please let me know what the best way is to keep going.

Upvotes: 0

Views: 836

Answers (1)

Christian Hujer
Christian Hujer

Reputation: 17935

You need to add a MouseMotionListener and implement mouseMoved(), or mouseDragged() if you like.

There are multiple ways how you could do that. It depends on whether you want click-move or drag. The difference is:

  • click-move: press, release, move, press, release
  • drag: press, move, release

Click-move

In that case you need to implement

  • The mouseClicked() handler of the MouseListener observer to toggle a boolean and remember the start position of the movement.
  • The mouseMoved() of the MouseMotionListener observer to perform the actual movement.

Like this:

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

Drag

In that case, you need to implement * The mousePressed() handler of the MouseListener observer for the start position of the drag. * The mouseDragged() handler of the MouseMotionListener observer for following the drag movement.

The only difference to the code before is that you do not need that boolean toggle.

Note about original answer

In my original answer I suggested to dynamically add/remove the MouseMotionListener in the corresponding event of the MouseListener. I no longer think that this is a good idea, because there is no "cheap" way to detect whether an observer was already registered, and thus a boolean would be needed anyway.

Note about the code

I don't think that having a field of type Graphics2D initialized from a paint() method is a good idea. The validity of an on-screen Graphics object is probably bound to the repaint() call tree. Calling its methods outside of the repaint() call tree is probably going to result in undefined behavior. The lifetime of the Graphics object is the repaint() call tree, not the ExampleGUI object, and the code should reflect that by not caching it in a field.

Extending UI classes (your extends JFrame) for usage is an anti-pattern and violates the Liskov Substitution Principle. Inheritance is (still) overused. Consider using delegation instead of inheritance.

Upvotes: 1

Related Questions