Dragging A Circle on a JFrame - java

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

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.

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.

Related

Java snap to grid

Okay so I'm trying to make things snap to a grid I have.
This is how I snap to the screen itself:
int finalCalcX = (mouseX / Handler.gridSpace32) * Handler.gridSpace32;
int finalCalcY = (mouseY / Handler.gridSpace32) * Handler.gridSpace32;
The variable names speak for themselves I think.
Mouse coordinates divided by my tiles being 32x32 pixels, times that again to get the snap-to-grid functionality.
Now this works fine for the screen, but when I want to add it to the "map" itself, I can't just add my map x and y offsets to that, it gets messed up.
I've played around with it for about two days, and I also got it to snap to the map itself, but when I'm say, halfway in the map on both axis, the mouseX and mouseY messes the grid thing up.
It's kind of hard for me to explain, but the offset from the 0, 0 (every origins position, even the screen) PLUS the maps offset when you move away from the origin, gets added to the distance between the cursor itself and the transparent snap-to-grid tile that I'm using to test.
Basically the offset between the maps origin, and the camera, is for some reason the same offset between the cursor and the transparent tile. So the further into the map i move, the further away the tile gets from the cursor, and eventually moves outside the screen width and height...
When I move further into the map, I want the snap-to-grid functionality to stay corret, no matter where on the map I am.
Render method:
for (int y = startY; y < endY; y++) {
for (int x = startX; x < endX; x++) {
gridSpace(graphics, (int) (x * Handler.gridSpace32 - handler.getCamera().getOffsetX()),
(int) (y * Handler.gridSpace32 - handler.getCamera().getOffsetY()));
checkHighlight(graphics);
}
}
The gridSpace is the grid itself.
Here is what's in the highlight at the moment:
int finalCalcX = (mouseX / Handler.gridSpace32) * Handler.gridSpace32;
int finalCalcY = (mouseY / Handler.gridSpace32) * Handler.gridSpace32;
graphics.setColor(new Color(100, 200, 100, 3));
graphics.fillRect(finalCalcX, finalCalcY, Handler.gridSpace32, Handler.gridSpace32);
Sorry for my terrible explanation skills, but that's the best I can do.
What am I doing wrong?
I think I recreated what you are getting, and the problem lies with usage of incorrect sign in calculations of camera position and translation. Executing code below you should get similar behavior to what you described, while uncommenting commented lines (while commenting out the ones that follow them) should give you correct behavior.
package test;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import javax.swing.*;
public class SnapToGrid extends JPanel implements MouseMotionListener{
private int[] camera;
private int[] mouse;
private final int gridSize = 16;
SnapToGrid() {
camera = new int[2];
mouse = new int[2];
setFocusable(true);
addMouseMotionListener(this);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
//g2.translate(-camera[0], -camera[1]);
g2.translate(camera[0], camera[1]);
//draw background
for (int i = 0; i < 9; i++)
for (int j = 0; j < 9; j++)
{
Color c = ((j*9) + i) % 2 == 0 ? Color.black : Color.white;
g2.setColor(c);
g2.fillRect(i*gridSize, j*gridSize, gridSize, gridSize);
}
g2.setColor(Color.blue);
int[] snappedPos = getSnappedMousePosition();
g2.fillRect(snappedPos[0], snappedPos[1], gridSize, gridSize);
}
private int[] getSnappedMousePosition() {
return new int[] {
camera[0] + mouse[0] - ((camera[0] + mouse[0]) % gridSize),
camera[1] + mouse[1] - ((camera[1] + mouse[1]) % gridSize)
};
}
public static void main (String[] args) {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new SnapToGrid());
frame.pack();
frame.setVisible(true);
}
#Override
public void mouseDragged(MouseEvent e) {
//camera[0] -= e.getX() - mouse[0];
//camera[1] -= e.getY() - mouse[1];
camera[0] += e.getX() - mouse[0];
camera[1] += e.getY() - mouse[1];
mouse[0] = e.getX();
mouse[1] = e.getY();
repaint();
}
#Override
public void mouseMoved(MouseEvent e) {
mouse[0] = e.getX();
mouse[1] = e.getY();
repaint();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(gridSize * 18, gridSize * 18);
}
}
One thing that I really don't like with your code, is that you are not using transformations. Using transformations allows you to separate world from viewport, submits to far easier debugging of issues like this here, and most importantly, if you want to add things like scaling or rotating later on, you only need to add few lines, as opposed to rewriting half of your render method.

