0

I have a two classes, one that contains the main method and calls the paint method which is called CityScape and another called Building.

I'm trying to create a cityscape with 5 buildings whose windows are randomly off or on (gray or yellow). The problem is that every time the repaint method is called, the windows randomly change.

How can I make java remember the state of the buildings (i.e. the lights) and make them not change when the repaint method is called (e.g. when the window is resized).

Here is the CityScape class

import javax.swing.*;
import java.awt.*;

public class CityScape extends JPanel {
  private Building building1 = new Building(this, new Rectangle(25, 300, 50, 250));
  private Building building2 = new Building(this, new Rectangle(125, 350, 100, 200));
  private Building building3 = new Building(this, new Rectangle(275, 250, 100, 300));
  private Building building4 = new Building(this, new Rectangle(425, 350, 50, 200));
  private Building building5 = new Building(this, new Rectangle(525, 400, 100, 150));


  public static void main(String[] args) throws InterruptedException {
    JFrame frame = new JFrame("CITYSCAPE");
    frame.setSize(675, 600);
    frame.setVisible(true);
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    CityScape c = new CityScape();
    frame.add(c);

  }


  public void paint(Graphics g) {
    super.paint(g);
    Graphics2D g2d = (Graphics2D) g;
    setBackground(g2d);
    paintBuildings(g2d);
  }

  public void paintBuildings(Graphics2D g2d) {
    building1.paint(g2d);
    building2.paint(g2d);
    building3.paint(g2d);
    building4.paint(g2d);
    building5.paint(g2d);
  }

  public void setBackground(Graphics2D g2d) {
    g2d.setColor(new Color(255, 102, 25));
    g2d.fillRect(0, 0, 1000, 1000);
    g2d.setColor(Color.DARK_GRAY);
    g2d.fillRect(0, 550, 700, 700);

  }


}

and here is the building class:

import java.awt.*;

public class Building {

  private CityScape cityScape;
  private Rectangle r;

  public Building(CityScape cityScape, Rectangle r) {
    this.cityScape = cityScape;
    this.r = r;
  }

  public void paint(Graphics2D g2d) { //Draws the windows on the building (either with lights off or on)
    int x = 0;
    int y = 0;
    for (x = 0; x < r.getWidth(); x = x + 25) {
      for (y = 0; y < r.getHeight(); y = y + 25) {
        if (randBool()) {
          g2d.setColor(Color.YELLOW);
          g2d.fillRect((int) r.getX() + x, (int) r.getY() + y, 25, 25);
          g2d.setColor(Color.WHITE);
          g2d.fillRect((int) r.getX(), (int) r.getY() + y, (int) r.getWidth(), 1);
        } else {
          g2d.setColor(Color.GRAY);
          g2d.fillRect((int) r.getX() + x, (int) r.getY() + y, 25, 25);
          g2d.setColor(Color.WHITE);
          g2d.fillRect((int) r.getX(), (int) r.getY() + y, (int) r.getWidth(), 1);
        }
        g2d.fillRect((int) r.getX() + x, (int) r.getY(), 1, (int) r.getHeight());
      }
    }
    g2d.fillRect((int) r.getX() + x, (int) r.getY(), 1, (int) r.getHeight());
  }




  public static boolean randBool() {
    return Math.random() < 0.5;
  }


}

Thank you

J.Doe
  • 3
  • 1

1 Answers1

0

You can't control the painting process as it will redraw for numerous reasons (such as re-sizing as you mentioned).

Instead you need to make it so that what is drawn is the same each time. You could make it so a Building is always drawn the same way once created.

Alternatively, you could draw the Building to a BufferedImage using Graphics2D obtained by BufferedImage::createGraphics(), being sure to maintain a reference to the BufferedImage you draw, and then draw the BufferedImage to your JPanel using Graphics::drawImage().

public class Building {

    private CityScape cityScape;
    private Rectangle r;
    private BufferedImage img;

    public Building(CityScape cityScape, Rectangle r) {
        this.cityScape = cityScape;
        this.r = r;
        this.img = draw();
    }

    private BufferedImage draw() {
        BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        // you will need to set width and height!

        Graphics2D g2d = img.createGraphics();
        // draw the image as before, but to the BufferedImage this time

        return img; // image of the buildings
    }

    public BufferedImage getImg() {
        return this.img; // returns the BufferedImage
    }

    private static boolean randBool() {
        return Math.random() < 0.5;
    }
}

Example of drawing BufferedImage, where myBuilding is an instance of Building:

@Override
public void paintComponent(Graphics g) {
    super.paintComponent(g);
    g.drawImage(myBuilding.getImg(), x, y, null);
}

Note: With a JPanel and any non-top level containers (e.g. JFrame is a top level container) you should override paintComponent() and not paint() or paintComponents() with the s.

Swing programs should override paintComponent() instead of overriding paint(). Although the API allows it, there is generally no reason to override paintBorder() or paintComponents() (and if you do, make sure you know what you're doing!). This factoring makes it easier for programs to override only the portion of the painting which they need to extend. For example, this solves the AWT problem mentioned previously where a failure to invoke super.paint() prevented any lightweight children from appearing.

See: Difference between paint, paintComponent and paintComponents in Swing and Painting in AWT and Swing

Community
  • 1
  • 1
d.j.brown
  • 1,822
  • 1
  • 12
  • 14