Ambiquity while drawing multiple shapes on Jpanel - java

Th code below has few problems :
1) The polygon joins last and first point itself, should not do itself but user should draw it.
2) The polygons lines disappeared after clicking on other shapes.
package Circles;
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Line2D;
import java.awt.geom.Path2D;
import javax.swing.*;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.Iterator;
//////////////////////////////////////////////////////////////PaintDemo
class PaintDemo2 {
//============================================================= main
public static void main(String[] args) {
PaintWindow2 window = new PaintWindow2();
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setVisible(true);
}//end main
}//endclass PaintDemo
#SuppressWarnings("serial")
////////////////////////////////////////////////////////////PaintWindow
class PaintWindow2 extends JFrame {
PaintPanel2 canvas = new PaintPanel2();
//====================================================== constructor
public PaintWindow2() {
//--- create the buttons
JButton circleButton = new JButton("Circle");
circleButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
canvas.setShape(PaintPanel2.CIRCLE);
}});
JButton rectangleButton = new JButton("Rectangle");
rectangleButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
canvas.setShape(PaintPanel2.Ellipse);
}});
JButton polyButton = new JButton("Polygon");
polyButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
canvas.setShape(PaintPanel2.POLY);
}});
//--- layout the buttons
JPanel buttonPanel = new JPanel();
buttonPanel.setLayout(new GridLayout(2, 1));
buttonPanel.add(circleButton);
buttonPanel.add(rectangleButton);
buttonPanel.add(polyButton);
//--- layout the window
Container content = this.getContentPane();
content.setLayout(new BorderLayout());
content.add(buttonPanel, BorderLayout.WEST);
content.add(canvas , BorderLayout.CENTER);
this.setTitle("Paint Demo");
this.pack();
}//end constructor
}//endclass PaintWindow
///////////////////////////////////////////////////////////// PaintPanel2
#SuppressWarnings("serial")
class PaintPanel2 extends JPanel implements MouseListener,
MouseMotionListener {
//--- Public constants used to specify shape being drawn.
public static final int NONE = 0;
public static final int LINE = 1;
public static final int Ellipse = 2;
public static final int CIRCLE = 3;
public static final int POLY = 4;
//--- Variables to store the current figure info
private int _shape = NONE;
public int getShape() {
return _shape;
}
private int _currentStartX = 0; // where mouse first pressed
private int _currentStartY = 0;
private int _currentEndX = 0; // where dragged to or released
private int _currentEndY = 0;
//--- BufferedImage to store the underlying saved painting.
// Will be initialized first time paintComponent is called.
private BufferedImage _bufImage = null;
private boolean polygonIsNowComplete = false;
//--- Private constant for size of paint area.
private static final int SIZE = 600; // size of paint area
private final Point trackPoint = new Point();
private Path2D currentShape;
private ArrayList<Path2D> lstPloys = new ArrayList<Path2D>();;
private Point lastPoint;
private Point currentPoint;
#SuppressWarnings("rawtypes")
private ArrayList points = new ArrayList();
//====================================================== constructor
public PaintPanel2() {
setPreferredSize(new Dimension(SIZE, SIZE));
setBackground(Color.white);
//--- Add the mouse listeners.
this.addMouseListener(this);
this.addMouseMotionListener(this);
}//endconstructor
//========================================================= setShape
public void setShape(int shape) {
//--- Provided so users can set the shape.
_shape = shape;
}//end setShape
//=================================================== paintComponent
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g; // downcast to Graphics2D
if (_bufImage == null) {
//--- This is the first time, initialize _bufImage
int w = this.getWidth();
int h = this.getHeight();
_bufImage = (BufferedImage)this.createImage(w, h);
Graphics2D gc = _bufImage.createGraphics();
gc.setColor(Color.white);
gc.fillRect(0, 0, w, h); // fill in background
}
g2.drawImage(_bufImage, null, 0, 0); // draw previous shapes
drawCurrentShape(g2);
}//end paintComponent
//================================================= drawCurrentShape
private void drawCurrentShape(Graphics2D g2) {
//--- Draws current shape on a graphics context, either
// on the context passed to paintComponent, or the
// context for the BufferedImage.
switch (_shape) {
case NONE :
break;
case CIRCLE:
g2.drawOval(_currentStartX, _currentStartY,
_currentEndX - _currentStartX,
_currentEndY - _currentStartY);
break;
case Ellipse:
g2.draw(new Ellipse2D.Double(_currentStartX, _currentStartY,
_currentEndX - _currentStartX,
_currentEndY - _currentStartY));
break;
case POLY:
drawPolyGon(g2);
break;
default: // should never happen
g2.drawString("Huh?", 10, 20);
break;
}
}//end paintComponent
private void drawPolyGon(Graphics2D g2) {
g2.create();
if (lastPoint != null) {
g2.setColor(Color.RED);
g2.fillOval(lastPoint.x - 2, lastPoint.y - 2, 4, 4);
}
if (currentShape != null) {
g2.setColor(Color.RED);
g2.draw(currentShape);
if (lastPoint != null && currentPoint != null) {
g2.setColor(new Color(255, 0, 0, 64));
g2.draw(new Line2D.Float(lastPoint, currentPoint));
}
}
g2.setColor(Color.BLACK);
for (Path2D shape : lstPloys) {
g2.draw(shape);
}
g2.dispose();
// TODO Auto-generated method stub
}
//===================================================== mousePressed
public void mousePressed(MouseEvent e) {
_currentStartX = e.getX(); // save x coordinate of the click
_currentStartY = e.getY(); // save y
_currentEndX = _currentStartX; // set end to same pixel
_currentEndY = _currentStartY;
}//end mousePressed
//===================================================== mouseDragged
public void mouseDragged(MouseEvent e) {
_currentEndX = e.getX(); // save new x and y coordinates
_currentEndY = e.getY();
this.repaint();
// show new shape
}//end mouseDragged
//==================================================== mouseReleased
public void mouseReleased(MouseEvent e) {
// This will save the shape that has been dragged by
// drawing it onto the bufferedImage where all shapes
// are written.
_currentEndX = e.getX(); // save ending coordinates
_currentEndY = e.getY();
//--- Draw the current shape onto the buffered image.
Graphics2D grafarea = _bufImage.createGraphics();
drawCurrentShape(grafarea);
this.repaint();
}//end mouseReleased
public void mouseMoved (MouseEvent e) {
if (currentShape != null) {
currentPoint = e.getPoint();
repaint();
} else {
currentPoint = null;
}
}
public void mouseEntered (MouseEvent e) {}
public void mouseExited (MouseEvent e) {}
public void mouseClicked (MouseEvent e) {
if (e.getButton() == MouseEvent.BUTTON1) {
if (e.getClickCount() == 1) {
Point p = e.getPoint();
lastPoint = p;
if (currentShape == null) {
currentShape = new Path2D.Float();
currentShape.moveTo(p.x, p.y);
} else {
currentShape.lineTo(p.x, p.y);
}
repaint();
} else if (e.getClickCount() == 2) {
currentShape.closePath();
lstPloys.add(currentShape);
currentShape = null;
lastPoint = null;
repaint();
}
}
}
}