Mouse Drag To Zoom implementation Java - not on click

I have written some code to implement a drag to zoom (i.e. draw a rectangle with the mouse and then zoom into this area so it fills the container) on a JPanel. However, before I wrote this into the Listener, I had a "mouseClicked" method, which is still there. This method performed a different function, simply returning the coords of the click.
Unfortunately, I mistakenly implemented the zoom function using MousePressed, which thus runs at the same time as the mouseClicked event. This means that when I click the mouse to get the coordinates, it also zooms in on a very small rectangle. Is there anyway to change this?
Thanks in advance for any suggestions, and please let me know if you need anything clarifying!
Here is the listener class:
class panelClickListener extends MouseAdapter implements MouseMotionListener {
JLabel toShowCoords, rangeLabel;
juliaPanel panelJulia;
mandPanel panelMand;
int startX, startY, currentX, currentY;
boolean dragging;
ComplexNumber startRange, endRange;
public panelClickListener(JLabel toShowCoordsIn, juliaPanel panelJuliaIn, mandPanel panelMandIn, JLabel rangeLabelIn) {
toShowCoords = toShowCoordsIn;
panelJulia = panelJuliaIn;
panelMand = panelMandIn;
rangeLabel = rangeLabelIn;
}
public void mouseClicked(MouseEvent e) {
int x = e.getX();
int y = e.getY();
userSelectedPoint = pointsToDrawOn[x][y];
toShowCoords.setText("Number Selected: " + userSelectedPoint.getReal() + " , " + userSelectedPoint.getImaginary());
panelJulia.setFixedNumber(pointsToDrawOn[x][y]);
panelJulia.repaint();
}
public void mousePressed(MouseEvent event) {
Point point = event.getPoint();
startX = point.x;
startY = point.y;
dragging = true;
}
public void mouseReleased(MouseEvent event) {
dragging = false;
startRange = pointsToDrawOn[startX][startY];
endRange = pointsToDrawOn[currentX][currentY];
maxVal = endRange;
minVal = startRange;
rangeLabel.setText("Real axis: " + minVal.getReal() + "," + maxVal.getReal() + " - Imaginary axis: " + minVal.getImaginary() + "," + maxVal.getImaginary());
populatePointArray();
panelMand.repaint();
}
public void mouseDragged(MouseEvent event) {
Point p = event.getPoint();
currentX = p.x;
currentY = p.y;
}
}
dragging should only be set to true when mouseDragged is called, then when mouseReleased is called, you need to check to see if dragging is true before processing the zoom
There's some theory to go with that, mouseClicked will only be called if the point of the mouse is the same for mousePressed and mouseReleased.
So in a click operation you can expect mousePressed, mouseReleased and mouseClicked to be called (I believe in that order, but you'd better test it).
In a drag operation, you can expect mousePressed, mouseDragged and mouseReleased to be called

Gap between mouse on screen and JPanel on Dragndrop

I use JPanel to draw a square on the screen.
When I use MouseDragged it works fine and goes wherever I want, almost. Each time I click on the square, the square automatically moves and the top left corner goes right under the mouse.
How should I do so that the square doesn't replace itself and stays right under the mouse ?
Thanks for any help.
Keep an account of the difference between top-left coordinates of Component which you are
moving and mousePressed location.
And when you get new position, just subtract that difference to it.
Here I have tried to explain it through coding. Let myJPanel be the component you want to
move. Then here is the MouseAdapter that can work for you. New position is stored in
newPosition variable.
new MouseAdapter(){
int diffx = 0, diffy = 0;
public void mousePressed(MouseEvent e) {
Point topLeft = myJPanel.getLocation();
Point mouseDn = e.getPoint();
diffx = mouseDn.x - topLeft.x;
diffy = mouseDn.y - topLeft.y;
}
public void mouseDragged(MouseEvent e) {
Point mouseDr = e.getPoint();
int newX = mouseDr.x - diffx;
int newY = mouseDr.y - diffy;
Point newPosition = new Point(newX, newY);
}
};

JFrame Simple Application

I am working on creating a game for fun that basically is a simplistic representation for evolution.
Essentially, when I click on my moving ball it changes color. The goal is to continuously change until it matches the background color meaning the ball is successfully hidden. Eventually I will add more balls but I am trying to figure out how to change its color upon a mouse click. I have created the moving ball animation so far.
How can I change the ball color when I click on the ball?
Code:
public class EvolutionColor
{
public static void main( String args[] )
{
JFrame frame = new JFrame( "Bouncing Ball" );
frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
BallPanel bp = new BallPanel();
frame.add( bp );
frame.setSize( 1800, 1100 ); // set frame size
frame.setVisible( true ); // display frame
bp.setBackground(Color.YELLOW);
} // end main
}
class BallPanel extends JPanel implements ActionListener
{
private int delay = 10;
protected Timer timer;
private int x = 0; // x position
private int y = 0; // y position
private int radius = 15; // ball radius
private int dx = 2; // increment amount (x coord)
private int dy = 2; // increment amount (y coord)
public BallPanel()
{
timer = new Timer(delay, this);
timer.start(); // start the timer
}
public void actionPerformed(ActionEvent e)
// will run when the timer fires
{
repaint();
}
public void mouseClicked(MouseEvent arg0)
{
System.out.println("here was a click ! ");
}
// draw rectangles and arcs
public void paintComponent( Graphics g )
{
super.paintComponent( g ); // call superclass's paintComponent
g.setColor(Color.red);
// check for boundaries
if (x < radius) dx = Math.abs(dx);
if (x > getWidth() - radius) dx = -Math.abs(dx);
if (y < radius) dy = Math.abs(dy);
if (y > getHeight() - radius) dy = -Math.abs(dy);
// adjust ball position
x += dx;
y += dy;
g.fillOval(x - radius, y - radius, radius*2, radius*2);
}
}
Take a look at How to Write a Mouse Listener.
Don't make decisions about the state of the view in the paintComponent, painting can occur for any number of reasons, many you don't control. Instead, make theses decisions within the actionPerformed method of your Timer
You may also wish to consider changing your design slightly. Rather then having the balls as JPanels, you create a virtual concept of a ball, which contains all the properties and logic it needs and use the JPanel to paint them. You could then store them in some kind of List, each time you register a mouse click you could iterate the List and check to see if any of the balls were clicked
Have look at Java Bouncing Ball for an example
Instead of hard-coding the color (g.setColor(Color.red);), why not create an attribute:
g.setColor(currentColor);
Then when you click in the circle area, change currentColor.

How do I add keystroke Z to keyPressed(KeyEvent e) to jump/repaint the ball to a new random location? (KeyListener demo used.)

I have limited Java experience, especially when it relates to graphics. I've had great help on this problem so far. It's starter code (at the bottom) which works fine as I tested it in Eclipse. I'm using it from a class to teach a high school student. As per the instructions below in comments, would someone know an easy way to extend the already simple program to cause the ball to jump/move/repaint to a random new location, after pressing the Z button? I'm thinking of adding the following code to the keyPressed(KeyEvent e) method:
else if(keyCode == KeyEvent.VK_Z)
{
//Not sure what to add here. Just want to change X and Y coordinates to
//anything, so long as the program works and stays fairly simple
//for now.
//----> g.fillOval(x + radius, y + radius, 2 * radius, 2 * radius);
// this line above says "g can not be resolved".
}
.
Any tips or ideas to keep the program simple would be very much appreciated. Thank you!
.
import java.awt.*;
import java.awt.event.*; // #1
import javax.swing.*;
/******************************************************************************
*
* KeyListenerDemo.java
* Demonstrates getting keyboard input using the KeyListener interface.
*
* Program 18: Extend this program by adding a few more keystroke commands:
* z (VK_Z) - Cause the ball to jump to a random new location.
* s (VK_S) - Make the ball smaller - multiply its diameter 1/2.
* b (VK_B) - Make the ball bigger - multiply its diameter by 2.
* c (VK_C) - Change the color (in any way you'd like).
*
* In addition, modify the program to ensure the following:
* - The ball goes all the way to the edge of the screen but stays
* completely on the screen.
* - If a doubled diameter doesn't fit, make it as large as possible.
* - Be sure the ball never completely disappears.
*
*****************************************************************************/
public class KeyListenerDemo extends JFrame
implements KeyListener // #2
{
// Class Scope Finals
private static final int SCREEN_WIDTH = 1000;
private static final int SCREEN_HEIGHT = 800;
private static final int START_RADIUS = 25;
private static final int START_X = 100;
private static final int START_Y = 100;
private static final int STEP_SIZE = 10;
// Class Scope Variables
private static int x = START_X; // x at center of the ball
private static int y = START_Y; // y at center of the ball
private static int radius = START_RADIUS; // radius of the ball
// Methods
/**
* Create the window and register this as a KeyListener
*
* #param args
*/
public static void main (String[] args)
{
// Set up the JFrame window.
KeyListenerDemo gp = new KeyListenerDemo();
gp.setSize(SCREEN_WIDTH, SCREEN_HEIGHT);
gp.setVisible(true);
gp.addKeyListener(gp); // #3
// If this class had a constructor and you moved this line into
// that constructor it could not refer to gp since that variable
// is local to this method. Instead you would write::
// addKeyListener(this);
}
/**
* Called when a key is first pressed
* Required for any KeyListener
*
* #param e Contains info about the key pressed
*/
public void keyPressed(KeyEvent e) // #4A
{
int keyCode = e.getKeyCode();
if (keyCode == KeyEvent.VK_LEFT)
{
x = x - STEP_SIZE;
}
else if (keyCode == KeyEvent.VK_RIGHT)
{
x = x + STEP_SIZE;
}
else if (keyCode == KeyEvent.VK_UP)
{
y = y - STEP_SIZE;
}
else if (keyCode == KeyEvent.VK_DOWN)
{
y = y + STEP_SIZE;
}
repaint();
}
/**
* Called when typing of a key is completed
* Required for any KeyListener
*
* #param e Contains info about the key typed
*/
public void keyTyped(KeyEvent e) // #4B
{
}
/**
* Called when a key is released
* Required for any KeyListener
*
* #param e Contains info about the key released
*/
public void keyReleased(KeyEvent e) // #4C
{
}
/**
* paint - draw the figure
*
* #param g Graphics object to draw in
*/
public void paint(Graphics g)
{
g.setColor(Color.white);
g.fillRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
g.setColor(Color.blue);
g.fillOval(x - radius, y - radius, 2 * radius, 2 * radius);
}
}
Try doing something like this:
public void keyPressed(KeyEvent e){
if(e.getKeyCode == KeyEvent.VK_Z){
moveRand();
//Assuming that is the method for moving the ball randomly.
}
}
and than
public void moveRand(Graphics2D g){
g.drawOval(Random.nextInt() / window.getWidth() + 1, Random.nextInt() / window.getHight() + 1, ballWidth, ballHight);
}
Hope this helped.
The reason that g cannot be resolved is that it does not exist in the keyPressed() method. g is what is called a parameter of the method paint(). This means that you can only refer to it within the method paint(). You can declare another variable named g outside the method paint(), but it won't be the same g. It won't even have to have the same type.
That said, you do not need to reference g directly at all in order to solve your problem. The paint() method already handles drawing a circle. You just need to change where the circle is and tell the application to repaint it. If you assign new values to x and y and then invoke repaint(), the circle will move. Notice that repaint() is already called at the end of the keyPressed() method.
Here is an example. Replace the code on the right hand side of the equals with whatever may be appropriate to your problem.
else if(keyCode == KeyEvent.VK_Z)
{
x = x + radius;
y = y + radius;
}
If you look at the other if/else clauses, you will see that this is what they are doing as well.

Categories