I am trying to make a program with a simple grid of rectangles where, if the user clicks on a rectangle, it is filled black. The problem I am having is that my mouseClicked method cannot access my paintComponent method, so I get an error.
Here is relevant code:
public void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
super.paintComponent(g2);
g2.setColor(Color.BLACK);
for(Rectangle2D rect : squares) {
g2.draw(rect);
}
}
public void mouseClicked(MouseEvent e) {
if((e.getX()>RECT_WIDTH && e.getX()<RECT_WIDTH+(game.getSize()-1)*BOX_DIM) && (e.getY()>RECT_HEIGHT && e.getY()<RECT_HEIGHT+(game.getSize()-1)*BOX_DIM)) {
Point2D point = new Point2D.Double(e.getX(), e.getY());
Rectangle2D rect = findRect(point);
g.setColor(Color.BLACK);
g.fill(rect);
repaint();
}
}
public Rectangle2D findRect(Point2D p) {
for(Rectangle2D rect : squares) {
if(rect.contains(p)) {
return rect;
}
}
return null;
}
"squares" is an arraylist of rectangle2Ds. The error is in the mouseClicked method on 'g' because eclipse cannot find g. Thanks for any help!
In your List object you need to store an object that contains two pieces of information:
The Rectangle
the Color of the Rectangle
When you click on the Rectangle you iterate through the List to find the Rectangle that was clicked and then you update the Color property of that Rectangle and invoke repaint().
You will also need to change the painting code to set the Color of the Rectangle before you invoke the draw() method.
Check out the DrawOnComponent example found in Custom Painting Approaches. It shows how to create the custom object to store two properties and how to paint this object in the paintComponent() method.
Related
I'm new to Java and don't know exactly what the cause.Let me explain the issue
I created a Rectangle Shape and its working, then i thought about changing its color to black for some testing but it seems not working below is my code.
When i call the method from paintComponent itself then its working but if i do the same from any other method then its not changing the color. I tried calling the method repaint also but still the same
public class Meme extends JPanel {
Rectangle2D.Float myRect = new Rectangle2D.Float(90, 90, 90, 90);
Graphics2D graphics2d;
public void DRAW() {
graphics2d.setColor(new Color(0, 0, 200));
graphics2d.fill(myRect);
}
public void ChangeColour() {
System.out.println("Called");
graphics2d.setPaint(Color.BLACK);
System.out.println("Called2");
graphics2d.fill(myRect);
System.out.println("Called3");
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
graphics2d = (Graphics2D) g;
graphics2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
graphics2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
DRAW();
}
}
Button click listener method
private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {
meme1.ChangeColour();
}
As far as I can remember, whenever you change some properties (color in this case), you have to call repaint. This will invoke a call to paintComponent and the frame will be drawn once again.
In your case, I am guessing even if you call repaint after changing color, the DRAW method gets called again in paintComponent which resets the changed color back to (0, 0, 200). Therefore, you don't see any change in the screen. But when you call changeColor in paintComponent method (assuming after the call to DRAW), the change of color persists and does not get overridden.
POSSIBLE SOLUTION
Just keep the color stored somewhere else. Like
Color myColor = new Color(0,0,200);
then in DRAW:
private void DRAW() {
graphics2d.setColor(myColor);
graphics2d.fill(myRect);
}
and in ChangeColor:
private void ChangeColour() {
myColor = Color.BLACK;
}
Hope it helps.
update your function like this
public void ChangeColour() {
System.out.println("Called");
graphics2d.setColor(new Color(1, 1, 200));
System.out.println("Called2");
graphics2d.fill(myRect);
System.out.println("Called3");
}
Painting in Swing is both passive and destructive. That is, a paint pass can occur at anytime for any number of reasons, many which you don't control. Destructive means, on each paint pass you are expected to repaint the entire component from scratch.
In Swing, you update the state you want change and then call repaint to trigger a new paint pass.
Painting should only ever paint the current state, it should never try and change it
public class Meme extends JPanel {
Rectangle2D.Float myRect = new Rectangle2D.Float(90, 90, 90, 90);
private Color color;
public void draw(Graphics2D graphics2d) {
graphics2d.setColor(color);
graphics2d.fill(myRect);
}
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
public void ChangeColour() {
color = Color.BLACK;
repaint();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
graphics2d = (Graphics2D) g.create();
graphics2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
graphics2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
draw(graphics2d);
graphics2d.dispose();
}
}
Also, the graphics context passed to your component is shared with all the other components, so it's important that any significant changes you make to the context are undone before the method exists - in most cases, it's just a simple case of calling create on the Graphics context to snapshot it state and dispose (on the copy you created) when you're done
I am trying to do a little program on Eclipse. The program goes like this: when I click for the 1st time on thr Panel on the frame, a line has to be drawn regarding the Y position of my mouse listener.The line takes all the width of the panel. On the 2nd click, another line has to be drawn, again regarding the Y position of where I clicked. After, I'll put a little circle between the 2 lines and make a little animation with it.
But now, I have a problem. When I click on the panel, a line is drawn, but if i click another time, the first line disappears and the 2nd line takes it place...
This is the code of the painComponent and my mousr listener. What is wrong with it ?
public Lines() {
addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
posY=e.getY();
posX=e.getX();
nbClic=e.getClickCount();
repaint();
}
});
setBackground(Color.black);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setColor(Color.blue);
if(nbClic>=1){
line1=new Line2D.Double(0, posY, getWidth(), posY);
g2d.draw(line1);
repaint();
}
if(nbClic>=2){
g2d.setColor(Color.YELLOW);
line2=new Line2D.Double(0, posY, getWidth(), posY);
g2d.draw(line2);
}
repaint();
}
Painting is an event that draws the entire component. You can't depend on past events because they are erased each time a repaint happens.
You would need to keep something like a List and each time you create a new line, you add it to the List.
List<Integer> yClicks = new ArrayList<>();
... {
addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
yClicks.add(e.getY());
repaint();
}
});
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D)g.create();
for(int y : yClicks) {
g2d.draw(new Line2D.Double(0, y, getWidth(), y));
}
g2d.dispose();
}
Also:
Never call repaint inside paintComponent! This will cause an endless cycle of repaints.
paintComponent is a protected method and should remain so unless there is a compelling reason to make it public.
Be careful changing the state of the Graphics object passed in to paintComponent because it is used elsewhere. Usually we create a local copy which is disposed when we are done.
I'm trying to draw inside my JPanel but everytime I click, the background of my JPanel disappears. It draws a line where the mouse is. I think it has something to do with the 2D graphics
Can someone help?
public Brush() {
addMouseListener(this);
addMouseMotionListener(this);
setBackground(Color.white);
}
#Override
public void paintComponent(Graphics g) {
Graphics2D g2;
// super.paintComponent(g);
g2 = (Graphics2D) g;
g2.setColor(brushColor);
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.setStroke(new BasicStroke(8, BasicStroke.CAP_ROUND, BasicStroke.JOIN_BEVEL));
//Ellipse2D.Double circle = new Ellipse2D.Double(p1.x,p1.y,20,20);
g2.fillOval(p1.x,p1.y,20,20);
}
#Override
public void mousePressed(MouseEvent e) {
dragging = true;
p1 = e.getPoint();
repaint();
}
#Override
public void mouseReleased(MouseEvent e) {
dragging = false;
p1 = e.getPoint();
repaint();
}
#Override
public void mouseDragged(MouseEvent e) {
if (dragging) {
p1 = e.getPoint();
repaint();
}
}
Always call the super.paintComponent(g) method inside of your override.
You're drawing wrong then. If you want to draw a bunch of ovals, then either
create a collection of them and draw them with a for loop in paintComponent, or
draw them in a BufferedImage which is then drawn in your paintComponent method.
If I want to draw a curve with the mouse, I usually create an ArrayList<Point> and draw lines between contiguous points, either in paintComponent or in a BufferedImage.
Again, your code is written to draw only one point (oval actually) within paintComponent. If coded correctly, this is all it will do.
I suggest, the easiest thing to do is:
Give you class an ArrayList<Point>
Add points when mouse is pressed and call repaint
In paintComponent, call the super method, and then use a for loop to iterate through the ArrayList.
Start the loop at the Point at item 1, not 0, and then draw a line between the previous Point and the current point.
To get fancier, you may wish to have an ArrayList<ArrayList<Point>> where you start a new ArrayList<Point> with each press of the mouse, finish it with each release and add it to the overall collection. This will allow several lines to be drawn.
Why not give this a go on your own first?
I'm trying to draw objects onto a canvas from an array, but the thing is, I have no clue how to? This must include the position and sizes of the shapes, and there will be more than one type of shape. The code I've got so far(It's inefficient/bad though)
public class MCanvas extends Canvas {
private Object[] world = {};
public void paint(Graphics g){
try{
// How to paint all the shapes from world here?
} catch (NullPointerException e) {
System.out.println(e.toString());
}
}
}
Any ideas? Thanks.
If your using objects that extend from java.awt.Shape, you can translate and draw them by using a Graphics2D context
Since (some whe around Java 1.3/4), the paint engine is guaranteed to use Graphics2D instance.
public void paint(Graphics g){
super.paint(g);
Graphics2D g2d = (Graphics2D)g;
for (Object o : world) {
if (o instanceof Shape) {
Shape shape = (Shape)o;
//if the shape isn't created with
// a location, you can translate them
shape.translate(...,...);
g2d.setColor(....);
g2d.draw(shape);
//g2d.fill(...);
}
}
}
You might like to take a look at 2D Graphics for more details.
Also, use a JPanel instead of Canvas and then override its paintComponent method
Have a look at Custom Painting for more details
I have a JComponent with a listener on it. On the JComponent, I draw a big image and the mouse listener adds small images where clicks occur (one big map on which I add some dots).
How can I programatically draw something outside the paintComponent method?
public void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
g2.drawImage(img1, 0, 0, this);
g2.finalize();
}
private MouseListener listener;
public void initListener() {
myCanvas = this;
listener = new MouseAdapter() {
public void mouseClicked(MouseEvent e) {
myCanvas.getGraphics().drawImage(img,e.getX(),e.getY(), myCanvas);
}
};
addMouseListener(listener);
}
My problem is with this:
public void drawDot(int x, int y){
myCanvas.getGraphics().drawImage(img, x, y, myCanvas);
}
It doesn't do anything. I have tried repaint().
You can't do this. All drawing occurs in the paintComponent() method. What you should do is build a model that represents what you want to draw, and modify the model in your mouse listener. Then call repaint() to ask that this component be redrawn when the model is modified. Inside your paint() method render the full paint from the model. For example:
List<Point> pointsToDrawSmallerImage = new ArrayList<Point>();
...
listener = new MouseAdapter() {
public void mouseClicked(MouseEvent evt ) {
pointsToDrawSmallerImage.add( evt.getPoint() );
repaint();
}
}
...
public void paintComponent(Graphics g) {
g.clear(); // clear the canvas
for( Point p : pointsToDrawSmallerImage ) {
g.drawImage(img, p.x, p.y, myCanvas);
}
}
You have to manage the drawing inside the paintComponent method. Java Graphics is not stateful, you have to take care of what you actually need to draw whatever you want inside the method. Every time the paint method is called, everything must be drawn again, there is nothing that "stays" on the canvas while adding other components
This means that you should store a list of elements that the paint method will take care to draw, eg. ArrayList<Point> points, then in paint method you should iterate them:
for (Point p : points)
draw the point
so that you just add the point to the list with the listener and call repaint.
You can find guidelines for Swing/AWT drawing here..
A particual API has the behavior you would like to have though, it is called Cocos2D and it has a port for Android/Java that you can find here.
that is not how draw works, the draw method paints everything which is in the method itself on every repaint,
that means if you call a method to draw something once, it will only be drawed for one repaint cycle and that's it.
if you want something t be drawn on click you have to add it on on click to a collection and draw the whole collection in every paint cycle, so it will stay permanently.