Paint cycles are stateless, that is, the contents of the graphics are not passed from one cycle to another.
You are required to re-paint the entire contents of component on each paint cycles.
You've attempt to implement a double buffer solution, but instead of passing the graphs context of the buffer, you've passed the graphics contents supplied to you by the paint system. If you passed the graphs context of the buffer to the drawCurrentShape method, it might solve your problem (and eliminate the need to cache all the shapes)
UPDATED
So, in your paintComponent method of your PaintPanel2 component, you are creating a BufferedImage, but you are not painting the components to it...
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g; // downcast to Graphics2D
if (_bufImage == null) {
//--- This is the first time, initialize _bufImage
int w = this.getWidth();
int h = this.getHeight();
_bufImage = (BufferedImage)this.createImage(w, h);
Graphics2D gc = _bufImage.createGraphics();
gc.setColor(Color.white);
gc.fillRect(0, 0, w, h); // fill in background
}
g2.drawImage(_bufImage, null, 0, 0); // draw previous shapes
drawCurrentShape(g2);
}//end paintComponent
Instead, you should use the graphics context from the buffered image to drawCurrentShape
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g; // downcast to Graphics2D
Graphics2D gc = null;
if (_bufImage == null) {
//--- This is the first time, initialize _bufImage
int w = this.getWidth();
int h = this.getHeight();
_bufImage = (BufferedImage)this.createImage(w, h);
gc = _bufImage.createGraphics();
gc.setColor(Color.white);
gc.fillRect(0, 0, w, h); // fill in background
} else {
gc = _bufImage.createGraphics();
}
drawCurrentShape(g2);
gc.dispose();
g2.drawImage(_bufImage, null, 0, 0); // draw previous shapes
}//end paintComponent
It should be noted that this might create some other issues, but the concept is sound.
Personally, I prefer to keep a List of all the shapes and repaint them. This gives you the ability to select, move, delete, re-order and change all the shapes within the program, as well as provide a type of history ;)

Related

Get mouse coordinates for moving a JLabel

