Java: Filling in a rectangle on a grid - java

I'd like to begin by saying this is an assignment. I do not want the answer spoon fed to me but I would like to know what is causing my problems.
I am currently implementing Conway's Game of Life. Clicking the cell should change the color, as to represent that cell being switched to an alive state. if clicked again, it should return to the default color.
When I click anywhere in the window, the program throws a Null Pointer Exception at line 56. Have been stuck on this for the last day or so, so any help is appreciated. Thanks!
Heres the code:
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
public class VisibleGrid extends JPanel implements MouseListener, KeyListener{
CellGrid cellGrid;
Graphics rect;
public VisibleGrid(){
addMouseListener(this);
cellGrid = new CellGrid();
}
//Draw the grid of cells, 7px wide, 75 times to create 75x75 grid
public void paint(Graphics g){
for(int i=0; i<525;i=i+7){
for(int j = 0; j<525; j=j+7){
g.drawRect(i ,j,7,7);
}
}
}
//auxillary method called to fill in rectangles
public void paint(Graphics g, int x, int y){
g.fillRect(x, y, 7, 7);
repaint();
}
//main method, adds this JPanel to a JFrame and sets up the GUI
public static void main(String[] args){
JFrame j = new JFrame("Conway's Game of Life");
j.setLayout(new BorderLayout());
j.add(new VisibleGrid(), BorderLayout.CENTER);
JTextArea info = new JTextArea("Press S to Start, E to End");
info.setEditable(false);
j.add(info, BorderLayout.SOUTH);
j.setSize(530,565);
j.setVisible(true);
}
//these methods are to satisfy the compiler/interface
//Begin Mouse Events
public void mouseExited(MouseEvent e){}
public void mouseEntered(MouseEvent e){}
public void mouseReleased(MouseEvent e){}
public void mousePressed(MouseEvent e){}
public void mouseClicked(MouseEvent e){
//fill the selected rectangle
rect.fillRect(e.getX(), e.getY(), 7,7);
repaint();
//set the corresponding cell in the grid to alive
int row = e.getY() /7;
int column = e.getX() /7;
cellGrid.getCell(row, column).setAlive(true);
}
//End Mouse Events
//These methods are to satisfy the compiler/interface
//Begin KeyEvents
public void keyReleased(KeyEvent e){}
public void keyPressed(KeyEvent e){}
public void keyTyped(KeyEvent e){}
}

The problem here is that your rect field is never set to anything so it stays as null. Calling rect.drawRect will cause the NullPointerException you're seeing.
If I remember correctly, Swing Graphics objects don't really like you painting on them when they're not expecting you to be doing any painting. I would therefore recommend against stashing the Graphics object you get during a call to paint() in a field such as rect. If you want to repaint part of the window, it's better to tell Swing what part of the window needs repainting and then let it call your paint() method.
Within your mouseClicked() method, I removed the call to rect.fillRect() and moved the call to repaint() to the end of the method. I also modified the paint() method to draw a filled rectangle if the cell was alive and an unfilled one otherwise. After doing this, your code appeared to work, in that I could click on some cells and they would turn black.
I have a few suggestions for improvements to your code. I'll leave the last two as exercises for you:
I'd recommend adding the line j.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); to main(). This line makes the application quit properly when you close the window.
At the moment, your code is repainting the entire 75 × 75 grid every time a single cell changes. It should be possible to change your code so that it repaints only the changed cell. You can pass a Rectangle to the repaint() method, which tells Swing 'only this part of my component needs to be repainted'. In the paint method, you can get hold of this rectangle using the getClipBounds() method of the Graphics class and use it to determine which cell or cells to repaint.
drawRect only draws the outline of a rectangle. If a cell dies, your paint method won't clear the existing black rectangle from the grid. You could fix this by drawing dead cells as a white filled rectangle with a black outline rectangle on top.

Are you sure the CellGrid objects has been filled with cells? I'm no Java expert but I don't see in your code this initialization...

Related

Updating the position of a rectangle or any other image without overlapping in JPanel

