I'm writing a program that lets the user draw a hollow rectangle on the screen on top of an image. They can also click on the image and the clicks are joined up to form polygons.
Joining up points works fine, but when the user drags to draw a rectangle, the previously drawn rectangles and polygons disappear. The runnable code is below;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Frame;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class ImageLabeller extends JFrame {
static boolean drawRectangle = false;
/**
* some java stuff to get rid of warnings
*/
private static final long serialVersionUID = 1L;
/**
* main window panel
*/
JPanel appPanel = null;
/**
* toolbox - put all buttons here
*/
JPanel toolboxPanel = null;
/**
* image panel - displays image and editing area
*/
static ImagePanel imagePanel;
/**
* handles New Object button action
*/
public ImageLabeller(String imageFilename) {
try {
this.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent event) {
System.out.println("Bye bye!");
System.exit(0);
}
});
// Create and set up the image panel.
// setup main window panel
setExtendedState(Frame.MAXIMIZED_BOTH);
appPanel = new JPanel();
appPanel.setLayout(new BoxLayout(appPanel, BoxLayout.Y_AXIS));
this.setContentPane(appPanel);
imagePanel = new ImagePanel(imageFilename);
imagePanel.setOpaque(true); // content panes must be opaque
imagePanel.setBorder(BorderFactory.createLineBorder(Color.green));
// create toolbox panel
toolboxPanel = new JPanel();
toolboxPanel.setBorder(BorderFactory.createLineBorder(Color.black));
JButton newPolyButton = new JButton("New object");
newPolyButton.setMnemonic(KeyEvent.VK_N);
// newPolyButton.setSize(50, 20);
newPolyButton.setToolTipText("Click to add new object");
newPolyButton.addActionListener(new DrawListener());
JButton newSquareButton = new JButton("New Square");
newSquareButton.setMnemonic(KeyEvent.VK_S);
// newPolyButton.setSize(50, 20);
newSquareButton.setEnabled(true);
newSquareButton.setToolTipText("Click to add new square");
newSquareButton.addActionListener(new SquareListener());
// add all buttons to toolboxPanel
toolboxPanel.add(newPolyButton);
toolboxPanel.add(newSquareButton);
// add all panels to appPanel
appPanel.add(toolboxPanel);
appPanel.add(imagePanel);
// appPanel.add(Box.createRigidArea(new Dimension(0,10)));
// display all the stuff
this.pack();
this.setVisible(true);
} catch (Exception e) {
System.err.println("Image: ");
e.printStackTrace();
}
}
public static void addNewPolygon() {
imagePanel.addNewPolygon();
}
public static void addNewRectangle() {
//imagePanel.addNewRectangle();
}
static class DrawListener implements ActionListener {
public void actionPerformed(ActionEvent event) {
addNewPolygon();
}
}
static class SquareListener implements ActionListener {
public void actionPerformed(ActionEvent event) {
drawRectangle = true;
imagePanel.drawingRectangle = true;
System.out.println(imagePanel.drawingRectangle);
}
}
public static void main (String args []) {
new ImageLabeller("/change to/a photo/ of your choice.jpg");
}
}
_
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.util.ArrayList;
import javax.imageio.ImageIO;
import javax.swing.JPanel;
public class ImagePanel extends JPanel implements MouseListener,
MouseMotionListener {
Rectangle currentRectangle = null;
boolean drawingRectangle = false;
/**
* some java stuff to get rid of warnings
*/
private static final long serialVersionUID = 1L;
/**
* image to be tagged
*/
BufferedImage image = null;
/**
* list of current polygon's vertices
*/
ArrayList<Point> currentPolygon = null;
/**
* list of polygons
*/
ArrayList<ArrayList<Point>> polygonsList = null;
ArrayList<Rectangle> rectangleList = null;
/**
* extended constructor - loads image to be labelled
*
* #param imageName
* - path to image
* #throws Exception
* if error loading the image
*/
public ImagePanel(String imageName) throws Exception {
currentPolygon = new ArrayList<Point>();
polygonsList = new ArrayList<ArrayList<Point>>();
rectangleList = new ArrayList<Rectangle>();
image = ImageIO.read(new File(imageName));
Dimension panelSize = new Dimension(image.getWidth(), image.getHeight());
this.setSize(panelSize);
this.setMinimumSize(panelSize);
this.setPreferredSize(panelSize);
this.setMaximumSize(panelSize);
setBounds(0, 0, image.getWidth(), image.getHeight());
addMouseListener(this);
addMouseMotionListener(this);
this.setVisible(true);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
System.out.println("Paint Component");
Graphics2D g2d = (Graphics2D) g;
// Paint image on screen
g2d.drawImage(image, 0, 0, image.getWidth(), image.getHeight(), null);
// display all the completed polygons
for (ArrayList<Point> polygon : polygonsList) {
drawPolygon(polygon);
finishPolygon(polygon);
System.out.println("Polly");
}
// Display all completed squares
for (Rectangle r : rectangleList) {
drawRectangle(r);
System.out.println("Square");
}
// display current polygon
if (currentPolygon != null) {
drawPolygon(currentPolygon);
}
// display current square
if (currentRectangle != null) {
drawRectangle(currentRectangle);
}
}
/**
* displays a polygon without last stroke
*
* #param polygon
* to be displayed
*/
public void drawPolygon(ArrayList<Point> polygon) {
Graphics2D g = (Graphics2D) this.getGraphics();
// set to red so I can see when it's being redrawn
g.setColor(Color.RED);
g.setStroke(new BasicStroke(3));
for (int i = 0; i < polygon.size(); i++) {
Point currentVertex = polygon.get(i);
if (i != 0) {
Point prevVertex = polygon.get(i - 1);
g.drawLine(prevVertex.getX(), prevVertex.getY(),
currentVertex.getX(), currentVertex.getY());
}
g.fillOval(currentVertex.getX() - 5, currentVertex.getY() - 5, 10,
10);
}
}
public void drawRectangle(Rectangle r) {
Graphics2D g = (Graphics2D) this.getGraphics();
g.setStroke(new BasicStroke(3));
g.setColor(Color.BLUE);
g.drawLine(r.getX1(), r.getY1(), r.getX2(), r.getY1());
g.drawLine(r.getX1(), r.getY1(), r.getX1(), r.getY2());
g.drawLine(r.getX2(), r.getY2(), r.getX2(), r.getY1());
g.drawLine(r.getX2(), r.getY2(), r.getX1(), r.getY2());
System.out.println(r.getX1() + " " + r.getY1() + " " + r.getX2());
System.out.println("Drawn rectangle");
}
/**
* displays last stroke of the polygon (arch between the last and first
* vertices)
*
* #param polygon
* to be finished
*/
public void finishPolygon(ArrayList<Point> polygon) {
// if there are less than 3 vertices than nothing to be completed
if (polygon.size() >= 3) {
Point firstVertex = polygon.get(0);
Point lastVertex = polygon.get(polygon.size() - 1);
Graphics2D g = (Graphics2D) this.getGraphics();
g.setColor(Color.GREEN);
g.setStroke(new BasicStroke(3));
g.drawLine(firstVertex.getX(), firstVertex.getY(),
lastVertex.getX(), lastVertex.getY());
}
}
/**
* moves current polygon to the list of polygons and makes pace for a new
* one
*/
public void addNewPolygon() {
// finish the current polygon if any
if (currentPolygon != null) {
finishPolygon(currentPolygon);
polygonsList.add(currentPolygon);
}
currentPolygon = new ArrayList<Point>();
}
public void mouseClicked(MouseEvent e) {
if (!drawingRectangle) {
int x = e.getX();
int y = e.getY();
// check if the cursor is within image area
if (x > image.getWidth() || y > image.getHeight()) {
// if not do nothing
return;
}
Graphics2D g = (Graphics2D) this.getGraphics();
// if the left button than we will add a vertex to poly
if (e.getButton() == MouseEvent.BUTTON1) {
g.setColor(Color.GREEN);
if (currentPolygon.size() != 0) {
Point lastVertex = currentPolygon
.get(currentPolygon.size() - 1);
g.setStroke(new BasicStroke(3));
g.drawLine(lastVertex.getX(), lastVertex.getY(), x, y);
}
g.fillOval(x - 5, y - 5, 10, 10);
currentPolygon.add(new Point(x, y));
System.out.println(x + " " + y + " polygon point");
}
}
}
public void mouseEntered(MouseEvent arg0) {
}
public void mouseExited(MouseEvent arg0) {
}
public void mousePressed(MouseEvent arg0) {
if (drawingRectangle) {
currentRectangle = new Rectangle(arg0.getX(), arg0.getY(),
arg0.getX(), arg0.getY(), Color.BLACK);
}
}
public void mouseReleased(MouseEvent arg0) {
if (drawingRectangle) {
rectangleList.add(new Rectangle(currentRectangle.getX1(),
currentRectangle.getY1(), arg0.getX(), arg0.getY(),
currentRectangle.getColor()));
System.out.println(currentRectangle.getX1() + " "
+ currentRectangle.getY1() + " " + arg0.getX() + " "
+ arg0.getY() + " rectangle point");
// unnecessary when mouseDragged calls paintComponent directly?
drawRectangle(new Rectangle(currentRectangle.getX1(),
currentRectangle.getY1(), arg0.getX(), arg0.getY(),
currentRectangle.getColor()));
currentRectangle = null;
drawingRectangle = false;
}
}
public void mouseDragged(MouseEvent arg0) {
if (drawingRectangle) {
currentRectangle = new Rectangle(currentRectangle.getX1(),
currentRectangle.getY1(), arg0.getX(), arg0.getY(),
currentRectangle.getColor());
System.out.println(currentRectangle.getX1() + " "
+ currentRectangle.getY1() + " " + arg0.getX() + " "
+ arg0.getX() + " " + "Dragging");
repaint();
// It works better using this instead on repaint()
// Graphics g = this.getGraphics();
// paintComponent(g);
}
}
public void mouseMoved(MouseEvent arg0) {
// TODO Auto-generated method stub
}
}
-
public class Point {
private int x = 0;
private int y = 0;
public Point() {
}
public Point(int x, int y) {
this.x = x;
this.y = y;
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
}
-
import java.awt.Color;
import java.awt.Graphics2D;
public class Rectangle {
// Initialize variables
private int x1; // x coordinate of first endpoint
private int y1; // y coordinate of first endpoint
private int x2; // x coordinate of second endpoint
private int y2; // y coordinate of second endpoint
private Color colour; // colour of the shape
// A no-parameter constructor that sets all the coordinates of the shape to
// 0 and the
// colour to Color.BLACK
public Rectangle() {
x1 = 0;
y1 = 0;
x2 = 0;
y2 = 0;
colour = Color.BLACK;
}
// A constructor that initializes the coordinates and colour to the values
// of the
// parameters supplied.
public Rectangle(int x1, int y1, int x2, int y2, Color col) {
this.x1 = x1;
this.y1 = y1;
this.x2 = x2;
this.y2 = y2;
this.colour = col;
}
public void setX1(int x1) {
this.x1 = x1;
}
public void setY1(int y1) {
this.y1 = y1;
}
public void setX2(int x2) {
this.x2 = x2;
}
public void setY2(int y2) {
this.y2 = y2;
}
public void setColor(Color colour) {
this.colour = colour;
}
public int getX1() {
return this.x1;
}
public int getY1() {
return this.y1;
}
public int getX2() {
return this.x2;
}
public int getY2() {
return this.y2;
}
public Color getColor() {
return this.colour;
}
public int getWidth() {
return (Math.abs(x2 - x1));
}
public int getHeight() {
return (Math.abs(y2 - y1));
}
public int getUpperLeftX() {
return (Math.min(x1, x2));
}
public int getUpperLeftY() {
return (Math.min(y1, y2));
}
}
Sorry for the mass of code, I tried to trim out as much as I could.
Clicking on the image draws points which are joined up to create lines. When the user clicks the "New Object" button, the first and last points are joined to create a polygon. This all works fine, but if you click "New Square" and drag on the image, all previous shapes flicker as the mouse is moved and disappear when the mouse button is released. If "New Square" is clicked again (a necessity of poor coding on my behalf so far) and another square drawn, the 'disappeared' shapes can be seen flickering again, but then disappear once the mouse is released.
I'm calling repaint() in the mouseDragged(...) event, which I thought was all that was necessary. It actually works (almost) as wanted when I change
repaint();
for
Graphics g = this.getGraphics();
paintComponent(g);
but every book and article I've read said I should never call paintComponent myself, ever. An issue with calling paintComponent however is that the background image tends to flicker alot.
If repaint() calls paintComponent, why do they lead to different results?
I also don't understand why, when using repaint() in the mouseDragged event, I must , I must also call drawRectangle(...) in mouseReleased for the square to be seen at all, but when using paintComponent, I don't?
Any advice or pointers are greatly appreciated, thankyou.
In your drawRectangle and drawPolygon you are re-getting a graphics object, but you are calling them from paintComponent. this is what's causing the odd behaviour, you should pass the graphics from painComponent in to those methods.
I am seeing other odd behaviour though, like the polygons stay green until I've finished painting a square, when they then turn red, but one of the lines stays green and eventually vanishes. I didn't look in to that too much.
Also, polygon drawing and Point and Rectangle are supported in awt, is there any reason you chose to create your own classes for those?
package test;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.util.ArrayList;
import javax.imageio.ImageIO;
import javax.swing.JPanel;
public class ImagePanel extends JPanel implements MouseListener,
MouseMotionListener {
Rectangle currentRectangle = null;
boolean drawingRectangle = false;
/**
* some java stuff to get rid of warnings
*/
private static final long serialVersionUID = 1L;
/**
* image to be tagged
*/
BufferedImage image = null;
/**
* list of current polygon's vertices
*/
ArrayList<Point> currentPolygon = null;
/**
* list of polygons
*/
ArrayList<ArrayList<Point>> polygonsList = null;
ArrayList<Rectangle> rectangleList = null;
/**
* extended constructor - loads image to be labelled
*
* #param imageName
* - path to image
* #throws Exception
* if error loading the image
*/
public ImagePanel(String imageName) throws Exception {
currentPolygon = new ArrayList<Point>();
polygonsList = new ArrayList<ArrayList<Point>>();
rectangleList = new ArrayList<Rectangle>();
image = ImageIO.read(new File(imageName));
Dimension panelSize = new Dimension(image.getWidth(), image.getHeight());
this.setSize(panelSize);
this.setMinimumSize(panelSize);
this.setPreferredSize(panelSize);
this.setMaximumSize(panelSize);
setBounds(0, 0, image.getWidth(), image.getHeight());
addMouseListener(this);
addMouseMotionListener(this);
this.setVisible(true);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
System.out.println("Paint Component");
Graphics2D g2d = (Graphics2D) g;
// Paint image on screen
g2d.drawImage(image, 0, 0, image.getWidth(), image.getHeight(), null);
// display all the completed polygons
for (ArrayList<Point> polygon : polygonsList) {
drawPolygon(polygon,g);
finishPolygon(polygon);
System.out.println("Polly");
}
// Display all completed squares
for (Rectangle r : rectangleList) {
drawRectangle(r,g);
System.out.println("Square");
}
// display current polygon
if (currentPolygon != null) {
drawPolygon(currentPolygon, g);
}
// display current square
if (currentRectangle != null) {
drawRectangle(currentRectangle, g);
}
}
/**
* displays a polygon without last stroke
*
* #param polygon
* to be displayed
*/
public void drawPolygon(ArrayList<Point> polygon, Graphics gr) {
Graphics2D g = null;
if (gr instanceof Graphics2D) {
g = (Graphics2D) gr;
}
else{ return; }
// set to red so I can see when it's being redrawn
g.setColor(Color.RED);
g.setStroke(new BasicStroke(3));
for (int i = 0; i < polygon.size(); i++) {
Point currentVertex = polygon.get(i);
if (i != 0) {
Point prevVertex = polygon.get(i - 1);
g.drawLine(prevVertex.getX(), prevVertex.getY(),
currentVertex.getX(), currentVertex.getY());
}
g.fillOval(currentVertex.getX() - 5, currentVertex.getY() - 5, 10,
10);
}
}
public void drawRectangle(Rectangle r, Graphics gr) {
Graphics2D g = null;
if (gr instanceof Graphics2D) {
g = (Graphics2D) gr;
}
else{ return; }
g.setStroke(new BasicStroke(3));
g.setColor(Color.BLUE);
g.drawLine(r.getX1(), r.getY1(), r.getX2(), r.getY1());
g.drawLine(r.getX1(), r.getY1(), r.getX1(), r.getY2());
g.drawLine(r.getX2(), r.getY2(), r.getX2(), r.getY1());
g.drawLine(r.getX2(), r.getY2(), r.getX1(), r.getY2());
System.out.println(r.getX1() + " " + r.getY1() + " " + r.getX2());
System.out.println("Drawn rectangle");
}
/**
* displays last stroke of the polygon (arch between the last and first
* vertices)
*
* #param polygon
* to be finished
*/
public void finishPolygon(ArrayList<Point> polygon) {
// if there are less than 3 vertices than nothing to be completed
if (polygon.size() >= 3) {
Point firstVertex = polygon.get(0);
Point lastVertex = polygon.get(polygon.size() - 1);
Graphics2D g = (Graphics2D) this.getGraphics();
g.setColor(Color.GREEN);
g.setStroke(new BasicStroke(3));
g.drawLine(firstVertex.getX(), firstVertex.getY(),
lastVertex.getX(), lastVertex.getY());
}
}
/**
* moves current polygon to the list of polygons and makes pace for a new
* one
*/
public void addNewPolygon() {
// finish the current polygon if any
if (currentPolygon != null) {
finishPolygon(currentPolygon);
polygonsList.add(currentPolygon);
}
currentPolygon = new ArrayList<Point>();
}
public void mouseClicked(MouseEvent e) {
if (!drawingRectangle) {
int x = e.getX();
int y = e.getY();
// check if the cursor is within image area
if (x > image.getWidth() || y > image.getHeight()) {
// if not do nothing
return;
}
Graphics2D g = (Graphics2D) this.getGraphics();
// if the left button than we will add a vertex to poly
if (e.getButton() == MouseEvent.BUTTON1) {
g.setColor(Color.GREEN);
if (currentPolygon.size() != 0) {
Point lastVertex = currentPolygon
.get(currentPolygon.size() - 1);
g.setStroke(new BasicStroke(3));
g.drawLine(lastVertex.getX(), lastVertex.getY(), x, y);
}
g.fillOval(x - 5, y - 5, 10, 10);
currentPolygon.add(new Point(x, y));
System.out.println(x + " " + y + " polygon point");
}
}
}
public void mouseEntered(MouseEvent arg0) {
}
public void mouseExited(MouseEvent arg0) {
}
public void mousePressed(MouseEvent arg0) {
if (drawingRectangle) {
currentRectangle = new Rectangle(arg0.getX(), arg0.getY(),
arg0.getX(), arg0.getY(), Color.BLACK);
}
}
public void mouseReleased(MouseEvent arg0) {
if (drawingRectangle) {
rectangleList.add(new Rectangle(currentRectangle.getX1(),
currentRectangle.getY1(), arg0.getX(), arg0.getY(),
currentRectangle.getColor()));
System.out.println(currentRectangle.getX1() + " "
+ currentRectangle.getY1() + " " + arg0.getX() + " "
+ arg0.getY() + " rectangle point");
// unnecessary when mouseDragged calls paintComponent directly?
/*drawRectangle(new Rectangle(currentRectangle.getX1(),
currentRectangle.getY1(), arg0.getX(), arg0.getY(),
currentRectangle.getColor()));*/
currentRectangle = null;
drawingRectangle = false;
}
}
public void mouseDragged(MouseEvent arg0) {
if (drawingRectangle) {
currentRectangle = new Rectangle(currentRectangle.getX1(),
currentRectangle.getY1(), arg0.getX(), arg0.getY(),
currentRectangle.getColor());
System.out.println(currentRectangle.getX1() + " "
+ currentRectangle.getY1() + " " + arg0.getX() + " "
+ arg0.getX() + " " + "Dragging");
repaint();
// It works better using this instead on repaint()
// Graphics g = this.getGraphics();
// paintComponent(g);
}
}
public void mouseMoved(MouseEvent arg0) {
// TODO Auto-generated method stub
}
}
Related
I'm trying to create a graphical component that allows me to draw one rectangle on a selected image (the rectangle must be drawn using drag-and-drop operations): the purpose of this component is to get the coordinates and the size of the rectangle drawn; the second goal is to provide a component which can be easily integrated in a graphical user interface.
The authors of this example created a subclass of JLabel in order to draw the image, then they added a MouseInputAdapter to the instance of this subclass in order to deal with the drawing of the rectangle.
I was inspired by that example, with the difference that I have created a subclass of JPanel class: I named it FigurePanel class. Then I made some changes in order to provide the following features:
if the image is larger than the instance of FigurePanel, then the scrollers must appear;
if the image is smaller than the instance of FigurePanel, then this image must be placed in the center of the panel;
while the user draws the rectangle, it should not extend beyond the limits of the image.
Here is the source code of FigurePanel class.
package imageselectionproject;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Rectangle;
import java.awt.event.MouseEvent;
import javax.swing.JPanel;
import javax.swing.event.MouseInputAdapter;
public class FigurePanel extends JPanel
{
private Image backgroundImage = null;
private Rectangle imageLimits = new Rectangle(0, 0, getWidth(), getHeight());
private Rectangle currentRect = null;
private Rectangle rectToDraw = null;
private final Rectangle previousRectDrawn = new Rectangle();
public FigurePanel()
{
setOpaque(true);
SelectionListener listener = new SelectionListener();
addMouseListener(listener);
addMouseMotionListener(listener);
}
#Override
public Dimension getPreferredSize()
{
return backgroundImage == null ? super.getPreferredSize() : new Dimension(backgroundImage.getWidth(this), backgroundImage.getHeight(this));
}
#Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g); //paints the background and image
if (backgroundImage != null)
{
g.drawImage(backgroundImage, imageLimits.x, imageLimits.y, this);
}
// If currentRect exists, paint a box on top.
if (currentRect != null)
{
// Draw a rectangle on top of the image.
g.setXORMode(Color.white); // Color of line varies
// depending on image colors
g.drawRect(rectToDraw.x, rectToDraw.y,
rectToDraw.width - 1, rectToDraw.height - 1);
System.out.println(rectToDraw);
}
}
public void setImage(Image image)
{
int x = 0;
int y = 0;
if (image != null)
{
backgroundImage = image;
// The following instructions are used to center the image on the panel.
/*x = (getSize().width - image.getWidth(this)) / 2;
y = (getSize().height - image.getHeight(this)) / 2;
if (x < 0) x = 0;
if (y < 0) y = 0;*/
}
else
{
backgroundImage = null;
}
currentRect = null;
setSize(getPreferredSize());
imageLimits.setBounds(x, y, getWidth(), getHeight());
System.out.println("imageLimits = " + imageLimits);
repaint();
}
private void updateDrawableRect()
{
int x = currentRect.x;
int y = currentRect.y;
int width = currentRect.width;
int height = currentRect.height;
// Make the width and height positive, if necessary.
if (width < 0)
{
width = 0 - width;
x = x - width + 1;
if (x < 0)
{
width += x;
x = 0;
}
}
if (height < 0)
{
height = 0 - height;
y = y - height + 1;
if (y < 0)
{
height += y;
y = 0;
}
}
// The rectangle should not extend beyond the boundaries of the image.
if (x < imageLimits.x)
{
width -= (imageLimits.x - x);
x = imageLimits.x;
}
else if ((x + width) > imageLimits.x + imageLimits.width)
{
width = imageLimits.x + imageLimits.width - x;
}
if (y < imageLimits.y)
{
height -= (imageLimits.y - y);
y = imageLimits.y;
}
if ((y + height) > imageLimits.y + imageLimits.height)
{
height = imageLimits.y + imageLimits.height - y;
}
// Update rectToDraw after saving old value.
if (rectToDraw != null)
{
previousRectDrawn.setBounds(rectToDraw.x, rectToDraw.y,
rectToDraw.width, rectToDraw.height);
rectToDraw.setBounds(x, y, width, height);
}
else
{
rectToDraw = new Rectangle(x, y, width, height);
}
}
private class SelectionListener extends MouseInputAdapter
{
#Override
public void mousePressed(MouseEvent e)
{
int x = e.getX();
int y = e.getY();
currentRect = new Rectangle(x, y, 0, 0);
updateDrawableRect();
repaint();
}
#Override
public void mouseDragged(MouseEvent e)
{
updateSize(e.getX(), e.getY());
}
#Override
public void mouseReleased(MouseEvent e)
{
updateSize(e.getX(), e.getY());
}
/*
* Update the size of the current rectangle
* and call repaint. Because currentRect
* always has the same origin, translate it
* if the width or height is negative.
*
* For efficiency (though
* that isn't an issue for this program),
* specify the painting region using arguments
* to the repaint() call.
*
*/
void updateSize(int x, int y)
{
currentRect.setSize(x - currentRect.x, y - currentRect.y);
updateDrawableRect();
Rectangle totalRepaint = rectToDraw.union(previousRectDrawn);
repaint(totalRepaint.x, totalRepaint.y,
totalRepaint.width, totalRepaint.height);
}
}
}
The method setImage is used to set a new image, so it invokes the method repaint to redraw the graphical component. In the code shown above, I disabled (via comments) instructions to center the image: in this way, the component seems to work properly.
Instead, if I enable such instructions, the image is correctly positioned in the center of the panel when it is smaller than the panel itself, however, I encountered the following problem: suppose that it is currently displayed an image larger than the panel, if the new image that I decide to load is smaller than the currently displayed image, then the new image is not displayed; if I try to reload the new image, then it appears.
Why does this problem occur? How to solve it?
I also created the FigurePanelTest class in order to test the FigurePanel class.
package imageselectionproject;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Image;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
public class FigurePanelTest extends JFrame
{
public FigurePanelTest()
{
FigurePanel imagePanel = new FigurePanel();
JScrollPane imageScrollPane = new JScrollPane();
imageScrollPane.setPreferredSize(new Dimension(420, 250));
imageScrollPane.setViewportView(imagePanel);
JButton imageButton = new JButton("Load Image");
imageButton.addActionListener(
new ActionListener()
{
#Override
public void actionPerformed(ActionEvent evt)
{
JFileChooser fc = new JFileChooser();
int returnValue = fc.showOpenDialog(null);
if (returnValue == JFileChooser.APPROVE_OPTION) {
File selectedFile = fc.getSelectedFile();
System.out.println(selectedFile.getName());
try
{
Image image = ImageIO.read(selectedFile.getAbsoluteFile());
imagePanel.setImage(image);
imageScrollPane.getViewport().setViewPosition(new Point(0, 0));
}
catch(IOException e)
{
e.printStackTrace();
}
}
}
}
);
Container container = getContentPane();
container.setLayout(new FlowLayout());
container.add(imageScrollPane);
container.add(imageButton);
setSize(600, 400);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
Here is the main.
public static void main(String args[]) {
/* Create and display the form */
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
new FigurePanelTest().setVisible(true);
}
});
}
For Andrew, a single program:
import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.event.MouseInputAdapter;
public class TestDrawPanel {
public static void main(String args[]) {
/* Create and display the form */
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
new FigurePanelTest().setVisible(true);
}
});
}
}
class FigurePanelTest extends JFrame {
public FigurePanelTest() {
final FigurePanel imagePanel = new FigurePanel();
final JScrollPane imageScrollPane = new JScrollPane();
imageScrollPane.setPreferredSize(new Dimension(420, 250));
imageScrollPane.setViewportView(imagePanel);
JButton imageButton = new JButton("Load Image");
imageButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent evt) {
JFileChooser fc = new JFileChooser();
int returnValue = fc.showOpenDialog(null);
if (returnValue == JFileChooser.APPROVE_OPTION) {
File selectedFile = fc.getSelectedFile();
System.out.println(selectedFile.getName());
try {
Image image = ImageIO.read(selectedFile.getAbsoluteFile());
imagePanel.setImage(image);
imageScrollPane.getViewport()
.setViewPosition(new Point(0, 0));
} catch (IOException e) {
e.printStackTrace();
}
}
}
});
Container container = getContentPane();
container.setLayout(new FlowLayout());
container.add(imageScrollPane);
container.add(imageButton);
setSize(600, 400);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
class FigurePanel extends JPanel {
private Image backgroundImage = null;
private Rectangle imageLimits = new Rectangle(0, 0, getWidth(), getHeight());
private Rectangle currentRect = null;
private Rectangle rectToDraw = null;
private final Rectangle previousRectDrawn = new Rectangle();
public FigurePanel() {
setOpaque(true);
SelectionListener listener = new SelectionListener();
addMouseListener(listener);
addMouseMotionListener(listener);
}
#Override
public Dimension getPreferredSize() {
return backgroundImage == null ? super.getPreferredSize()
: new Dimension(backgroundImage.getWidth(this),
backgroundImage.getHeight(this));
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g); // paints the background and image
if (backgroundImage != null) {
g.drawImage(backgroundImage, imageLimits.x, imageLimits.y, this);
}
// If currentRect exists, paint a box on top.
if (currentRect != null) {
// Draw a rectangle on top of the image.
g.setXORMode(Color.white); // Color of line varies
// depending on image colors
g.drawRect(rectToDraw.x, rectToDraw.y, rectToDraw.width - 1,
rectToDraw.height - 1);
System.out.println(rectToDraw);
}
}
public void setImage(Image image) {
int x = 0;
int y = 0;
if (image != null) {
backgroundImage = image;
// The following instructions are used to center the image on the
// panel.
/*
* x = (getSize().width - image.getWidth(this)) / 2; y =
* (getSize().height - image.getHeight(this)) / 2;
*
* if (x < 0) x = 0; if (y < 0) y = 0;
*/
} else {
backgroundImage = null;
}
currentRect = null;
setSize(getPreferredSize());
imageLimits.setBounds(x, y, getWidth(), getHeight());
System.out.println("imageLimits = " + imageLimits);
repaint();
}
private void updateDrawableRect() {
int x = currentRect.x;
int y = currentRect.y;
int width = currentRect.width;
int height = currentRect.height;
// Make the width and height positive, if necessary.
if (width < 0) {
width = 0 - width;
x = x - width + 1;
if (x < 0) {
width += x;
x = 0;
}
}
if (height < 0) {
height = 0 - height;
y = y - height + 1;
if (y < 0) {
height += y;
y = 0;
}
}
// The rectangle should not extend beyond the boundaries of the image.
if (x < imageLimits.x) {
width -= (imageLimits.x - x);
x = imageLimits.x;
} else if ((x + width) > imageLimits.x + imageLimits.width) {
width = imageLimits.x + imageLimits.width - x;
}
if (y < imageLimits.y) {
height -= (imageLimits.y - y);
y = imageLimits.y;
}
if ((y + height) > imageLimits.y + imageLimits.height) {
height = imageLimits.y + imageLimits.height - y;
}
// Update rectToDraw after saving old value.
if (rectToDraw != null) {
previousRectDrawn.setBounds(rectToDraw.x, rectToDraw.y,
rectToDraw.width, rectToDraw.height);
rectToDraw.setBounds(x, y, width, height);
} else {
rectToDraw = new Rectangle(x, y, width, height);
}
}
private class SelectionListener extends MouseInputAdapter {
#Override
public void mousePressed(MouseEvent e) {
int x = e.getX();
int y = e.getY();
currentRect = new Rectangle(x, y, 0, 0);
updateDrawableRect();
repaint();
}
#Override
public void mouseDragged(MouseEvent e) {
updateSize(e.getX(), e.getY());
}
#Override
public void mouseReleased(MouseEvent e) {
updateSize(e.getX(), e.getY());
}
/*
* Update the size of the current rectangle and call repaint. Because
* currentRect always has the same origin, translate it if the width or
* height is negative.
*
* For efficiency (though that isn't an issue for this program), specify
* the painting region using arguments to the repaint() call.
*/
void updateSize(int x, int y) {
currentRect.setSize(x - currentRect.x, y - currentRect.y);
updateDrawableRect();
Rectangle totalRepaint = rectToDraw.union(previousRectDrawn);
repaint(totalRepaint.x, totalRepaint.y, totalRepaint.width,
totalRepaint.height);
}
}
}
The problem is calculating the x and y in the setImage().It's not calculating the center of panel correctly.I'm saying this by checking the values of x and y after loading small and large images many times
However I tried putting the centering the image inside paintComponent and it worked perfectly
if (backgroundImage != null) {
imageLimits.x = (this.getWidth() - backgroundImage.getWidth(this)) / 2;
imageLimits.y = (this.getHeight() - backgroundImage.getHeight(this)) / 2;
g.drawImage(backgroundImage, imageLimits.x, imageLimits.y, this);
}
I made a mini code that draw oval and link each other , now i try to move the oval(Circle) but I have a problem (in coding)
// Panneau.java
public class Panneau extends JPanel {
private int R = 20;
private boolean isDrag = false;
String text = "stack";
int x = 250, y = 200;
int height = 50, width = 50;
Random Rnd = new Random();
int rand=Rnd.nextInt();
int r=Math.abs(rand%250);
int r2=Math.abs(rand%250);
public Panneau() {
addMouseListener(new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
if ((x<=e.getX() && x+R>=e.getX()) && ( y<=e.getY() && y+R>=e.getY())) {
moveVertex(e.getX(),e.getY());
isDrag = true;
}
}
#Override
public void mouseReleased(MouseEvent e) {
isDrag = false;
}
});
addMouseMotionListener(new MouseAdapter() {
#Override
public void mouseDragged(MouseEvent e) {
if (isDrag) moveVertex(e.getX(),e.getY());
}
});
}
private void moveVertex(int x1, int y1) {
if ((x!=x1) || (y!=y1)) {
x=x1-10;
y=y1-10;
repaint();
}
}
#Override
protected void paintComponent(Graphics g){
// declaration
super.paintComponent(g);
g.setColor(Color.black);
g.drawLine(x,y,x+r,y+r2);
g.setColor(Color.yellow);
g.fillOval(x-height/2, y-width/2,width, height);
g.fillOval((x-height/2)+r, (y-width/2)+r2,width, height);
FontMetrics fm = g.getFontMetrics();
double textWidth = fm.getStringBounds(text, g).getWidth();
g.setColor(Color.blue);
g.drawString(text, (int) (x - textWidth/2),(int) (y + fm.getMaxAscent() / 2));
g.drawString(text, (int) (x - textWidth/2)+r,(int) (y + fm.getMaxAscent() / 2)+r2);
}
}
I must move the two circles and the line must not move(Graph node)
please help me and thanks :)
After the update ( thanks to MadProgrammer) now I can move all the figure ( but if I clicked in the red circle only) , I want to move just circles thanks :)
Basically, because instead of using reapint(int, int) you could use repaint()
private void moveVertex(int x1, int y1) {
int OFFSET = 1;
if ((x != x1) || (y != y1)) {
x = x1 - 10;
y = y1 - 10;
repaint();
}
}
This will ensure that the entire component is repainted.
While I wouldn't discount the use of repaint(int, int), because your painting process is relatively simple, it's not going to provide you with a great deal of benefit at this stage
Updated with additional example
IF I understand, you want to be able to move a single node and have the line remain joined.
While it might be possible to implement within the code you have available, a simpler soltution would be to take advantage of the 2D Graphics Shape API, this provides a number of really useful functions, including determining of points fall within a given shape.
It also means you don't need to keep track of a large number of parameters, but instead, get a self contained object that just knows how it should be painted...
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Line2D;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class TestGraphNode {
public static void main(String[] args) {
new TestGraphNode();
}
public TestGraphNode() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new Panneau());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class Panneau extends JPanel {
private int radius = 50;
private String text = "stack";
private List<Ellipse2D> nodes;
private Ellipse2D dragged;
private Point offset;
public Panneau() {
nodes = new ArrayList<>(25);
nodes.add(new Ellipse2D.Float(50 - (radius / 2), 100 - (radius / 2), radius, radius));
nodes.add(new Ellipse2D.Float(350 - (radius / 2), 100 - (radius / 2), radius, radius));
addMouseListener(new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
for (Ellipse2D node : nodes) {
if (node.contains(e.getPoint())) {
System.out.println("Clicked...");
dragged = node;
// Adjust for the different between the top/left corner of the
// node and the point it was clicked...
offset = new Point(node.getBounds().x - e.getX(), node.getBounds().y - e.getY());
// Highlight the clicked node
repaint();
break;
}
}
}
#Override
public void mouseReleased(MouseEvent e) {
// Erase the "click" highlight
if (dragged != null) {
repaint();
}
dragged = null;
offset = null;
}
});
addMouseMotionListener(new MouseAdapter() {
#Override
public void mouseDragged(MouseEvent e) {
if (dragged != null && offset != null) {
// Adjust the position of the drag point to allow for the
// click point offset
Point to = e.getPoint();
to.x += offset.x;
to.y += offset.y;
// Modify the position of the node...
Rectangle bounds = dragged.getBounds();
bounds.setLocation(to);
dragged.setFrame(bounds);
// repaint...
repaint();
}
}
});
}
#Override
public Dimension getPreferredSize() {
return new Dimension(400, 400);
}
#Override
protected void paintComponent(Graphics g) {
// declaration
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
// Draw the connecting lines first
// This ensures that the lines are under the nodes...
Point p = null;
for (Ellipse2D node : nodes) {
g2d.setColor(Color.BLACK);
Point to = node.getBounds().getLocation();
to.x += radius / 2;
to.y += radius / 2;
if (p != null) {
g2d.draw(new Line2D.Float(p, to));
}
p = to;
}
// Draw the nodes...
for (Ellipse2D node : nodes) {
g2d.setColor(Color.yellow);
g2d.fill(node);
if (node == dragged) {
g2d.setColor(Color.BLUE);
g2d.draw(node);
}
g2d.setColor(Color.BLUE);
FontMetrics fm = g.getFontMetrics();
int textWidth = fm.stringWidth(text);
int x = node.getBounds().x;
int y = node.getBounds().y;
int width = node.getBounds().width;
int height = node.getBounds().height;
g.drawString(text,
x + ((width - textWidth)) / 2,
y + ((height - fm.getHeight()) / 2) + fm.getAscent());
}
g2d.dispose();
}
}
}
I'm working on a game project. The aim is clicking the balls and drop them into the basket which is below in the JPanel. I created some ways to do that but I can't achieve it. In my opinion, when the user click the ball's center with a margin of error (because the program can't catch the real point of balls), the program understands the action and runs the function of this issue. After clicking the ball it should be dropped down straight but the other balls should be continued. I use MouseListener#mousePressed method, but it doesn't work or I'm missing some parts. In addition, I made some changes in my source code, but I want to listen your advices so I am writing this topic.
I wrote a method which finds the mouse and ball coordinates. So when I make some changes to it, I can achieve my project goal. You can see the editing in the picture.
This is my source code ;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.AdjustmentEvent;
import java.awt.event.AdjustmentListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.ArrayList;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollBar;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Game {
public Game() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager
.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException
| IllegalAccessException
| UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("MultipleBallApp");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new BallControl());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class BallControl extends JPanel {
/**
*
*/
private static final long serialVersionUID = 1L;
private BallPanel ballPanel = new BallPanel();
private JButton Suspend = new JButton("Suspend");
private JButton Resume = new JButton("Resume");
private JButton Add = new JButton("+1");
private JButton Subtract = new JButton("-1");
private JScrollBar Delay = new JScrollBar();
public BallControl() {
// Group buttons in a panel
JPanel panel = new JPanel();
panel.add(Suspend);
panel.add(Resume);
panel.add(Add);
panel.add(Subtract);
// Add ball and buttons to the panel
ballPanel.setBorder(new javax.swing.border.LineBorder(Color.red));
Delay.setOrientation(JScrollBar.HORIZONTAL);
ballPanel.setDelay(Delay.getMaximum());
setLayout(new BorderLayout());
add(Delay, BorderLayout.NORTH);
add(ballPanel, BorderLayout.CENTER);
add(panel, BorderLayout.SOUTH);
this.addMouseListener(new MouseListener() {
#Override
public void mouseReleased(MouseEvent arg0) {
// TODO Auto-generated method stub
}
#Override
public void mousePressed(MouseEvent clickEvent) {
// TODO Auto-generated method stub
System.out.println("X coordinate =" + clickEvent.getX());
System.out.println("Y coordinate = " + clickEvent.getY());
double radius1;
int x = 0;
double y = 0;
int radius = 15;
double xM = clickEvent.getX();
double yM = clickEvent.getY();
radius1 = Math.sqrt((xM - x) * (xM - x) + (yM - y)
* (yM - y));
System.out.println("Radius1 =" + radius1);
// ballPanel.list.get(0).setD
}
#Override
public void mouseExited(MouseEvent arg0) {
// TODO Auto-generated method stub
}
#Override
public void mouseEntered(MouseEvent arg0) {
// TODO Auto-generated method stub
}
#Override
public void mouseClicked(MouseEvent arg0) {
// TODO Auto-generated method stub
}
});
// Register listeners
Suspend.addActionListener(new Listener());
Resume.addActionListener(new Listener());
Add.addActionListener(new Listener());
Subtract.addActionListener(new Listener());
Delay.addAdjustmentListener(new AdjustmentListener() {
public void adjustmentValueChanged(AdjustmentEvent e) {
ballPanel.setDelay(Delay.getMaximum() - e.getValue());
}
});
}
class Listener implements ActionListener {
public void actionPerformed(ActionEvent e) {
if (e.getSource() == Suspend) {
ballPanel.suspend();
} else if (e.getSource() == Resume) {
ballPanel.resume();
} else if (e.getSource() == Add) {
ballPanel.add();
} else if (e.getSource() == Subtract) {
ballPanel.subtract();
}
}
}
}
class BallPanel extends JPanel {
/**
*
*/
private static final long serialVersionUID = 1L;
private int delay = 30;
public ArrayList<AnimatedShape> list = new ArrayList<AnimatedShape>();
private AnimatedRectange rectangle;
public BallPanel() {
this.rectangle = new AnimatedRectange(-25, 373, 50, 25, Color.BLACK);
timer.start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(400, 400);
}
// Create a timer with the initial delay
protected Timer timer = new Timer(delay, new ActionListener() {
/**
* Handle the action event
*/
#Override
public void actionPerformed(ActionEvent e) {
for (AnimatedShape ball : list) {
ball.update(getBounds());
}
rectangle.update(getBounds());
repaint();
}
});
public void add() {
int radius = 15;
// Randomised position
int x = (int) (Math.random() * (getWidth() - (radius * 2)))
+ radius;
int y = (int) (Math.random() * (getHeight() - (radius * 2)))
+ radius;
Color color = new Color((int) (Math.random() * 256),
(int) (Math.random() * 256), (int) (Math.random() * 256));
AnimatedBall ball = new AnimatedBall(x, y, radius, color);
list.add(ball);
}
// public void formula(MouseEvent clickEvent) {
// double radius1;
// int x = 0;
// double y = 0;
// int radius = 15;
// double xM = clickEvent.getX();
// double yM = clickEvent.getY();
// radius1 = Math.sqrt((xM - x) * (xM - x) + (yM - y) * (yM - y));
// System.out.println("Radius1 =" + radius1);
// }
public void subtract() {
if (list.size() > 0) {
list.remove(list.size() - 1); // Remove the last ball
}
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
for (AnimatedShape ball : list) {
ball.paint(this, g2d);
}
rectangle.paint(this, g2d);
}
public void suspend() {
timer.stop();
}
public void resume() {
timer.start();
}
public void setDelay(int delay) {
this.delay = delay;
timer.setDelay(delay);
}
}
public interface AnimatedShape {
public void update(Rectangle bounds);
public void paint(JComponent parent, Graphics2D g2d);
}
public abstract class AbstractAnimatedShape implements AnimatedShape {
private Rectangle bounds;
private int dx, dy;
public AbstractAnimatedShape() {
}
public void setBounds(Rectangle bounds) {
this.bounds = bounds;
}
public Rectangle getBounds() {
return bounds;
}
public int getDx() {
return dx;
}
public int getDy() {
return dy;
}
public void setDx(int dx) {
this.dx = dx;
}
public void setDy(int dy) {
this.dy = dy;
}
#Override
public void update(Rectangle parentBounds) {// ball
Rectangle bounds = getBounds();
int dx = getDx();
int dy = getDy();
bounds.x += dx;
bounds.y += dy;
if (bounds.x < parentBounds.x) {
bounds.x = parentBounds.x;
setDx(dx *= -1);
} else if (bounds.x + bounds.width > parentBounds.x
+ parentBounds.width) {
bounds.x = parentBounds.x + (parentBounds.width - bounds.width);
setDx(dx *= -1);
}
if (bounds.y < parentBounds.y) {
bounds.y = parentBounds.y;
setDy(dy *= -1);
} else if (bounds.y + bounds.height > parentBounds.y
+ parentBounds.height) {
bounds.y = parentBounds.y
+ (parentBounds.height - bounds.height);
setDy(dy *= -1);
}
}
}
public class AnimatedBall extends AbstractAnimatedShape {
private Color color;
public AnimatedBall(int x, int y, int radius, Color color) {
setBounds(new Rectangle(x, y / 2, radius * 2, radius * 2));
this.color = color;
setDx(Math.random() > 0.5 ? 2 : -2);
// setDy(Math.random() > 0.5 ? 2 : -2);
}
public Color getColor() {
return color;
}
#Override
public void paint(JComponent parent, Graphics2D g2d) {
Rectangle bounds = getBounds();
g2d.setColor(getColor());
g2d.fillOval(bounds.x, bounds.y, bounds.width, bounds.height);
}
}
public class AnimatedRectange extends AbstractAnimatedShape {
private Color color;
public AnimatedRectange(int x, int y, int width, int height, Color color) {
setBounds(new Rectangle(x, y, width, height));
this.color = color;
setDx(2);
}
// Don't want to adjust the vertical speed
#Override
public void setDy(int dy) {
}
#Override
public void paint(JComponent parent, Graphics2D g2d) {
Rectangle bounds = getBounds();
g2d.setColor(color);
g2d.fill(bounds);
}
}
/**
* Main method
*/
public static void main(String[] args) {
new Game();
}
}
At the end of your mousePressed method, you can go though all AnimatedShape objects, check whether they are an AnimatedBall. When the object is a ball, you can test whether the mouse click hits the ball. The mouse click hits the ball when the distance between the center of the ball and the mouse position is smaller than the ball radius. When the ball is hit, you can set its horizontal speed to 0, and the vertical speed to 5 or so.
for (AnimatedShape as : ballPanel.list)
{
if (as instanceof AnimatedBall)
{
AnimatedBall ball = (AnimatedBall)as;
Rectangle b = ball.getBounds();
int ballCenterX = b.x + b.width / 2;
int ballCenterY = b.y + b.height / 2;
Point p = new Point(ballCenterX, ballCenterY);
double d = p.distance(clickEvent.getPoint());
if (d < radius)
{
ball.setDx(0);
ball.setDy(5);
}
}
}
Note
In order to make this work properly, you have to attach this listener to the ballPanel. Originally, you had the line
this.addMouseListener(new MouseListener() {
in your code. You have to change this to
ballPanel.addMouseListener(new MouseListener() {
Otherwise, the mouse coordinates will refer to the wrong component!
Concerning the question from the comment
how can I stop and disapper clickedball's when they crash the bound
You may probably insert method to check this, and call this method in the actionPerformed method of your timer. Further explainations are probably beyond the scope of an anser on a Q&A site. Stackoverflow is not a homework-solution-generator.
Given the following code :
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.util.ArrayList;
import java.util.Iterator;
import javax.swing.JFrame;
import javax.swing.JPanel;
/**
*
* #author X2
*
*/
public class PolygonnerJframe
{
public static void main (String[] args)
{
JFrame frame = new JFrame("Draw polygons");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setContentPane(new DrawingPanel());
frame.pack();
frame.setVisible(true);
}
}
/**
* Main class
* #author X2
*
*/
class DrawingPanel extends JPanel implements MouseListener, MouseMotionListener
{
/**
*
*/
private static final long serialVersionUID = 1L;
private static final Dimension MIN_DIM = new Dimension(300, 300);
private static final Dimension PREF_DIM = new Dimension(500, 500);
private boolean polygonDone = false;
private final Point trackPoint = new Point(); // The 'dummy' point tracking the mouse
private ArrayList<Point> points = new ArrayList<Point>(); // The list of points making up a polygon
private ArrayList<Point> helper = new ArrayList<Point>(); // The list of points making up a polygon
public ArrayList<Point> copyCreate(ArrayList<Point> input , ArrayList<Point> output)
{
int i = 0;
if (output == null)
output = new ArrayList<Point>();
while (i < input.size())
{
output.add(input.get(i));
i++;
}
return output;
}
/**
* Setting the dimensions of the windows
*/
public Dimension getMinimumSize() { return MIN_DIM; }
public Dimension getPreferredSize() { return PREF_DIM; }
/**
* The only constructor needed for this class
*/
DrawingPanel()
{
super();
addMouseListener(this);
addMouseMotionListener(this);
}
/**
* The drawing itself
*/
public void paintComponent(Graphics g)
{
super.paintComponent(g);
int numPoints = points.size();
if (numPoints == 0)
return; // nothing to draw
Point prevPoint = (Point) points.get(0);
// draw polygon
Iterator<Point> it = points.iterator();
while (it.hasNext())
{
Point curPoint = (Point) it.next();
draw(g, prevPoint, curPoint);
prevPoint = curPoint;
}
// now draw tracking line or complete the polygon
if (polygonDone == true)
{
draw(g, prevPoint, (Point) points.get(0));
}
else // polygonDone == false
draw(g, prevPoint, trackPoint);
}
/**
* MouseListener interface
*/
public void mouseClicked(MouseEvent evt)
{
int x = evt.getX();
int y = evt.getY();
switch (evt.getClickCount())
{
case 1: // single-click
if (polygonDone == true)
{
this.helper = this.copyCreate(this.points, this.helper); // copy the new coordinates into the helper
points.clear();
polygonDone = false;
}
points.add(new Point(x, y));
repaint();
break;
case 2: // double-click
polygonDone = true;
points.add(new Point(x, y));
// repaint();
break;
default: // ignore anything else
break;
}
}
/**
* MouseMotionListener interface
*/
public void mouseMoved(MouseEvent evt)
{
trackPoint.x = evt.getX();
trackPoint.y = evt.getY();
repaint();
}
/**
* draw points and lines
* #param g
* #param p1
* #param p2
*/
private void draw(Graphics g, Point p1, Point p2)
{
int x1 = p1.x;
int y1 = p1.y;
int x2 = p2.x;
int y2 = p2.y;
// draw the line first so that the points
// appear on top of the line ends, not below
g.setColor(Color.green.darker());
g.drawLine(x1 + 3, y1 + 3, x2 + 3, y2 + 3);
g.drawLine(x1 + 4, y1 + 4, x2 + 4, y2 + 4);
g.drawLine(x1 + 5, y1 + 5, x2 + 5, y2 + 5);
g.setColor(Color.green);
g.fillOval(x1, y1, 8, 8);
g.setColor(Color.black);
g.fillOval(x2, y2, 8, 8);
}
public void mouseDragged(MouseEvent evt) { /* EMPTY */ }
public void mousePressed(MouseEvent evt) { /* EMPTY */ }
public void mouseReleased(MouseEvent evt) { /* EMPTY */ }
public void mouseEntered(MouseEvent evt) { /* EMPTY */ }
public void mouseExited(MouseEvent evt) { /* EMPTY */ }
}
I can only draw one polygon each time , meaning - when I try to start a new polygon
the "old" polygon vanishes , but I don't understand why .
So how can I draw multiple polygons ?
What causes the old polygon to vanish ? I thought maybe due to repaint() , but I tried without it but it didn't help .
I'd appreciate your help
The polygons are indeed being erased by calling points.clear(). To counter this, you could to maintain co-ordinate information using the Polygon class about previous polygons in a separate List which could be painted along with the "in-progress" polygon. This is outlined in Custom Painting Approaches.
I am trying to add a MouseListener to my custom JComponent. I just want the MouseListener to be triggered when pressing withing the bounds of the circle (the JComponent's painting method draws a circle). I have tried with the below code but I just cannot get it to work (loook especially in the mousePressed method). How can I tackle this problem?
The SSCCE:
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.AffineTransform;
import java.awt.geom.Rectangle2D;
import javax.swing.JComponent;
import javax.swing.JFrame;
public class AffineTransformTest {
private static TransformingCanvas canvas;
public static void main(String[] args) {
JFrame frame = new JFrame();
canvas = new AffineTransformTest.TransformingCanvas();
TranslateHandler translater = new TranslateHandler();
canvas.addMouseListener(translater);
canvas.addMouseMotionListener(translater);
canvas.addMouseWheelListener(new ScaleHandler());
frame.setLayout(new BorderLayout());
frame.getContentPane().add(canvas, BorderLayout.CENTER);
frame.setSize(500, 500);
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.setVisible(true);
}
private static class TransformingCanvas extends JComponent {
private double translateX;
private double translateY;
private double scale;
TransformingCanvas() {
translateX = 0;
translateY = 0;
scale = 1;
setOpaque(true);
setDoubleBuffered(true);
}
#Override
public void paint(Graphics g) {
AffineTransform tx = new AffineTransform();
tx.translate(translateX, translateY);
tx.scale(scale, scale);
Graphics2D ourGraphics = (Graphics2D) g;
ourGraphics.setColor(Color.WHITE);
ourGraphics.fillRect(0, 0, getWidth(), getHeight());
ourGraphics.setTransform(tx);
ourGraphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
ourGraphics.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
ourGraphics.setColor(Color.BLACK);
ourGraphics.fillOval(50, 50, 50, 50);
}
}
private static class TranslateHandler implements MouseListener,
MouseMotionListener {
private int lastOffsetX;
private int lastOffsetY;
public void mousePressed(MouseEvent e) {
lastOffsetX = e.getX();
lastOffsetY = e.getY();
double width = canvas.scale * 50;
double height = canvas.scale * 50;
double x = (AffineTransformTest.canvas.getWidth() - width) / 2;
double y = (AffineTransformTest.canvas.getHeight() - height) / 2;
Rectangle2D.Double bounds = new Rectangle2D.Double(x, y, width, height);
System.out.println(bounds + " " + e.getPoint());
if (bounds.contains(e.getPoint())) {
System.out.println("Click!");
}
}
public void mouseDragged(MouseEvent e) {
int newX = e.getX() - lastOffsetX;
int newY = e.getY() - lastOffsetY;
lastOffsetX += newX;
lastOffsetY += newY;
canvas.translateX += newX;
canvas.translateY += newY;
canvas.repaint();
}
public void mouseClicked(MouseEvent e) {
}
public void mouseEntered(MouseEvent e) {
}
public void mouseExited(MouseEvent e) {
}
public void mouseMoved(MouseEvent e) {
}
public void mouseReleased(MouseEvent e) {
}
}
private static class ScaleHandler implements MouseWheelListener {
public void mouseWheelMoved(MouseWheelEvent e) {
if (e.getScrollType() == MouseWheelEvent.WHEEL_UNIT_SCROLL) {
canvas.scale += (.1 * e.getWheelRotation());
canvas.scale = Math.max(0.00001, canvas.scale);
canvas.repaint();
}
}
}
}
Here is the code. Some quick notes, there is still some debug in the code, and replace calls to LOGGER object with System.out.println().
import java.awt.Color;
import java.awt.Container;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.geom.AffineTransform;
import java.awt.geom.Area;
import java.awt.geom.Point2D;
import java.util.logging.Logger;
import javax.swing.JComponent;
import javax.swing.event.MouseInputListener;
public abstract class LCARSComponent extends JComponent implements MouseInputListener {
/**
* A reference to the global Logger object.
*/
protected final static Logger LOGGER = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME);
protected int x;
protected int y;
protected int w;
protected int h;
protected double scaleFactor = 1.0;
protected Area area;
protected Area scaledArea;
protected int style;
protected Color color;
protected AffineTransform renderingTransform;
protected ActionListener actionListener;
public LCARSComponent(Container parent, int x, int y, int w, int h, int style) {
this.x = x;
this.y = y;
this.w = w;
this.h = h;
this.style = style;
setBounds(x,y,w,h);
addMouseListener(this);
}
#Override
protected void paintComponent(Graphics g) {
/**
* First, paint the background if the component is opaque. Required when
* JComponent is extended, and the paintCompnent() method is overridden.
*/
if(isOpaque()) {
g.setColor(getBackground());
g.fillRect(0, 0, getWidth(), getHeight());
}
/**
* Create a Graphics2D object so we can use Java 2D features.
*/
Graphics2D g2d = (Graphics2D) g.create();
/**
* Set the anti aliasing rendering hint and the color to draw with.
*/
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setColor(color);
scaleComponent();
/**
* Draw the Area object of the LCARS component, and fill it.
*/
g2d.draw(scaledArea);
g2d.fill(scaledArea);
g2d.drawRect(scaledArea.getBounds().x, scaledArea.getBounds().y, scaledArea.getBounds().width, scaledArea.getBounds().height);
/**
* Clean up when the method has completed by disposing the Graphics2D object that was created.
*/
g2d.dispose();
}
protected void scaleComponent() {
double sw = (double)getParent().getWidth() / (double)getParent().getPreferredSize().width;
double sh = (double)getParent().getHeight() / (double)getParent().getPreferredSize().height;
LOGGER.info("scaledWidth = " + sw);
LOGGER.info("scaledHeight = " + sh);
double scaleFactor;
if(sh < sw) {
scaleFactor = sh;
}
else {
scaleFactor = sw;
}
LOGGER.info("scaleFactor = " + scaleFactor);
if(scaleFactor != this.scaleFactor) {
this.scaleFactor = scaleFactor;
scaledArea = new Area(area);
renderingTransform = new AffineTransform();
renderingTransform.scale(scaleFactor, scaleFactor);
scaledArea.transform(renderingTransform);
}
setBounds((int)(this.x*scaleFactor), (int)(this.y*scaleFactor), this.getWidth(), this.getHeight());
}
public Point screenToComponent(Point pt) {
if(renderingTransform == null) {
Graphics2D g2d = (Graphics2D)(this.getParent().getGraphics());
renderingTransform = g2d.getTransform();
}
Point2D pt2d = renderingTransform.transform(pt,null);
LOGGER.info("mouse click: " + pt.getX() + ", " + pt.getY() + " -- " + pt2d.getX() + ", " + pt2d.getY());
return new Point((int)Math.round(pt2d.getX()), (int)Math.round(pt2d.getY()));
}
public void setActionListener(ActionListener actionListener) {
this.actionListener = actionListener;
}
#Override
public void mouseClicked(MouseEvent e) {
Point pt = e.getPoint();
LOGGER.info("mouseClicked: " + this.getName() + " - " + pt.getX() + ", " + pt.getY());
if(area.contains(e.getPoint())) {
if(actionListener != null) {
actionListener.actionPerformed(new ActionEvent(e.getSource(), e.getID(), e.paramString()));
}
if(color.equals(Color.black))
color = Color.blue;
else
color = Color.black;
this.repaint();
}
}
#Override
public void mousePressed(MouseEvent e) {
// TODO Auto-generated method stub
if(area.contains(e.getLocationOnScreen()))
System.out.println("mousePressed()...");
}
#Override
public void mouseReleased(MouseEvent e) {
// TODO Auto-generated method stub
if(area.contains(e.getLocationOnScreen()))
System.out.println("mouseReleased()...");
}
#Override
public void mouseEntered(MouseEvent e) {
Point pt = e.getPoint();
// TODO Auto-generated method stub
System.out.println("mouseEntered()...");
LOGGER.info("mouseEntered: " + this.getName() + " - " + pt.getX() + ", " + pt.getY());
}
#Override
public void mouseExited(MouseEvent e) {
// TODO Auto-generated method stub
Point pt = e.getPoint();
System.out.println("mouseExited()...");
LOGGER.info("mouseExited: " + pt.getX() + ", " + pt.getY());
}
#Override
public void mouseDragged(MouseEvent e) {
// TODO Auto-generated method stub
System.out.println("mouseDragged()...");
}
#Override
public void mouseMoved(MouseEvent e) {
// TODO Auto-generated method stub
System.out.println("mouseMoved()...");
}
}
Two points:
First, I would use a Shape or Area object and use its .contains() method. Bounds is always a Rectangle that surrounds your entire shape. Also, not sure why you are creating a bounds rectangle when you can simply use the setBounds() method of your JComponent.
Second, the bounds of a JComponent can be translated, but I haven't found anything built-in to scale them. Though, you can change the size dynamically. I am working on an identical problem. Right now I am considering dynamically changing the bounds when my JComponent scales. I am trying to understand correlation between screen coordinates and JComponent coordinates. The mouse coordinates always seem to be in unscaled frame/screen coordinates (might be an artifact of the full screen mode I am using. The solutions I have seen paint everything in a JPanel and do not use discrete JComponents to solve the scaling problem. The code is a little hard to follow, and not particularly modular, but it does work.
In any case, I am sure it can ultimately be done. It is simply a matter of getting the math right. I will post the code for the scalable JComponent extension when I have finished it and it works reliably. Hope this helps a little.