I am working on a project, develop a game called
Don't get mad bro.
I have a JPanel with shapes (circles) draw on it and JLabel components that contains images.I need that whenever I click "Throw dice" (which in background return a number between 1 and 6) I should wait for current player to click on one of his pawns, and that pawn should move after n positions, where n is equal with number that dice returned.
My question is, should I create a new thread in which I wait for mouseClick event? And how to get coordinates of mouseClick?
Here is my class that inherits panel and draw circles and add labels.
public class ImagePanel extends JPanel{
private static final long serialVersionUID = 1L;
ImageMatrix imageMatrix;
BufferedImage[] images;
public static JLabel[][] labels;
DrawGameBoard board = new DrawGameBoard();
List<GameFigure> gameCircles;
List<FinishFigure> finishCircles;
int initialHeight = 528;
int initialWidth = 596;
ThreadForPawnsClick labelsClick;
public ImagePanel(){
labels = new JLabel[4][4];
images = new BufferedImage[4];
setBackground(new Color(255,255,153));
gameCircles = new ArrayList<GameFigure>();
finishCircles = new ArrayList<FinishFigure>();
imageMatrix = new ImageMatrix(initialWidth,initialHeight);
try {
images[0] = ImageIO.read(new File("C:\\Users\\babii\\.eclipse\\DontGetMad\\resource\\red.png"));
images[1] = ImageIO.read(new File("C:\\Users\\babii\\.eclipse\\DontGetMad\\resource\\green.png"));
images[2] = ImageIO.read(new File("C:\\Users\\babii\\.eclipse\\DontGetMad\\resource\\blue.png"));
images[3] = ImageIO.read(new File("C:\\Users\\babii\\.eclipse\\DontGetMad\\resource\\yellow.png"));
} catch (IOException e) {
e.printStackTrace();
}
for(int i=0;i<4;i++)
{
for(int j=0;j<4;j++)
labels[i][j] = new JLabel(new ImageIcon(images[i]));
}
setLayout(null);
board.DrawHomeBoard(imageMatrix, labels);
for(int i=0;i<4;i++)
for(int j=0;j<4;j++)
add(labels[i][j]);
}
#Override
public void paintComponent(Graphics g){
super.paintComponent(g);
int width = this.getWidth();
int height = this.getHeight();
imageMatrix.update(width, height);
setLayout(null);
gameCircles = board.DrawMainBoard(g, imageMatrix);
//labels = board.DrawHomeBoard(g, imageMatrix, labels);
//board.DrawHomeBoard(imageMatrix, labels);
finishCircles = board.DrawFinishBoard(g, imageMatrix);
/*for(int i=0;i<4;i++)
for(int j=0;j<4;j++)
add(labels[i][j]);
*/
}
}
Also, why my imageMatrix doesn't extend on whole screen, even if I call update matrix in paintComponent()?
My question is, should I create a new thread in which I wait for mouseClick event?
No, absolutely not. You need instead to change the state of the GUI somehow to wait-for-mouse-click mode, and then alter the behavior of the GUI's response to mouse clicks depending on its state. Usually state is represented by instance fields of the class. So when you need to wait, you change one of these state fields, and on mouse click, you check the state of the field and vary what happens depending on that. For instance in a turn based game of chess, one state field could be private boolean blackTurn, and then base what mouse does based on its state.
And how to get coordinates of mouseClick?
In the MouseListener the MouseEvent parameter gives you the x and y position of the mouse relative to the listened to component and to the screen. If your MouseListener is attached to the JLabels, then you can get a reference to the clicked JLabel via the MouseEvent's getSource() method, and then can get the location of the JLabel relative to its container JPanel (if needed) by calling getLocation() on it.
Side note: in a Swing GUI where you're moving around sprites, it's usually better not to put the sprites into JLabels but rather to simply draw them directly in the paintComponent method of a drawing JPanel.
As an example of what I mean, here is a program that draws 4 colored circles, circles that are draggable, but only draggable when the corresponding JRadioButton has been selected with the JRadioButton setting the "state" of the GUI. Here the state is represented by an enum called ColorState that holds 4 colors and corresponding text. Here is this enum:
import java.awt.Color;
public enum ColorState {
RED("Red", Color.RED),
GREEN("Green", Color.GREEN),
BLUE("Blue", Color.BLUE),
ORANGE("Orange", Color.ORANGE);
private String text;
private Color color;
private ColorState(String text, Color color) {
this.text = text;
this.color = color;
}
public String getText() {
return text;
}
public Color getColor() {
return color;
}
}
Then we create a drawing JPanel, one that holds four Ellipse2D Shape objects in a Map,
private Map<ColorState, Shape> colorStateMap = new EnumMap<>(ColorState.class);
Within a for loop, we create the JRadioButtons, give them ActionListeners that set the object's state, and populate the Map with Ellipse2D Shape objects
for (final ColorState state : ColorState.values()) {
// create the JRadioButton
JRadioButton radioButton = new JRadioButton(state.getText());
add(radioButton); // add to GUI
buttonGroup.add(radioButton); // add to ButtonGroup
// give it an ActionListener that changes the object's state
radioButton.addActionListener(e -> {
colorState = state;
});
// create a randomly placed Ellipse2D and place into Map:
double x = Math.random() * (W - CIRCLE_WIDTH);
double y = Math.random() * (H - CIRCLE_WIDTH);
Ellipse2D ellipse = new Ellipse2D.Double(x, y, CIRCLE_WIDTH, CIRCLE_WIDTH);
colorStateMap.put(state, ellipse);
}
We draw the ellipses within paintComponent:
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
// make for smooth graphics
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
// iterate through the enum, extracting the ellipse and drawing it
for (ColorState state : ColorState.values()) {
Shape shape = colorStateMap.get(state);
if (shape != null) {
g2.setColor(state.getColor());
g2.fill(shape); // draw the ellipse
}
}
}
Finally in a MouseAdapter (both MouseListener and MouseMotionListener), we listen for mouse presses and register success if the left mouse is clicked and if it is clicked within the appropriate Shape:
private class MyMouse extends MouseAdapter {
private Shape selectedShape = null;
private Point2D offset = null;
#Override
public void mousePressed(MouseEvent e) {
// check that correct button pressed
if (e.getButton() != MouseEvent.BUTTON1) {
return;
}
// has our colorState been set yet? If not, exit
if (colorState == null) {
return;
}
// is an appropriate Shape held by the Map? If so, get it
Shape shape = colorStateMap.get(colorState);
if (shape == null) {
return;
}
// does this shape contain the point where the mouse was pressed?
if (!shape.contains(e.getPoint())) {
return;
}
// Get the selected shape, get the mouse point location relative to this shape
selectedShape = shape;
double x = e.getX() - shape.getBounds2D().getX();
double y = e.getY() - shape.getBounds2D().getY();
offset = new Point2D.Double(x, y);
}
#Override
public void mouseDragged(MouseEvent e) {
// drag shape to new location
if (selectedShape != null) {
double x = e.getX() - offset.getX();
double y = e.getY() - offset.getY();
Rectangle2D bounds = selectedShape.getBounds2D();
bounds.setFrame(new Rectangle2D.Double(x, y, bounds.getWidth(), bounds.getHeight()));
((Ellipse2D) selectedShape).setFrame(bounds);
repaint();
}
}
#Override
public void mouseReleased(MouseEvent e) {
selectedShape = null;
}
}
Note that the mouse dragging code is thanks to MadProgrammer's answer here. Please up-vote this answer.
The entire class looks like so:
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.EnumMap;
import java.util.Map;
import javax.swing.*;
#SuppressWarnings("serial")
public class StateDependentMouseListener extends JPanel {
private static final int W = 800;
private static final int H = 650;
private static final double CIRCLE_WIDTH = 60.0;
private ButtonGroup buttonGroup = new ButtonGroup();
private ColorState colorState = null;
private Map<ColorState, Shape> colorStateMap = new EnumMap<>(ColorState.class);
public StateDependentMouseListener() {
setPreferredSize(new Dimension(W, H));
for (final ColorState state : ColorState.values()) {
// create the JRadioButton
JRadioButton radioButton = new JRadioButton(state.getText());
add(radioButton); // add to GUI
buttonGroup.add(radioButton); // add to ButtonGroup
// give it an ActionListener that changes the object's state
radioButton.addActionListener(e -> {
colorState = state;
});
// create a randomly placed Ellipse2D and place into Map:
double x = Math.random() * (W - CIRCLE_WIDTH);
double y = Math.random() * (H - CIRCLE_WIDTH);
Ellipse2D ellipse = new Ellipse2D.Double(x, y, CIRCLE_WIDTH, CIRCLE_WIDTH);
colorStateMap.put(state, ellipse);
}
MyMouse myMouse = new MyMouse();
addMouseListener(myMouse);
addMouseMotionListener(myMouse);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
// make for smooth graphics
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
// iterate through the enum, extracting the ellipse and drawing it
for (ColorState state : ColorState.values()) {
Shape shape = colorStateMap.get(state);
if (shape != null) {
g2.setColor(state.getColor());
g2.fill(shape); // draw the ellipse
}
}
}
private class MyMouse extends MouseAdapter {
private Shape selectedShape = null;
private Point2D offset = null;
#Override
public void mousePressed(MouseEvent e) {
// check that correct button pressed
if (e.getButton() != MouseEvent.BUTTON1) {
return;
}
// has our colorState been set yet? If not, exit
if (colorState == null) {
return;
}
// is an appropriate Shape held by the Map? If so, get it
Shape shape = colorStateMap.get(colorState);
if (shape == null) {
return;
}
// does this shape contain the point where the mouse was pressed?
if (!shape.contains(e.getPoint())) {
return;
}
// Get the selected shape, get the mouse point location relative to this shape
selectedShape = shape;
double x = e.getX() - shape.getBounds2D().getX();
double y = e.getY() - shape.getBounds2D().getY();
offset = new Point2D.Double(x, y);
}
#Override
public void mouseDragged(MouseEvent e) {
// drag shape to new location
if (selectedShape != null) {
double x = e.getX() - offset.getX();
double y = e.getY() - offset.getY();
Rectangle2D bounds = selectedShape.getBounds2D();
bounds.setFrame(new Rectangle2D.Double(x, y, bounds.getWidth(), bounds.getHeight()));
((Ellipse2D) selectedShape).setFrame(bounds);
repaint();
}
}
#Override
public void mouseReleased(MouseEvent e) {
selectedShape = null;
}
}
private static void createAndShowGui() {
StateDependentMouseListener mainPanel = new StateDependentMouseListener();
JFrame frame = new JFrame("StateDependentMouseListener");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> createAndShowGui());
}
}