My approach was to use 'reprint()' inside a loop was successful to move an image that means updating its position without overlapping but now I want to see the image moving and I used 'thread.sleep()' to give time gaps between the repaint()s but it doesn't seem to work
import java.awt.*;
import javax.swing.*;
public class jp extends JPanel implements ActionListener{
Timer t;
JPanel jl=new JPanel();
int x,y;
jp(){
x=10;
//y=10;
t=new Timer(5,this);
t.start();
}
public void actionPerformed(ActionEvent e){
x++;
y++;
if(x>500){
x=0;
y=0;
}
repaint();
}
public void paintComponent(Graphics g){
super.paintComponent(g);
this.setBackground(Color.black);
g.setColor(Color.blue);
g.fillRect(x,20,50,50);
}
}
public class Jpanel extends JFrame{
public static void main(String[] args) {
jp p=new jp();
JFrame j=new JFrame("TEST_CASE-1");
j.add(p);
j.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
j.setSize(700,500);
j.setVisible(true);
}
}
If you are hoping to get an animation by having multiple drawings in paintComponent() such as using a for-loop like what you did, you are going to be disappointed.
It will only perform all the drawings and show it to you all at once. Instead of concurrently drawing 200 rectangles, you only need to draw 1 rectangle and update its position.
To perform an animation, you need either a timer (You may try javax.swing.timer) or a loop if you are planning to do something more sophisticated. You can implement an infinite loop and perform the rendering in the loop while updating the position of your rectangle.
Ultimately, no matter which approach you adopt. You will need store the position of your rectangle. Update and render it in such as way:
update position
render
update position
render
update position
render
and not
update position
update position
update position
render
render
render
Example of animation with timer:
Repainting/refreshing the JFrame
Example animating using loop:
while(running){
update(); //update position of your rect
repaint(); //redraw your rect (and all other stuff)
}

Custom Drawing loop in Swing

I'm trying to learn how to do custom GUI stuff in Java for a group project I'm working on. I've done user form type GUIs in the past so I know the gist of what I'm doing here, but the custom drawing stuff still confuses me.
I copied this code from online and I've been trying to figure out how it works, but I don't get why I can't loop the drawing method. As a simple test I'm trying to make the program draw an oval on my cursor. It draws the oval on the cursor, but only once on runtime and then does nothing.
How can I make this loop so I can continue to draw things? Or is there a different way I need to call/use the methods?
public class BombermanGUI extends JFrame {
public static final int CANVAS_WIDTH = 640;
public static final int CANVAS_HEIGHT = 480;
private DrawCanvas canvas;
public BombermanGUI() {
canvas = new DrawCanvas();
canvas.setPreferredSize(new Dimension(CANVAS_WIDTH, CANVAS_HEIGHT));
Container cp = getContentPane();
cp.add(canvas);
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
this.pack();
this.setTitle("......");
this.setVisible(true);
}
private class DrawCanvas extends JPanel{
#Override
public void paintComponent(Graphics g){
super.paintComponent(g);
setBackground(Color.BLACK);
int x, y;
x = MouseInfo.getPointerInfo().getLocation().x - this.getLocationOnScreen().x;
y = MouseInfo.getPointerInfo().getLocation().y - this.getLocationOnScreen().y;
g.setColor(Color.YELLOW);
g.drawOval(x, y, 10, 10);
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new BombermanGUI();
}
});
}
}
Painting a complex series of callbacks and responses to changes within the system. The first thing to remember is that you don't control the painting process, but rather make suggestions to the system so that it can make decisions about what and when it should repaint...
Take a look at Painting in AWT and Swing and Performing Custom Painting for more details.
Painting is a destructive process. It is assumed that when a repaint occurs, that you will repaint the entire state of the current component. This means that you will need some kind of model which maintains all the content that needs to be painted...
Have a look at 2D Graphics, in particular, have a look at the section on Shape
MouseInfo is a seriously crappy way to detect the location of the mouse for this purpose, instead, you should be using a MouseListener and/or MouseMotionListener to detect mouse events.
Basically, when the user presses a mouse button, you would record the location of the mouse press. When the mouse is moved, you would calculate the width and height of the movement relative to the mouse press and update the "current" shape. You would call repaint to request that the UI be updated and paint this shape via the paintComponent method (painting all the previous shapes first).
When the mouse button is released, you would commit the "current" shape to the model, so it will be painted every time paintComponent is called.
THIS IS tobais_k ANSWER IM ANSWERING TO CLOSE THE QUESTION!
Either add an event listener and have it call the repaint method, e.g. a mouse motion listener for tracking your mouse cursos, or have some thread run your game and trigger repaint in regular intervals.

How to set the background in a JPanel without using the super call in paintComponent()

