views:

237

answers:

2

I am trying to have a circle appear on the screen, then when the user clicks INSIDE the circle, enable the ability for them to drag the circle where the mouse goes while it is being pressed.

This is the code i have so far, the drag works, but it is allowing the user to drag without them pressing the inside of the circle, just when anywhere on the screen is pressed.

I hope i am not too confusing

here's the code i have, please if someone could just tell me the code that needs to be corrected, it will save me anymore hours.

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

public class DragCircle extends JFrame {
    private static final long serialVersionUID = 1L;

    public static int size = 400;

    public static int r = 10;

    private int x;

    private int y;

    private int cX;

    private int cY;

    private int dX;

    private int dY;

    private MouseHandler mh;

    boolean isCircleClicked = false;

    public static void main(String[] args) {
     DragCircle c1 = new DragCircle();
     c1.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

    }

    public DragCircle() {

     super("Drag circle");

     cX = r + 100;
     cY = r + 100;

     mh = new MouseHandler();
     addMouseListener(mh);
     addMouseMotionListener(mh);

     setSize(size, size);
     setVisible(true);

    }

    public void paint(Graphics g) {
     super.paint(g);
     g.setColor(Color.RED);
     g.fillOval(cX, cY, r * 2, r * 2);

    }

    private class MouseHandler extends MouseAdapter implements
      MouseMotionListener {
     public void mousePressed(MouseEvent me)

     {

      if ((cX - me.getX()) * (cX - me.getX()) + (cY - me.getY())
        * (cY - me.getY()) < r * r) {
       isCircleClicked = true;
      }
     }

     public void mouseDragged(MouseEvent me) {
      if (isCircleClicked) {

       x = me.getX() - dX;
       y = me.getY() - dY;
       cX = x + r;
       cY = y + r;
       repaint();
      }
     }

     public void mouseReleased(MouseEvent e) {
      isCircleClicked = false;
     }

    }

}
A: 

The structure of you program is wrong. You should never override the paint(...) method of a JFrame. That is an old AWT trick and should NOT be used with Swing.

Read the section from the Swing tutorial on Custom Painting for an example of the proper way to do painting. The basics are to override the paintComponent(...) method of a JPanel and then add the panel to the content pane of the frame.

With regards to you question a better solution is to create an Elllipse2D object to represent your circle. Then the custom painting can use the drawShape(...) method of the Graphics2D class. Then in you MouseListener code you can use the Shape.contains(...) method to see if the mouse was clicked on the circle.

camickr
Hit-testing a circle is trivial though and the hit-testing code looks correct to me. I assume it only happens if the OP first moves the circle and *then* tries to drag outside it (where `isCircleClicked` is still set to `true`, thus moving the circle.
Joey
Apparently its not trivial as you had to modify the code to take into account the proper coordinates. Thats why it is simpler to use a Shape Object so you don't have to worry about the details and you just invoke the contains(...) method. I try to recommend solutions that use the API rather than reinvent the wheel.
camickr
+1  A: 

Your one problem is with

public void mouseDragged(MouseEvent me) {
    if (isCircleClicked = true) {

What you're doing here is setting isCircleClicked to true every time you drag the mouse. Also this statement evaluates to true which is why you can drag anywhere and move the circle. Change this to

 if (isCircleClicked) {

and you should be fine here.

The next problem is that you never reset isCircleClicked to false. You should be doing this either in mouseReleased or change your mousePressed as follows:

public void mousePressed(MouseEvent me) {
    isCircleClicked =
        (cX - me.getX()) * (cX - me.getX()) +
        (cY - me.getY)) * (cY - me.getY()) < r * r;
}

which will set isCircleClicked accordingly.

There is still something to do, though. In the current form you need to start dragging to the upper-left of the center point, as illustrated below:

+------+
|      |
|    .-|-.
|   /  |  \
+------+  |
    \     /
     '-_-'

This is because of your drawing: fillOval takes an upper-left corner of the oval and a width and height of the bounding rectangle. It's not the center point and the respective diameters. Hence you need to adapt his as follows:

g.fillOval(cX - r, cY - r, r * 2, r * 2);

Note the offset by the radius to the left and top.

Furthermore, your dragging code needs a bit more work. You are currently assuming that the user drags the circle's center. What you need to do is save the coordinates of the mouse click and then move the circle based on the mouse's movement relative to the last point. Currently you're moving relative to the circle's center so for the movement to be nice you have to start dragging exactly in the center of the circle. I'll leave that as an exercise for you :-)

Besides, your listener class already inherits from MouseAdapter so you don't need to explicitly implement the MouseMotionListener since MouseAdapter implements it already.

Joey
i just update the code.. i edited the post to show what i did.. but its acting weird, when i run it the circle is not being dragged at all
Madison
Try dragging a little to the left and top of the circle. It works for me. Now I just have to find out *why* it's not hit-testing the circle correctly.
Joey
Found the problem.
Joey
Thanks! its good now :) appreciate it.
Madison