fill circle points only during mouse over

I have the following two pieces of code which currently allow me to highlight the circles around the various points of a polygon. THe problem is after my mouse leaves the circle they remain filled. Is there something simple im missing here? Thanks!
public void mouseMoved(MouseEvent e)
{
Graphics2D g2d = (Graphics2D) getGraphics();
if (mode == MODIFY)
// in modify mode only
{
x1 = e.getX();
y1 = e.getY();
shapeList.get(selindex).fillPoint(x1,y1,g2d);
x2 = e.getX();
y2 = e.getY();
if (x1 == x2 && y1 == y2)
{}
else
repaint();
}
}
public void fillPoint(int x, int y, Graphics2D g)
{
for (int t =0; t < npoints;t++)
{
if (thePoints.get(t).contains(x,y))
g.fill(thePoints.get(t));
}
}
public void draw(Graphics2D g)
{
// Implement this method to draw the MyPoly onto the Graphics2D argument g.
// See MyRectangle2D.java for a simple example of doing this. In the case of
// this MyPoly class the method is more complex, since you must handle the
// special cases of 1 point (draw only the point circle), 2 points (draw the
// line) and the case where the MyPoly is selected. You must also use the
// color of the MyPoly in this method.
/*if(highlighted) // this method fills all the circles when selected - a backup piece of code if I couldnt get the proper implimentation to work
{
for (int t =0; t < thePoints.size(); t++)
{
g.fill(thePoints.get(t));
}
}*/
if (thePoints.size() <=2)
{
g.draw(this);
for (int i =0; i <thePoints.size();i++ )
{
g.draw(thePoints.get(i));
}
}
g.setColor(myColor);
if (highlighted)
{
g.draw(this);
for (int i =0; i <thePoints.size();i++ )
{
g.draw(thePoints.get(i));
}
}
else if (!highlighted)
{
if (thePoints.size()>2)
g.fill(this);
else
g.draw(this);
g.fill(this);
}
}
public void paintComponent (Graphics g) // Method to paint contents of panel
{
super.paintComponent(g); // super call needed here
Graphics2D g2d = (Graphics2D) g;
for (int i = 0; i < shapeList.size(); i++)
{
shapeList.get(i).draw(g2d); // IMPLEMENT: draw(). This method will utilize
// the predefined Graphics2D methods draw() (for the outline only,
// when the object is first being drawn or it is selected by the user)
// and fill() (for the filled in shape) for the "basic" Polygon
// but will require additional code to draw the enhancements added
// in MyPoly (ex: the circles indicating the points in the polygon
// and the color). Also special cases for MyPoly objects with only
// 1 or 2 points must be handled as well. For some help with this see
// handout MyRectangle2D
}
}
Suggestions:
Get the Graphics out of the MouseMotionListener.
Instead all that you want to do within the MouseMotionListener will be to:
Un-highlight all the points
Then mark as highlighted (not sure based on your code how you'll do that) any selected point, or point that contains the mouse Point.
Then call repaint(). -- ALWAYS call repaint within the mouselistener.
I would recommend that you have several lists present, including thePoints which can hold your ellipses, as well as lines to hold your lines. Also you'll need a Shape variable to hold the highlighted oval, say called highlightedOval:
private List<Shape> thePoints = new ArrayList<>();
private List<Shape> lines = new ArrayList<>();
private Shape highlightedOval = null;
Then in the MouseMotionListener, you'd keep things simple, and "un-select" the highlighted oval first of all, then in a for loop select it if the oval contains the MouseEvent's Point. Then call repaint():
#Override
public void mouseMoved(MouseEvent e) {
highlightedOval = null;
for (Shape oval : thePoints) {
if (oval.contains(e.getPoint())) {
highlightedOval = oval;
}
}
repaint();
}
For example:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Line2D;
import java.util.ArrayList;
import java.util.List;
import javax.swing.*;
#SuppressWarnings("serial")
public class HighlightPolygon extends JPanel {
private static final Color LINE_COLOR = Color.green;
private static final double OVAL_RAD = 12;
private static final Color HIGHLIGHTED_OVAL_COLOR = Color.RED;
private static final Color OVAL_COLOR = Color.PINK;
private static final int PREF_W = 600;
private static final int PREF_H = PREF_W;
private List<Shape> thePoints = new ArrayList<>();
private List<Shape> lines = new ArrayList<>();
private Shape highlightedOval = null;
public HighlightPolygon(List<Point> points) {
double w = 2 * OVAL_RAD;
double h = w;
for (int i = 0; i < points.size(); i++) {
int x1 = points.get(i).x;
int y1 = points.get(i).y;
double x = x1 - OVAL_RAD;
double y = y1 - OVAL_RAD;
thePoints.add(new Ellipse2D.Double(x, y, w, h));
int i2 = i + 1;
i2 %= points.size();
int x2 = points.get(i2).x;
int y2 = points.get(i2).y;
lines.add(new Line2D.Double(x1, y1, x2, y2));
}
MyMouse myMouse = new MyMouse();
addMouseMotionListener(myMouse);
// addMouseListener(myMouse);
}
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(PREF_W, PREF_H);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
// to give smooth graphics
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
// draw all the ovals (if we want them under the lines
for (Shape oval : thePoints) {
// if our oval is the selected one, fill it with the highlighted color,
// otherwise the regular
Color c = oval == highlightedOval ? HIGHLIGHTED_OVAL_COLOR : OVAL_COLOR;
g2.setColor(c);
g2.fill(oval);
}
g2.setColor(LINE_COLOR);
for (Shape line : lines) {
g2.draw(line);
}
}
private class MyMouse extends MouseAdapter {
#Override
public void mouseMoved(MouseEvent e) {
highlightedOval = null;
for (Shape oval : thePoints) {
if (oval.contains(e.getPoint())) {
highlightedOval = oval;
}
}
repaint();
}
}
private static void createAndShowGui() {
List<Point> points = new ArrayList<>();
points.add(new Point(100, 100));
points.add(new Point(300, 200));
points.add(new Point(500, 100));
points.add(new Point(400, 300));
points.add(new Point(500, 500));
points.add(new Point(300, 400));
points.add(new Point(100, 500));
points.add(new Point(200, 300));
JFrame frame = new JFrame("HighlightPolygon");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(new HighlightPolygon(points));
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}

Detecting when multiple Ellipse objects are being clicked

I am working on a version of the Squares game. For it I need to detect when my Ellipses are being clicked. But the problem is my method is using one Ellipse object. How can I detect which Ellipse is being clicked? Here is my code.
Main Squares class
public static boolean running = false;
public Squares() {
this.setSize(600, 600);
this.setTitle("Squares");
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
this.setContentPane(new SquarePane());
this.setLocationRelativeTo(null);
this.setVisible(true);
}
public static void main(String[] args) {
try {
new Squares();
} catch (Exception e) {
e.printStackTrace();
System.out.println("Crashed");
System.exit(-1);
}
running = true;
}
}
SquaresPanel Class
public static int x = 100;
public static int y = 100;
public static Color randomColor;
public static float r;
public static float g;
public static float b;
public void paintComponent(Graphics gra) {
Graphics2D g2d = (Graphics2D) gra;
gra.setColor(Color.black);
gra.fillRect(0, 0, 600, 600);
Random rand = new Random();
for (int i = 0; i < 8; i++) {
for (int j = 0; j < 8; j++) {
Ellipse2D oval = new Ellipse2D.Double(x, y, 10, 10);
r = rand.nextFloat();
g = rand.nextFloat();
b = rand.nextFloat();
randomColor = new Color(r, g, b);
g2d.setColor(randomColor);
g2d.fill(oval);
x += 50;
}
x = 100;
y += 50;
}
}
Thanks guys!
Will
Without looking too much at your code (as I see it's lacking alot) I will just explain how your requirement can be achieve.
1st : You need a MouseListener and implement the mousePressed. From the MouseEvent object, you can obtain the point clicked. See How to Write MouseListener if you are unsure.
public void mousePressed(MouseEvent e) {
Point p = e.getPoint();
}
2nd: Keep a List of your ellipses
List<Ellipse2D> ellipses;
3rd: Keep a selectedEllipse variable to hold the select one.
Ellipse2D selectedEllipse;
4th: After clicking the point, you loop through the list, checking if each Ellipse2D.contains the point. Then do something with the selected Ellipse
public void mousePressed(MouseEvent e) {
Point p = e.getPoint();
for (Ellipse2D ellipse : ellipses) {
if (ellipse.contains(p) {
selectedEllipse = ellipse;
// do something with selectedEllipse
break;
} else {
selectedEllipse = null;
}
}
}
5th: Loop through your ellipses to paint the in the paintComponent method
protected void paintComponent(Grapchics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g;
for (Ellipse2D ellipse : ellipses) {
g2.fill(ellipse):
}
}
Side Notes
You must call super.paintComponent in your paintComponent method
protected void paintComponent(Graphics g) {
super.paintComponent(g);
}
UPDATE
After taking a closer look at your code, I see more of what you are trying to achieve. Looks like you want am 8 by 8 grid of your ellipses. Another option is just to create 64 panels. and paint each of them.
First have a panel class where you can set the color
public class EllipsePanel extends JPanel {
private Color color;
public EllipsePanel(Color color) {
this.color = color;
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(color);
g.fillOval(0, 0, getWidth(), getHeight());
}
}
Then you can use a panel to hold all those panel and use a GridLayout, but also keeping a JPanel[][] so you can easily refer to each panel. You can also a add a mouselistener to each panel
JPanel gridPanel = new JPanel(new GridLayout(8, 8));
EllipsePanel[][] panels = new EllipsePanel[8][8];
EllipsePanel selectedPanel = null;
int currentRow;
int currentCol;
...
for (int i = 0; i < 8; i++) {
for (int j = 0; i < 8; j++) {
final EllipPanel panel = new EllipsePanel(getRendomColor);
panel.addMouseListener(new MouseAdapter(){
public void mousePressed(MouseEvent e) {
selectedPanel = panel;
// do something with selected panel;
}
});
gridPanel.add(panel);
}
}
You should implement a mouse listener on your JPannel, and then use the position clicked retrieved from the listener to work out which ellipse was clicked

Java simple paint code not working

I have this simple paint code that should draw but instead it moves the oval around the panel.
When I remove super.paintComponent(g) line the program works it paints and not just move the oval, but I keep reading that we should not remove this line, so what can I do to leave the line in but still get the desired results?
class OraclePaint extends JFrame {
public static void main(String[] args) {
OraclePaint ss = new OraclePaint();
ss.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
ss.add(new MyPanel());
ss.setSize(250, 200);
ss.setVisible(true);
}
}
class MyPanel extends JPanel {
private int x = -10, y = -10;
public MyPanel() {
addMouseMotionListener(new MouseMotionAdapter() {
public void mouseDragged(MouseEvent event) {
x = event.getX();
y = event.getY();
repaint();
}
}); // end call to addMouseMotionListener
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.fillOval(x, y, 22, 22);
}
}
Based on the description, I assume that you want something like "a simple paint program".
It is correct to invoke super.paintComponent(g) as the first line of an overridden paintComponent. And it is true that this erases the background (that is, everything that was painted before will be deleted).
In Swing, everything that you want to paint has to be painted in the paintComponent method (or in any method that is called from there, and receives the same Graphics object).
If you want to "save" everything that you have painted, you have to paint everything into an image (that is, into a BufferedImage), and paint this image in your paintComponent method.
There are some other issues with the code, but without changing too much of the remaining code, this could roughly (!) be achieved like this:
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import java.awt.image.BufferedImage;
import javax.swing.JFrame;
import javax.swing.JPanel;
class OraclePaint extends JFrame {
public static void main(String[] args) {
OraclePaint ss = new OraclePaint();
ss.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
ss.add(new MyPanel());
ss.setSize(250, 200);
ss.setVisible(true);
}
}
class MyPanel extends JPanel {
private BufferedImage image = null;
public MyPanel() {
addMouseMotionListener(new MouseMotionAdapter() {
#Override
public void mouseDragged(MouseEvent event) {
if (image != null) {
// Paint into the image
Graphics g = image.getGraphics();
g.setColor(Color.BLACK);
g.fillOval(event.getX(), event.getY(), 22, 22);
g.dispose();
repaint();
}
}
}); // end call to addMouseMotionListener
}
// Make sure that the image is not 'null' and that
// it has the same size as this panel
private void validateImage()
{
if (image == null)
{
image = new BufferedImage(getWidth(), getHeight(),
BufferedImage.TYPE_INT_ARGB);
}
if (image.getWidth() != getWidth() || image.getHeight() != getHeight())
{
BufferedImage newImage = new BufferedImage(getWidth(), getHeight(),
BufferedImage.TYPE_INT_ARGB);
Graphics g = newImage.getGraphics();
g.drawImage(image, 0, 0, null);
g.dispose();
image = newImage;
}
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
validateImage();
g.drawImage(image, 0, 0, null);
}
}
change your MyPanel to this:
class MyPanel extends JPanel
{
private int x2 = 0, y2 = 0;
private int x1 = 0, y1 = 0;
public MyPanel()
{
addMouseMotionListener(new MouseMotionAdapter()
{
public void mouseDragged( MouseEvent event )
{
x2 = event.getX();
y2 = event.getY();
repaint();
}
}
); // end call to addMouseMotionListener
addMouseListener(new MouseListener() {
#Override
public void mousePressed(MouseEvent e) {
x1 = e.getX();
y1 = e.getY();
}
});
}
public void paintComponent(Graphics g)
{
super.paintComponent(g);
g.fillOval(x1, y1, x2, y2);
}
}

Component with transparent background to BufferedImage?

I have a component extends JPanel. It saves itself as bufferedimage on every call of paintComponent method. Component is not completely transparent, only its background. Problem is background is not transparent. I am using setOpaque(false);
Here is my relevant code;
private BufferedImage bufImage = null;
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g;
// if first time call
if (bufImage == null) {
int w = this.getWidth();
int h = this.getHeight();
bufImage = (BufferedImage)this.createImage(w, h);
}
g2.drawImage(bufImage, null, 0, 0);
// draw sth
g2.draw(sth);
}
--
I also tried
bufImage = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
instead of
bufImage = (BufferedImage)this.createImage(w, h);
When i do that; background transperancy works but i can only draw with white color. I have no idea what causes that.
Note:
I used that code to check if it is working;
File outputfile = new File("saved.png");
ImageIO.write(bufImage, "png", outputfile);
saved.png had transparent background but drawings were only white.
This is the component, only lets drawing rectangle with mouse;
class PaintPanel extends JPanel implements MouseListener, MouseMotionListener {
private BufferedImage _bufImage = null;
private boolean dragging = false;
private Point _start = null, _end = null;
public PaintPanel() {
setOpaque(false);
this.addMouseListener(this);
this.addMouseMotionListener(this);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g;
if (_bufImage == null) {
int w = this.getWidth();
int h = this.getHeight();
_bufImage = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
//_bufImage = (BufferedImage)this.createImage(w, h);
}
g2.drawImage(_bufImage, null, 0, 0);
if (dragging) {
drawCurrentShape(g2);
}
}
private void drawCurrentShape(Graphics2D g2) {
int startx = (int) _start.getX();
int starty = (int) _start.getY();
int stopx = (int) _end.getX();
int stopy = (int) _end.getY();
int width = Math.abs(startx - stopx);
int height = Math.abs(starty - stopy);
int x = startx, y = starty;
if(x > stopx)
x = stopx;
if(y > stopy)
y = stopy;
Rectangle r = new Rectangle(x, y, width, height);
g2.draw(r);
}
public void mousePressed(MouseEvent e) {
dragging = true;
_start = e.getPoint();
_end = _start;
}
public void mouseDragged(MouseEvent e) {
_end = e.getPoint();
this.repaint();
}
public void mouseReleased(MouseEvent e) {
_end = e.getPoint();
if (dragging) {
dragging = false;
drawCurrentShape(_bufImage.createGraphics());
this.repaint();
}
}
public void mouseMoved (MouseEvent e) {}
public void mouseEntered(MouseEvent e) {}
public void mouseExited (MouseEvent e) {}
public void mouseClicked(MouseEvent e) {}
}
try this:
bufImage = new BufferedImage(w,h,java.awt.image.BufferedImage.TYPE_INT_ARGB);
Graphics2D graphics = bufImage.createGraphics();
this.print(graphics);
graphics.dispose();
The key is to use print()
Edit: I tried the following and transparency works like a charm:
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class Test2 {
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
final JPanel p = new JPanel();
p.setPreferredSize(new Dimension(400, 400));
p.setOpaque(false);
JButton button = new JButton("Hello world");
p.add(button);
frame.add(p);
frame.pack();
frame.setVisible(true);
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
BufferedImage bufImage = new BufferedImage(p.getWidth(), p.getHeight(), java.awt.image.BufferedImage.TYPE_INT_ARGB);
Graphics2D graphics = bufImage.createGraphics();
p.print(graphics);
graphics.dispose();
try {
ImageIO.write(bufImage, "png", new File("d:/tmp/tmp.png"));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
}
}
createImage(w, h) will create a "blank" image with the specified width and height. That being said, what you'll need to do is invoke createGraphics on the BufferedImage instance and draw directly to the returned Graphics object.

Categories

Resources