I'm attempting to make a little paint app where you input the color you want, and change the size of the brush. I did this, and it works, but if I try to paint quickly, the app doesn't update for a short period of time, leaving dots looking like this:
60 times per second (because 60 times per second should be enough to not leave dots/gaps. See tick method. As a test, I tried putting no limit on the ticksPerSecond, and I got the same output), if the user is causing a mouseDragged or mousePressed event, I add a an instance of the Circle class (seen below)
class Circle {
int x, y;
int radius;
Color color;
Circle(int x, int y, int radius, Color color) {
this.x = x;
this.y = y;
this.radius = radius;
this.color = color;
}
}
My tick method: (Called 60 times each second)
private void tick() {
this.middle = this.brush.radius / 2; // Used because java.awt.Graphics will paint from your cursors xy, which will paint the circle down and to the right of the cursor
if (this.frame.isDragging) {
add(new Circle(this.frame.x - this.middle, this.frame.y - this.middle, this.brush.radius, this.brush.color));
}
}
To render, I just run through a foreach loop, seen below
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g); // Paint over everything else
try {
for (Circle circle : this.circles) {
g.setColor(circle.color);
g.fillOval(circle.x, circle.y, circle.radius, circle.radius);
}
} catch (ConcurrentModificationException ignore) {} //TODO MAKE THIS BETTER
}
Ignore that I'm catching a ConcurrentModifictionException, this is a temporary fix because I'm adding to a list that's being read here. (It only causes the paint to flash while I am painting, so it works for now)
I was asked for my Frame, here it is (You probably don't need this, but I included any code you needed. I remove implement methods that I didn't use):
class Frame extends JFrame implements MouseListener, MouseMotionListener {
int x, y;
boolean isDragging, isMouseOnScreen;
Frame(int width) {
this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
this.getContentPane().setPreferredSize(new Dimension(width, width / 3 * 2));
this.setResizable(false);
this.pack();
this.setLocationRelativeTo(null);
BufferedImage image = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB);
Cursor cursor = this.getToolkit().createCustomCursor(image, new Point(0, 0), "Cursor");
this.setCursor(cursor);
this.setFocusable(true);
this.requestFocus();
this.addMouseListener(this);
this.addMouseMotionListener(this);
this.x = this.y = 0;
this.isDragging = this.isMouseOnScreen = false;
}
#Override
public void mouseDragged(MouseEvent e) {
this.x = e.getX() - 5;
this.y = e.getY() - 35;
}
#Override
public void mouseMoved(MouseEvent e) {
this.x = e.getX() - 5;
this.y = e.getY() - 35;
}
#Override
public void mousePressed(MouseEvent e) {
this.isDragging = true;
}
#Override
public void mouseReleased(MouseEvent e) {
this.isDragging = false;
}
#Override
public void mouseEntered(MouseEvent e) {
isMouseOnScreen = true;
}
#Override
public void mouseExited(MouseEvent e) {
isMouseOnScreen = false;
}
}
Related
As the title states, I am trying to detect a mouse hover over an object that is not a JComponent.
Right now I have a window with a green JPanel. When you left-click on this JPanel you create a point.
What I am trying to do is to have extra information displayed when I hover over these points. However, I have no idea how to even begin detecting if I am hovering my mouse over a point. I tried looking into the MouseListener interface but I could not find any examples of people using MouseListener with an object. I have only seen people use MouseListener with JComponents. I would preferably like to have this mouse hover detection code in my Point class if possible to keep my code clean.
JPanel Code
class Map extends JPanel implements MouseListener {
public static ArrayList<Point> points = new ArrayList<Point>(); //array for the points
public Map() {
this.setBackground(Color.green);
this.setPreferredSize(new Dimension(1280, 720));
this.addMouseListener(this);
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D graphics = (Graphics2D) g;
drawPoints(graphics);
}
private void drawPoints(Graphics2D graphics) {
for(int i = 0; i < points.size(); i++) {
points.get(i).drawPoint(graphics);
}
}
#Override
public void mouseClicked(MouseEvent e) {
}
#Override
public void mousePressed(MouseEvent e) {
if(e.getButton() == MouseEvent.BUTTON1) { //Left Click
points.add(new Point(e.getX(), e.getY()));
repaint();
}
else if(e.getButton() == MouseEvent.BUTTON3) { //right click
for(int i = points.size() - 1; i >= 0; i--) { //loop backwards so if points overlap remove the one on top first
Point current = points.get(i);
if( Math.abs( e.getX() - current.x ) < current.size/2 && Math.abs( e.getY() - current.y ) < current.size/2 ) {
points.remove(i);
repaint();
break;
}
}
}
}
#Override
public void mouseReleased(MouseEvent e) {
}
#Override
public void mouseEntered(MouseEvent e) {
}
#Override
public void mouseExited(MouseEvent e) {
}
}
Point Code
public class Point {
public int x, y;
public int size = 10;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
public Point() {
this.x = 0;
this.y = 0;
}
public void drawPoint(Graphics2D graphics) {
graphics.setPaint(Color.black);
graphics.setStroke(new BasicStroke(5));
graphics.drawOval(x - (size/2), y - (size/2), size, size);
graphics.setPaint(Color.red);
graphics.fillOval(x - (size/2), y - (size/2), size, size);
}
public void drawInfo(Graphics2D graphics) {
graphics.drawString("test", x, y);
}
}
I had the same issue with a Packet-Tracer-Like program, where I drew rects.
If they are just points, I would check if the mouse cords are the same as the point cords when the mouse is moved.
#Override
public void mouseMoved(MouseEvent e) {
entered = false;
if(point.x == e.getX() && point.y == e.getY()){
entered = true;
}
}
If although, like in my case, the drawn object has a width and a height, it gets messier.
#Override
public void mouseMoved(MouseEvent e) {
entered = false;
if((e.getX() <= point.x+width) && (e.getX() >= point.x)){
if((e.getY() <= point.y+height) && (e.getY() >= point.y)){
entered = true;
}
}
}
I'm trying to write a Pong applet in Java. When the user holds down either up or down, their paddle should move smoothly, but this isn't the case. It moves, then pauses, then starts moving again. Is there a way to stop this short pause from happening?
Main Class:
public class Main extends JApplet {
public DrawPanel dp = new DrawPanel(400, 400);
public void init(){
add(dp);
setSize(400, 400);
requestFocusInWindow();
Action moveDown = new AbstractAction(){
public void actionPerformed(ActionEvent e){
dp.player.y += 10;
dp.repaint();
}
};
dp.getInputMap().put(KeyStroke.getKeyStroke("pressed DOWN"), "move down");
dp.getActionMap().put("move down", moveDown);
}
}
DrawPanel Class:
public class DrawPanel extends JPanel {
public Paddle player;
public Paddle opponent;
public DrawPanel(int height, int width){
int y = (height / 2) - (height / 10);
int w = 15;
int h = height / 5;
player = new Paddle(2, y, 15, h, Color.white);
opponent = new Paddle(width - (w+2), y, 15, h, Color.white);
}
public void paintComponent(Graphics g){
super.paintComponent(g);
this.setBackground(Color.BLACK);
g.setColor(player.color);
g.fillRect(player.x, player.y, player.width, player.height);
g.setColor(opponent.color);
g.fillRect(opponent.x, opponent.y, opponent.width, opponent.height);
}
}
Paddle Class:
public class Paddle {
public int x, y, width, height;
public Color color;
public Paddle(int _x_, int _y_, int w, int h, Color c){
x = _x_;
y = _y_;
width = w;
height = h;
color = c;
}
}
The underlying issue is an impedance mismatch between the "instance" notion of a keyPressed and the "duration" notion of the movement.
Instead of trying to smooth that over in the view (which shouldn't have anything to do with the details of the game logic anyway), enhance the game model with api that's a better fit. F.i. add start/stop logic and bind those methods to keyPressed/keyReleased:
public class Paddle {
public void startMoveDown() {
// here goes the logic, f.i. starting a timer
// in the actionPerformed of that timer:
... moveUnitDown();
}
public void stopMoveDown() {
//...
}
protected void moveUnitDown() {
y+=unit;
// ideally, have listeners and notify them the change of y
}
}
// in the view:
Action startMoveDown = new AbstractAction(){
public void actionPerformed(ActionEvent e){
player.startMoveDown();
}
};
dp.getInputMap().put(KeyStroke.getKeyStroke("pressed DOWN"), "start move down");
dp.getActionMap().put("start move down", startMoveDown);
// in the view:
Action stopMoveDown = new AbstractAction(){
public void actionPerformed(ActionEvent e){
player.stopMoveDown();
}
};
dp.getInputMap().put(KeyStroke.getKeyStroke("released DOWN"), "stop move down");
dp.getActionMap().put("stop move down", stopMoveDown);
Here i have a code which draws a rectangle on the mouseClicked position using the paintComponent.I can get the output message but anything related to graphics and .draw() wont work.
Code:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public final class testclass extends JFrame {
static JPanel p;
Timer t;
int x = 1;
int y = 1;
int xspeed = 1;
int yspeed = 1;
public testclass() {
initComponents();
this.setBounds(100, 300, 500, 500);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
t.start();
this.add(p);
}
public void initComponents() {
final ActionListener action = new ActionListener() {
public void actionPerformed(ActionEvent evt) {
System.out.println("Hello!");
p.repaint();
}
};
t = new Timer(50, action);
p = new JPanel() {
public void paintComponent(Graphics g) {
super.paintComponent(g);
final Graphics2D gD = (Graphics2D) g;
moveBALL();
gD.drawOval(x, y, 25, 25);
p.addMouseListener(new MouseListener() {
#Override
public void mouseReleased(MouseEvent e) {
System.out.println("a");
}
#Override
public void mousePressed(MouseEvent e) {
System.out.println("b");
}
#Override
public void mouseExited(MouseEvent e) {
System.out.println("c");
}
#Override
public void mouseEntered(MouseEvent e) {
System.out.println("d");
}
#Override
public void mouseClicked(MouseEvent e) {
gD.drawRect(e.getX(), e.getY(), 10, 60);
gD.setColor(Color.green);
System.out.println("clicked");
}
});
}
void moveBALL() {
x = x + xspeed;
y = y + yspeed;
if (x < 0) {
x = 0;
xspeed = -xspeed;
} else if (x > p.getWidth() - 20) {
x = p.getWidth() - 20;
xspeed = -xspeed;
}
if (y < 0) {
y = 0;
yspeed = -yspeed;
} else if (y > p.getHeight() - 20) {
y = p.getHeight() - 20;
yspeed = -yspeed;
}
}
};
}
public static void main(String args[]) {
EventQueue.invokeLater(new Runnable() {
public void run() {
new testclass().setVisible(true);
p.setBackground(Color.WHITE);
}
});
}
}
What is the proper way to implement a mouseListener() in this program?
Thanks.
Some suggestions on current code:
Watch class naming scheme i.e testclass should be TestClass or even better Test (but thats nit picking). All class names begin with capital letter and each new word thereafter is capitalized.
Dont extend JFrame unnecessarily.
Dont call setBounds on JFrame rather use appropriate LayoutManager and/or override getPreferredSize() of JPanel and return dimensions which fits its content.
Always call pack() on JFrame before setting it visible (taking above into consideration).
Use MouseAdapter vs MouseListener
Dont call moveBall() in paintComponent rather call it in your Timer which repaints the screen, not only slightly better design but we also should not do possibly long running tasks in paint methods.
As for your problem I think your logic is a bit skewed.
One approach would see the Rectangle (or Rectangle2D) get replaced by its own custom class (which will allow us to store attributes like color etc). Your ball would also have its own class which has the method moveBall() and its attributes like x and y position etc. On every repaint() your JPanel would call the method to move the ball, the JPanel itself could wrap the moveBall() in its own public method which we could than call from the timer which repaints the screen.
Here is an example of your code with above fixes implemented (please analyze it and if you have any questions let me know):
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import javax.swing.*;
public class Test {
private MyPanel p;
private Timer t;
public Test() {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
initComponents();
frame.add(p);
frame.pack();
frame.setVisible(true);
t.start();
}
private void initComponents() {
final ActionListener action = new ActionListener() {
#Override
public void actionPerformed(ActionEvent evt) {
p.moveEntities();//moves ball etc
p.repaint();
}
};
t = new Timer(50, action);
p = new MyPanel();
p.addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
p.addEntity(e.getX(), e.getY(), 10, 50, Color.GREEN);
System.out.println("clicked");
}
});
p.setBackground(Color.WHITE);
}
public static void main(String args[]) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new Test();
}
});
}
}
class MyPanel extends JPanel {
int width = 300, height = 300;
ArrayList<MyRectangle> entities = new ArrayList<>();
MyBall ball = new MyBall(10, 10, 25, 25, Color.RED, width, height);
void addEntity(int x, int y, int w, int h, Color c) {
entities.add(new MyRectangle(x, y, w, h, c));
}
void moveEntities() {
ball.moveBALL();
}
#Override
protected void paintComponent(Graphics grphcs) {
super.paintComponent(grphcs);
Graphics2D g2d = (Graphics2D) grphcs;
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setColor(ball.getColor());
g2d.fillOval((int) ball.x, (int) ball.y, (int) ball.width, (int) ball.height);
for (MyRectangle entity : entities) {
g2d.setColor(entity.getColor());
g2d.fillRect((int) entity.x, (int) entity.y, (int) entity.width, (int) entity.height);
}
}
#Override
public Dimension getPreferredSize() {
return new Dimension(width, height);
}
}
class MyRectangle extends Rectangle2D.Double {
Color color;
public MyRectangle(double x, double y, double w, double h, Color c) {
super(x, y, w, h);
color = c;
}
public void setColor(Color color) {
this.color = color;
}
public Color getColor() {
return color;
}
}
class MyBall extends Ellipse2D.Double {
int xspeed = 1;
int yspeed = 1;
Color color;
private final int maxWidth;
private final int maxHeight;
public MyBall(double x, double y, double w, double h, Color c, int maxWidth, int maxHeight) {
super(x, y, w, h);
color = c;
this.width = w;//set width and height of Rectangle2D
this.height = h;
//set max width and height ball can move
this.maxWidth = maxWidth;
this.maxHeight = maxHeight;
}
public void setColor(Color color) {
this.color = color;
}
public Color getColor() {
return color;
}
void moveBALL() {
x = x + xspeed;
y = y + yspeed;
if (x < 0) {
x = 0;
xspeed = -xspeed;
} else if (x > maxWidth - ((int) getWidth() / 2)) {// i dont like hard coding values its not good oractice and resuaibilty is diminshed
x = maxWidth - ((int) getWidth() / 2);
xspeed = -xspeed;
}
if (y < 0) {
y = 0;
yspeed = -yspeed;
} else if (y > maxHeight - ((int) getHeight() / 2)) {
y = maxHeight - ((int) getHeight() / 2);
yspeed = -yspeed;
}
}
}
First of all the paint component is called every time swing needs to redraw the component.
And you are adding a new instance of mouse listener to the panel every time the paint is called.
Just move the line
p.addMouseListener(new MouseListener() {...}
out of the paint component, preferably after the initialization of the panel.
default template is
JPanel p = new JPanel(){
#Override
public void paintComponent(Graphics g) {
}
};
p.addMouseListener(new MouseListener() or new MouseAdapter()
//Your overridden methods
});
Hope this helps.
My question has been alluded to in java draw line as the mouse is moved, however, I have not advanced far enough into this book to have covered JPanels, JFrames and Points as stated by the prior programmer who asked this question.
Answering this question definitely would help most beginner programmers better understand the graphics class and drawing, an often intricate process, especially for beginners.
According to the text I am using (as I am learning Java on my own), this was the example of how to draw a line using Java:
/*
* LineTest
* Demonstrates drawing lines
*/
import java.awt.*;
public class LineTest extends Canvas {
public LineTest() {
super();
setSize(300, 200);
setBackground(Color.white);
}
public static void main(String args[]) {
LineTest lt = new LineTest();
GUIFrame frame = new GUIFrame("Line Test");
frame.add(lt);
frame.pack();
frame.setVisible(true);
}
public void paint(Graphics g) {
g.drawLine(10, 10, 50, 100);
g.setColor(Color.blue);
g.drawLine(60, 110, 275, 50);
g.setColor(Color.red);
g.drawLine(50, 50, 300, 200);
}
}
The specification is:
Create an application that allows you to draw lines by clicking the initial
point and dragging the mouse to the second point. The application should
be repainted so that you can see the line changing size and position as you
are dragging the mouse. When the mouse button is released, the line is
drawn.
As you will recognize, running this program does not create any drawing by the user. I believe this error is encountered due to line 21: g.drawLine(x, y, x2, y2); being incorrect since this is the statement defining the drawing of the line.
Any help is greatly appreciated. Thank you in advance for all your time and cooperation regarding this matter.
My code to answer the question is:
import java.awt.*;
import java.awt.event.*;
public class LineDrawer extends Canvas
implements MouseListener, MouseMotionListener {
int x, y, x2, y2;
public LineDrawer() {
super();
setSize(300, 200);
setBackground(Color.white);
}
public void mouseClicked(MouseEvent me) {
int x = me.getX();
int y = me.getY();
int x2 = me.getX();
int y2 = me.getY();
}
public void paint(Graphics g) {
g.drawLine(x, y, x2, y2);
g.setColor(Color.blue);
}
public void mousePressed(MouseEvent me) {
repaint();
}
public void mouseDragged(MouseEvent me) {
}
public void mouseExited(MouseEvent me) {
}
public void mouseEntered(MouseEvent me) {
}
public void mouseReleased(MouseEvent me) {
}
public void mouseMoved(MouseEvent me) {
}
public static void main(String args[]) {
LineDrawer ld = new LineDrawer();
GUIFrame frame = new GUIFrame("Line Drawer");
frame.add(ld);
frame.pack();
frame.setVisible(true);
}
}
P.S.: I have been hesitant to ask for help since I am concerned that other programmers would answer with methods that I have not yet learned.
int x1, y1, x2, y2;
public void mousePressed(MouseEvent e){
x1 = e.getX();
y1 = e.getY();
}
public void mouseDragged(MouseEvent e){
x2 = e.getX();
y2 = e.getY();
// Now Paint the line
repaint();
}
Hope it helps.
Let's start with
public void mouseClicked(MouseEvent me) {
int x = me.getX();
int y = me.getY();
int x2 = me.getX();
int y2 = me.getY();
}
You have previous declared x, y, x2 and y2, but in this method, you have overridden those decelerations with new ones, meaning that the previous declared variables will not be used and the event parameters will be ignored.
mouseClicked is fired AFTER a mousePressed and mouseReleased event, meaning that this is actually when the user has released the mouse button.
Extreme Coder has pointed out that MouseClicked is only fired when the mouse button is pressed and released at the same point, i.e. no dragging is involved - it's still not the right method to use, but the clarification is nice
What you should do is...
On mousePressed store the x, y position of the click and on mouseReleased store the x2, y2 position.
On the mouseDragged event, you should update the x2, y2 values and call repaint
public void mousePressed(MouseEvent me) {
// Mouse is down, but hasn't yet being released...
x = me.getX();
y = me.getY();
// We need to "override" any previous values...
x2 = x;
y2 = y;
repaint();
}
public void mouseDragged(MouseEvent me) {
x2 = me.getX();
y2 = me.getY();
repaint();
}
public void mouseReleased(MouseEvent me) {
// Here I would store the points so I could re-draw each new line...
}
Instead of using x, y, x2 and y2, it might be better to use two arrays, ie
private int[] startPoint;
private int[] endPoint;
Then you could do something like...
public void mousePressed(MouseEvent me) {
// Mouse is down, but hasn't yet being released...
startPoint = new int[2];
startPoint[0] = me.getX();
startPoint[1] = me.getY();
endPoint = startPoint;
repaint();
}
public void mouseDragged(MouseEvent me) {
endPoint = new int[2];
endPoint[0] = me.getX();
endPoint[1] = me.getY();
repaint();
}
Now I prefer paintComponent from JComponent, but I'll stick to you example for now..
public void paint(Graphics g) {
super.paint(g); // This is super important...
if (startPoint != null && endPoint != null && startPoint.length == 2 && endPoint.length == 2) {
g.drawLine(startPoint[0], startPoint[1], endPoint[0], endPoint[1]);
}
}
Additional
This is of some concern...
public void paint(Graphics g) {
g.drawLine(x, y, x2, y2);
g.setColor(Color.blue);
}
The order of operation is VERY important. Setting the color AFTER you've painted the line with have no effect on you paint operations (but may effect paint operations that occur after you).
Also, you MUST call super.paint(g) - this is super important...
Examples
A "basic" example, using int[] arrays for point storage...
public class BasicLineDraw {
public static void main(String[] args) {
new BasicLineDraw();
}
public BasicLineDraw() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception ex) {
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new DrawLinePane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class DrawLinePane extends JPanel {
private int[] startPoint;
private int[] endPoint;
private List<int[][]> lines;
public DrawLinePane() {
lines = new ArrayList<int[][]>(25);
MouseAdapter handler = new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
startPoint = new int[]{e.getX(), e.getY()};
endPoint = startPoint;
repaint();
}
#Override
public void mouseDragged(MouseEvent e) {
endPoint = new int[]{e.getX(), e.getY()};
repaint();
}
#Override
public void mouseReleased(MouseEvent e) {
if (startPoint != null && endPoint != null && startPoint.length == 2 && endPoint.length == 2) {
lines.add(new int[][]{startPoint, endPoint});
}
startPoint = null;
endPoint = null;
repaint();
}
};
addMouseListener(handler);
addMouseMotionListener(handler);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
if (startPoint != null && endPoint != null && startPoint.length == 2 && endPoint.length == 2) {
g2d.setColor(Color.RED);
g2d.drawLine(startPoint[0], startPoint[1], endPoint[0], endPoint[1]);
}
g2d.setColor(Color.BLUE);
for (int[][] line : lines) {
g2d.drawLine(line[0][0], line[0][1], line[1][0], line[1][1]);
}
g2d.dispose();
}
}
}
And a more advanced example, using Point and Java's 2D Graphics API
public class LineDrawer {
public static void main(String[] args) {
new LineDrawer();
}
public LineDrawer() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception ex) {
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new DrawLinePane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class DrawLinePane extends JPanel {
private Point anchor;
private Point lead;
private List<Line2D> lines;
public DrawLinePane() {
lines = new ArrayList<Line2D>(25);
MouseAdapter handler = new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
lead = null;
anchor = e.getPoint();
repaint();
}
#Override
public void mouseDragged(MouseEvent e) {
lead = e.getPoint();
repaint();
}
#Override
public void mouseReleased(MouseEvent e) {
if (lead != null && anchor != null && !anchor.equals(lead)) {
lines.add(new Line2D.Float(anchor, lead));
}
repaint();
}
};
addMouseListener(handler);
addMouseMotionListener(handler);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setColor(Color.RED);
if (lead != null && anchor != null) {
Composite composite = g2d.getComposite();
g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.25f));
g2d.draw(new Line2D.Float(anchor, lead));
g2d.setComposite(composite);
}
for (Line2D line : lines) {
g2d.draw(line);
}
g2d.dispose();
}
}
}
I have created a program which draws a circle when I click on the screen. I have it working so that I can draw as many circles as I want. I can even drag one circle and not the others if I hard code which circle I am dragging. The code is:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.util.List;
import java.util.ArrayList;
public class DrawBall extends JPanel implements MouseListener, MouseMotionListener {
private List<Ball> balls;
private int x, y;
private int numBalls = 0;
boolean drag = false;
public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
private static void createAndShowGUI() {
JFrame frame = new JFrame("Draw Ball");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JComponent newContentPane = new DrawBall();
newContentPane.setOpaque(true);
frame.setContentPane(newContentPane);
frame.setSize(300, 300);
frame.setVisible(true);
}
public DrawBall() {
super(new BorderLayout());
balls = new ArrayList<Ball>(10);
addMouseListener(this);
addMouseMotionListener(this);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setRenderingHint(RederingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
for(Ball ball: balls) {
ball.paint(g2d);
}
g2d.dispose();
}
public void mouseClicked(MouseEvent event) {
x = (int) event.getPoint().getX();
y = (int) event.getPoint().getY();
Ball ball = new Ball(Color.red, numBalls, 30);
ball.setX(x);
ball.setY(y);
balls.add(ball);
numBalls = numBalls + 1;
repaint();
}
public void mousePressed(MouseEvent event) {
drag = true;
}
public void mouseReleased(MouseEvent event) {
drag = false;
}
public void mouseEntered(MouseEvent event) {}
public void mouseExited(MouseEvent event) {}
public void mouseDragged(MouseEvent event) {
if(drag == true) {
x = (int) event.getPoint().getX();
y = (int) event.getPoint().getY();
if(event.getSource() == balls.get(0)) {
Ball ball = balls.get(0);
ball.setX(x);
ball.setY(y);
balls.set(0,ball);
}
repaing();
}
}
public void mouseMoved(MouseEvent event) {}
public class Ball {
private Color color;
private int x, y, diameter, id;
public Ball(Color color, int id, int diameter) {
setColor(color);
setID(id);
setDiameter(diameter);
}
public void setX(int x) {
this.x = x;
}
public void setY(int y) {
this.y = y;
}
public void setID(int id) {
this.id = id;
}
public void setDiameter(int diameter) {
this.diameter = diameter;
}
public void setColor(Color color) {
this.color = color;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public int getID() {
return id;
}
public int getDiameter() {
return diameter;
}
public Color getColor() {
return color;
}
protected void paint(Graphics2D g2d) {
int x = getX();
int y = getY();
g2d.setColor(getColor());
g2d.fillOval(x, y, getDiameter(), getDiameter());
}
}
}
My question is this: In my mouseDragged method, is there an easy way to tell which circle I am hovering over? I have played around with the event.getSource() but it doesn't seem to be working for my circles, at least not in the way I would expect. Thanks for any help.
Modify your Ball class so it creates a circle based on the center point and the radius, rather than the upper left point and the diameter.
Then, you can calculate if you click inside of a circle by applying the distance formula to the point you've clicked and the center point of each of the balls in turn.
The first ball where the distance is less than the radius is the ball you've clicked on. Or, if you want to be more sophisticated, the ball with the smallest distance less than the radius is the ball you've clicked on.
In this case, the event source is the JPanel, not the Ball. Try adding a System.out.println(event.getSource()); in your mouseDragged() method to see for yourself. As others have suggested, just calculate distance from the mouse pressed point to your circles. Basing it on center/radius will make the math easier.
You could modify you Ball class to take advantage of the Shape's API and use an Ellipse2D to maintain the coordinates of the circle and render it.
You could then simply use the Ellipse2D#contains to determine if the shape was clicked or not.
You will have to loop through the collection of balls checking each one until you either reach the end or find the ball you are looking for