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
Related
I would like to move Shape objects, which I created previously. I cannot figure out how to change (update) shape location. Now I am getting cast error:
java.awt.geom.Ellipse2D$Double cannot be cast to java.awt.Graphics2D
Now I can access particular shape, but it seems it doesn't have setLocation() or something like that.
Please give me tip, because I got stuck.
Frame
public class Frame {
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.setSize(600, 600);
frame.setTitle("Prosty Paint");
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
Drawer drawer = new Drawer(frame.getComponents());
Menu menu = new Menu(drawer);
frame.setJMenuBar(menu.menuBar);
frame.add(drawer);
frame.setVisible(true);
}
}
Drawer
public class Drawer extends JPanel implements MouseListener, MouseMotionListener {
private final int defaultSize = 50;
private Color defaultColor = Color.BLACK;
private int oldX, oldY, currentX, currentY;
public String currentShape = "";
public Boolean editMode = false;
private int activeShape;
private final ArrayList<Shape> shapes = new ArrayList<>();
private final ArrayList<Color> colors = new ArrayList<>();
public Drawer(Component... components) {
for (Component c : components) {
c.addMouseListener(this);
c.addMouseMotionListener(this);
}
colors.add(Color.BLACK);
}
#Override
public void mousePressed(MouseEvent e) {
Point point = this.getMousePosition();
currentX = point.x;
currentY = point.y;
if (editMode) {
for (int i = 0; i < shapes.size(); i++) {
Shape shape = shapes.get(i);
if (shape.contains(e.getPoint())) {
System.out.println("Clicked shape " + i);
activeShape = i;
}
}
} else {
switch (currentShape) {
case "circle":
Ellipse2D ellipse2D = new Ellipse2D.Double(currentX, currentY, defaultSize, defaultSize);
shapes.add(ellipse2D);
colors.add(defaultColor);
break;
case "rec":
Rectangle2D rectangle2D = new Rectangle2D.Double(currentX, currentY, defaultSize, defaultSize);
shapes.add(rectangle2D);
colors.add(defaultColor);
break;
}
repaint();
}
}
#Override
public void mouseReleased(MouseEvent e) {
Point point = this.getMousePosition();
currentX = point.x;
currentY = point.y;
if(editMode){
Shape shape = shapes.get(activeShape);
Graphics2D g = (Graphics2D) shape;
g.translate(currentX,currentY);
}
repaint();
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
for (int i = 0; i < shapes.size(); i++) {
Shape shape = shapes.get(i);
Color color = colors.get(i);
g2.setColor(color);
g2.fill(shape);
}
}
}
You haven't provided SSCCE (your code cannot be compiled by me) so I cannot test my solution, but your approach in the method mouseReleased is wrong. To translate the shape you need to replace
Graphics2D g = (Graphics2D) shape;
g.translate(currentX,currentY);
by
AffineTransform transform = new AffineTransform();
transform.translate(currentX, currentY);
shape = transform.createTransformedShape(shape);
shapes.set(activeShape, shape);
If it doesn't help please provide a SSCCE.
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();
}
});
}
}
Recently I've been working on a program that paints an area with empty, colored squares. Their locations on the screen are based off of the values 1 and 2 in a text file. 1s are supposed to make red boxes, and 2s are supposed to make green boxes. However, when I run the program, only red boxes are painted. I did some testing and found out that the repaint method is only being called twice(once sometimes for some reason), even though there are close to 300 values in the file, and repaint() should be called once for every value. Here is my code:
public class MAP extends JFrame {
public static void main(String[] args) throws IOException {
MAP map = new MAP();
}
Shape shape;
int x = -32;
int y = 0;
ArrayList<Shape> shapes = new ArrayList<Shape>();
Graphics2D g2;
Color coulor = null;
private class PaintSurface extends JComponent {
public PaintSurface() {
}
public void paint(Graphics g) {
g2 = (Graphics2D) g;
g2.setColor(coulor);
for (Shape s : shapes) {
g2.draw(s);
}
}
}
public MAP() throws FileNotFoundException, IOException {
JFrame frame = new JFrame();
JPanel panel = new JPanel();
frame.add(panel);
frame.setTitle("Grid Maker");
frame.setSize(400, 200);
frame.setDefaultCloseOperation(EXIT_ON_CLOSE);
frame.add(new PaintSurface(), BorderLayout.CENTER);
frame.setVisible(true);
readNextLine();
}
private void readNextLine() throws IOException {
File file = new File("map.txt");
BufferedReader in = new BufferedReader(new FileReader(file));
String line = in.readLine();
while (line != null) {
for (int i = 0; i < line.length(); i++) {
char c = line.charAt(i);
if (c == '1') {
coulor = Color.RED;
x += 32;
int smallX = x / 32;
int smallY = y / 32;
shape = new Rectangle2D.Float(x, y, 32, 32);
shapes.add(shape);
repaint();
} else if (c == '2') {
coulor = Color.GREEN;
x += 32;
int smallX = x / 32;
int smallY = y / 32;
shape = new Rectangle2D.Float(x, y, 32, 32);
shapes.add(shape);
repaint();
}
}
line = in.readLine();
x = -32;
y += 32;
}
}
}
Why isn't this code working properly?
Just to add to other answers, here is a piece of code (based on yours) which looks already a lot better (yet there are still some issues, but you are not there yet):
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Shape;
import java.awt.geom.Rectangle2D;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JComponent;
import javax.swing.JFrame;
public class MAP extends JFrame {
public static void main(String[] args) throws IOException {
MAP map = new MAP();
}
public static class ColoredShape {
private Shape shape;
private Color color;
public ColoredShape(Shape shape, Color color) {
super();
this.shape = shape;
this.color = color;
}
public Shape getShape() {
return shape;
}
public Color getColor() {
return color;
}
}
int x = -32;
int y = 0;
List<ColoredShape> shapes = new ArrayList<ColoredShape>();
Graphics2D g2;
private class PaintSurface extends JComponent {
public PaintSurface() {
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g2 = (Graphics2D) g;
for (ColoredShape s : shapes) {
g2.setColor(s.getColor());
g2.draw(s.getShape());
}
}
}
public MAP() throws FileNotFoundException, IOException {
JFrame frame = new JFrame();
frame.setTitle("Grid Maker");
frame.setSize(400, 400);
frame.setDefaultCloseOperation(EXIT_ON_CLOSE);
frame.add(new PaintSurface(), BorderLayout.CENTER);
frame.setVisible(true);
readNextLine();
}
private void readNextLine() throws IOException {
BufferedReader in = new BufferedReader(new StringReader("11121\n1221\n2212\n221121\n111221\n11221\n222\n2222\n"));
String line = in.readLine();
while (line != null) {
for (int i = 0; i < line.length(); i++) {
char c = line.charAt(i);
Color color = null;
if (c == '1') {
color = Color.RED;
} else if (c == '2') {
color = Color.GREEN;
}
if (color != null) {
shapes.add(new ColoredShape(new Rectangle2D.Float(x, y, 32, 32), color));
x += 32;
repaint();
}
}
line = in.readLine();
x = -32;
y += 32;
}
}
}
Painting is transient, or stateless.
repaint is a "request" made to the repaint manager to tell it that it should, at some time in the future, when it's ready, it should paint some portion of the screen, that it deems to be dirty.
This means that when you call g2.setColor(coulor) in you paint method, it is using the LAST value that it was set to (when paint is called)....which is probably RED.
Raufio is right, you should be providing color information along with the shapes. Personally, I would set up a second List which just contained Color objects, where each index of the Shape list corresponded directly to the Color in the Color List.
Check out Painting in AWT and Swing for more details on how painting in works in Swing.
Now, to the whiny part ;)
It is not recommended to override paint. There are lots of reasons for this, paint is responsible for calling a number of important methods, including paintChildren and paintComponent, which perform very important tasks.
Instead, you should override paintComponent (and make sure you call super.paintComponent)
Check out Performing Custom Painting for more details.
Update with rough example
So this is a rough example of what I'm talking about...
public class TestPainting {
public static void main(String[] args) {
new TestPainting();
}
public TestPainting() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception ex) {
}
JFrame frame = new JFrame("Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new PaintingPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class PaintingPane extends JPanel {
private static final int WIDTH = 200;
private static final int HEIGHT = 200;
private List<Shape> shapes;
private List<Color> colors;
public PaintingPane() {
shapes = new ArrayList<>(25);
colors = new ArrayList<>(25);
for (int index = 0; index < (int) Math.round(Math.random() * 100); index++) {
int x = (int) Math.round(Math.random() * (WIDTH * 0.75f));
int y = (int) Math.round(Math.random() * (HEIGHT * 0.75f));
int width = (int) Math.round(Math.random() * (WIDTH * 0.25f));
int height = (int) Math.round(Math.random() * (HEIGHT * 0.25f));
if (width < 5) {
width = 5;
}
if (height < 5) {
height = 5;
}
if (x + width > WIDTH) {
x -= width - WIDTH;
}
if (y + height > HEIGHT) {
y -= height - HEIGHT;
}
if (x < 0) {
x = 0;
}
if (y < 0) {
y = 0;
}
Color color = ((int)Math.round(Math.random() * 2)) == 1 ? Color.RED : Color.GREEN;
shapes.add(new Rectangle(x, y, width, height));
colors.add(color);
}
}
#Override
public Dimension getPreferredSize() {
return new Dimension(WIDTH, HEIGHT);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
for (int index = 0; index < shapes.size(); index++) {
g2d.setColor(colors.get(index));
g2d.draw(shapes.get(index));
}
g2d.dispose();
}
}
}
The first thing I see is that you are only coloring shapes one color at a time. So here:
public void paint(Graphics g) {
g2 = (Graphics2D) g;
g2.setColor(coulor); //set the drawing color
for (Shape s : shapes) {
g2.draw(s); //draw in that color
}
}
All your shapes are being drawn in the same color, when you want to color them differently. I think a better way to do things is to add all your shapes into your list, keeping track of their color, and calling repaint() once. Also, I would change the paint method to something to the effect of:
public void paint(Graphics g) {
g2 = (Graphics2D) g;
for (Shape s : shapes) {
g2.setColor(coulor[indexOfShape]); //set the drawing color
g2.draw(s); //draw in that color
}
}
Also, for repaint only being called twice: It is probably throwing an IOException. Try using a try {...} catch(IOException e) {...} block instead of just throwing it up the line. Something like:
private void readNextLine() {
try {
File file = new File("map.txt");
BufferedReader in = new BufferedReader(new FileReader(file));
String line = in.readLine();
...
...
} catch (IOException e) {
e.printStackTrace();
}
}
It should complain about something if it isn't reading right.
I am practicing with Java swing. I've created a matrix as 2D array of Rectangle2D objects. I've set the MouseListener to paint the square which was clicked. I've set up the for loop which checks which square was clicked. When I click on a square it's painted in blue. The thing is, when I click on the other square, the first one turns white and the other turns blue. My question is, how to keep the color of the square which was first clicked?
Here's the code of Matrica(Matrix) class(It's in my native tongue, crtanjeMatrice means drawMatrix and duzina is length.
public class Matrica extends JPanel implements MouseListener {
Rectangle2D[][] matrica = new Rectangle2D[8][8];
Color boja = Color.white;
int a = 0;
int b = 0;
public Matrica() {
addMouseListener(this);
}
public void paint(Graphics g){
Graphics2D g2 = (Graphics2D)g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.clearRect(0, 0, this.getWidth(), this.getHeight());
crtanjeMatrice(g2);
g2.setColor(boja);
g2.fill(matrica[a][b]);
}
private void crtanjeMatrice(Graphics2D g2){
g2.setColor(Color.black);
int startX = 0;
int startY =0;
int duzina = 50;
for(int i=0; i < matrica.length; i++){
for(int j = 0; j < matrica.length; j++){
matrica[i][j] = new Rectangle2D.Double(startX,startY,duzina,duzina);
g2.draw(matrica[i][j]);
startX = startX + duzina;
}
startY = startY + duzina;
startX = 0;
}
}
#Override
public void mouseClicked(MouseEvent e) {
}
#Override
public void mousePressed(MouseEvent e) {
int m,n;
int x = e.getX();
int y = e.getY();
for(int i = 0; i < matrica.length; i++)
for(int j =0; j < matrica.length; j++)
if(matrica[i][j].contains(x, y)){
boja = Color.blue;
a = i;
b = j;
repaint();
}
}
#Override
public void mouseReleased(MouseEvent e) {
}
#Override
public void mouseEntered(MouseEvent e) {
}
#Override
public void mouseExited(MouseEvent e) {
}
}
You are currently paint a single matrix cell in the paint method:
g2.fill(matrica[a][b]);
but you need to "remember" which rectangles have been clicked. To this end, a simple List can be used to store the indices of rectangles already clicked. A class based on the Point class offers a good means of storing row-column index information in the list.
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.clearRect(0, 0, this.getWidth(), this.getHeight());
crtanjeMatrice(g2);
for (MyRowColumn index : indexList) { // MyRowColumn based on Point
g2.setColor(Color.blue);
g2.fill(matrica[index.x][index.y]);
}
}
Aside:, don't use override paint, override paintComponent instead and remember to call super.paintComponent to render child components.
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 ;)