In this program, I want to draw a series of lines that interact to form a web. Each time the timer ticks, a line is drawn. Therefore, I cannot have the super.paintComponent(g) call in the paintComponent() because I need the previous lines to appear. However, I would like to set the background colour and as far as I've found, the setBackground() method can be called only if the super call is first made. I am not sure if the fillRect method would work either because it would draw a rectangle over the old line each time. I tried having the setBackground() method in the constructor, and it did not work either.
Here is my code:
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
public class prettyWebPanel extends JPanel implements ActionListener {
Timer time = new Timer(100,this);
private Color colour1 = Color.black;
private Color colour2 = Color.white;
JButton start = new JButton("Start");
int j = 0;
public prettyWebPanel() {
setPreferredSize(new Dimension (550,550));
this.add(start);
start.addActionListener(this);
setBackground(colour1);
}
public void paintComponent(Graphics g) {
setBackground(colour1);
setForeground(colour2);
if (j<490) g.drawLine(20, 20+j, 20+j, 500);
}
public void actionPerformed(ActionEvent e) {
if (e.getSource() == start) time.start();
else if (e.getSource() == time) {
j+=10;
repaint();
}
}
}
because I need the previous lines to appear.
Then you need to do incremental painting. See Custom Painting Approaches for two common ways to do this:
Keep a List of objects to paint and repaint them every time
Do the painting onto a BufferedImage.

Why won't the JPanel change color and stay that way?

I'm working on a assignment for school but I got a problem :P.
I got this code:
public void mouseEntered(MouseEvent e) {
MyPanel b = (MyPanel)e.getSource();
System.out.println("ID: "+b.getId()+"");
b.setColor(Color.blue);
}
In the MyPanel object I got:
public void setColor(Color kleur) {
if(this.getBackground()==Color.white) {
this.setBackground(kleur);
repaint();
}
}
When I enter the panel with my mouse the color flashes that I entered. But I want it to stay the color so I can draw a trail in a Jform with 500 Jpanels(I've added them to a ArrayList but this part works just fine)
What am I doing wrong?
Based on #ErickRobertson's comment on the question, I guess the problem is the following:
Your MyPanel replaces the JPanel#paintComponents() method. Is that possible? If so, you could do the following. In your MyPanel#setColor(Color) method, you don't set the background, but a field containing your new background color:
private Color backgroundColor = Color.white;
public void setColor(Color kleur) {
backgroundColor = kleur;
repaint();
}
Then, in your MyPanel#paintComponents(Graphics):
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
// draw background
g.setColor(backgroundColor);
g.fillRect(0, 0, getWidth(), getHeight());
// draw your stuff here
}
Make sure that only one JPanel is visible at a time.
When you add the JPanels to their parent, are they all going on top of each other? If so, then when you call repaint() on one of them, it's being repainted immediately and you can see it as blue. But as soon as the whole window repaints again, the JPanels are painted in the order they have been added and the last one is ultimately painted on top. This panel still has a white background, so that's what you're seeing.
Make sure only one of these panels are visible at a time, or that you have some plan in place to manage these panels so that only one of them are visible. Otherwise, make sure they are laid out in a grid or some other way so they don't appear on top of each other.
Where is your MouseListener implemented, since you are getting the panel from the MouseEvent. It's easier to have the panels implement the MouseListener and let them decide when to change color for themselves.
class Panel extends JPanel implements MouseListener {
public Panel() {
// Make sure the listener listens
addMouseListener(this);
}
#Override
public void mouseEntered(MouseEvent e) {
setColor(Color.blue);
}
// ... other mouselisteners can be ignored or implemented as needed
}
You can still keep a reference to some other class if you need it to be notified of a mouseenter. Just create a private member and set the reference in the constructor.
public void setColor(Color kleur) {
if(this.getBackground()==Color.white) {
this.setBackground(kleur);
repaint();
}
}
dont use == repalce with equals and try invalidate(. Your code is basically saying only replace background if background is white ???

How to draw a filled oval where a mouse clicked on Jpanel

I want to write a code to draw a filled oval where ever the mouse is clicked inside a panel. I used to develop some codes but unfortunately when I tried to do the next click the whole panel blanked and new point appeared. I want to keep the previous points and add some new ones by the next user’s click on the panel. How do I implement the paint component of MyPanel? Here is my code; it does not work properly, because it produces some small points instead of rectangle.
class MyPanel extends JPanel {
Point pointClicked;
public MyPanel() {
this.addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
pointClicked = e.getPoint();
}
});
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.fillRect(pointClicked.x, pointClicked.y, 1, 1);
}
}
I want to keep the previous points and add some new ones by the next user’s click on the panel.
You need to keep track of each oval painted and repaint all ovals each time the paintComponent() method is called.
Check out Custom Painting Approaches for two different ways to do this

Categories

Resources