I've been trying for quite a while to get this to work and done a lot of research but I can't work out why it's not happening: My end goal is get a grid of x by y green squares, and when you click on a square it changes colour. (this is part of a bigger project)
Now I am aware that this is probably a very inefficient way of doing so, but I've tried different ways and nothing seems to work.
In my Grid class I have this:
squares = new Square[width][height];
for (int i = 0; i < width; i++){
for (int j = 0; j < height; j++){
squares[i][j] = new Square(i,j);
frame.getContentPane().add(squares[i][j]);
}
}
And this is my Square class:
public class Square extends JLabel implements MouseListener{
private int x,y,width,height, mouseX,mouseY;
private Color colour = Color.GREEN;
public Square(int width, int height){
this.x = width*45;
this.y = height*45;
addMouseListener(this);
this.setBounds(x, y, 45, 45);
}
public void paintComponent(Graphics g){
Graphics2D g2 = (Graphics2D) g;
g2.setColor(colour);
g2.fillRect(x, y, 45, 45);
}
#Override
public void mouseClicked(MouseEvent e) {
mouseX = e.getX();
mouseY = e.getY();
if(mouseX > x && mouseX < x+45 && mouseY > y && mouseY < y+45){
if(colour.equals(Color.GREEN)){
colour = Color.RED;
} else{
colour = Color.GREEN;
}
repaint();
}
}
}
However when I run the code, all I see is the first square and the last square; how come?
If you have any tips of how I could have done this far more efficiently, please do tell and criticize this code.
To solve your problem. You need to change your code in some places.
1) You don't need to pass x, y co-ordinates with Square() constructor. Declare empty constructor like this:
public Square() {
addMouseListener(this);
this.setBounds(x, y, 45, 45);
}
2) Set GridLayout to your JFrame.
frame.setLayout(new GridLayout(width, height));
3) Call empty constructor on your loop.
Square[][] squares = new Square[width][height];
for (int i = 0; i < width; i++) {
for (int j = 0; j < height; j++) {
squares[i][j] = new Square();
frame.add(squares[i][j]);
}
}
Related
I'm trying to make the buttons highlight (by changing color) when i am hovering above them with my mouse.
this is my code;
import javax.swing.JOptionPane;
final int BUTTON_WIDTH = 100;
final int BUTTON_HEIGHT = 50;
int buttonX = 0;
int COLOR = 150;
void setup() {
size(800, 400);
}
void draw() {
for (int i=0; i<=8; i++) {
drawButtons();
buttonX = buttonX+BUTTON_WIDTH;
}
}
void drawButtons() {
strokeWeight(2);
fill(0, COLOR, COLOR);
rect(buttonX, 0, BUTTON_WIDTH, BUTTON_HEIGHT);
}
I thought it would work if i add this ;
if(mouseX<BUTTON_WIDTH && mouseY<BUTTON_HEIGHT){
COLOR = 255;
}
It does not work though, i am trying to make the button highlight when the mouse is over it and the other buttons would remain unchanged.
Your condition in the draw (before drawing the buttons) is wrong. Here a correct version of your program :
final int BUTTON_WIDTH = 100;
final int BUTTON_HEIGHT = 50;
int buttonX = 0;
void setup() {
size(800, 400);
strokeWeight(2);
}
void draw() {
for (int i=0; i<=8; i++) {
// The condition was wrong before, now it checks if you are on a button
if(buttonX <= mouseX && mouseX < buttonX + BUTTON_WIDTH && mouseY<BUTTON_HEIGHT){
fill(0, 255, 255);
} else {
fill(0, 150, 150);
}
rect(buttonX, 0, BUTTON_WIDTH, BUTTON_HEIGHT);
buttonX += BUTTON_WIDTH;
}
buttonX = 0; // don't forget to put buttonX at 0 after the for loop, or the next buttons drawn are going to be invisible (on the right of the canva) and you won't see the color change.
}
Now it should be ok !
I have to draw n + 1 amount of circles horizontally and vertically in a GUI. Which I have successfully done as shown below. With this, a 2D array of strings will be printed between them, centralised.
How it currently stands
Now I want to draw numbers in a "square" of the dots.
How I want the final result
for (int i = 0; i < width; i++) {
for (int j = 0; j < width; j++) {
canvas.drawCircle( (j + 1) * 125, (i + 1) * 125, 15, Color.white);
}
}
for (int r = 0; r < size; r++) {
for (int c = 0; c < size; c++) {
canvas.drawString(events[r][c], (r + 1) * 150, (c + 1) * 150, Color.green );
}
}
Width in this case is 4, so basically (n-1) dots/circles in the picture.
Size is 3 which is just the length of the 2d array, as there are 4 circles in this case there will be 3 numbers between each one
Events is the 2D array with the data containing the numbers
The drawCircle method's signature is
(x, y, radius, color)
The drawString method's signature is
(text, x, y color)
I believe part of the problem is also the drawing of circles. Basically I think it has to do with the rubbish formula I have for determining the x, y coords for both circles and the text. Any help is appreciated, thank you.
Provided is some code which I believe does what you want. Some of the constants may be tweaked to fit your final requirements. I used a 2D array of numbers and converted to strings during paint. It also allows for the following:
Changing the ball diameter or number of balls will still provide a
correct graph (although it will change its location within the
panel).
The String numbers track along with the size of the balls
Anti-aliasing was turned on to smooth out the graphics.
FontMetrics was used to fine tune the location of the numbers.
One final note: Because this is not resource intensive, the coordinates are calculated within the paintComponent method. A more optimum solution would be to adopt the Flyweight design pattern and precalculate as much as possible before entering paintComponent.
import java.awt.*;
import javax.swing.*;
public class SwingMain extends JPanel {
final static int WIDTH = 700;
final static int HEIGHT = 700;
final static int SEPARATION = 100;
final static int DIAMETER = 25;
final static int NBALLS = 4;
final static int XSTART = WIDTH / (NBALLS + 2);
final static int YSTART = HEIGHT / (NBALLS + 2);
JFrame frame = new JFrame();
int[][] numbers = new int[NBALLS - 1][NBALLS - 1];
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> new SwingMain().start());
}
public void start() {
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setPreferredSize(new Dimension(WIDTH, HEIGHT));
frame.add(this);
setBackground(Color.RED);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
// populate numbers in 2D array.
for (int r = 0; r < NBALLS - 1; r++) {
for (int c = 0; c < NBALLS - 1; c++) {
numbers[r][c] = r * (NBALLS - 1) + c + 1;
}
}
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
// Allow smoothing of the graphics.
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setColor(Color.white);
// Iterating by the number of balls is more consistent than
// by x and y due to round of errors.
int y = YSTART;
for (int r = 0; r < NBALLS; r++) {
int x = XSTART;
for (int c = 0; c < NBALLS; c++) {
g2d.fillOval(x, y, DIAMETER, DIAMETER);
x += SEPARATION;
}
y += SEPARATION;
}
// This is the same as above except that the start begins
// halfway between the first row and column
// have the size of the font track with the diameter of the balls
g2d.setFont(new Font("ARIAL", Font.BOLD, DIAMETER));
FontMetrics fm = g2d.getFontMetrics();
y = YSTART + SEPARATION / 2;
for (int r = 0; r < NBALLS - 1; r++) {
int x = XSTART + SEPARATION / 2;
for (int c = 0; c < NBALLS - 1; c++) {
String number = Integer.toString(numbers[r][c]);
// Do some final position adjustment with the font metrics to
// center the number
int strWidth = fm.stringWidth(number);
int strHeight = fm.getAscent();
g2d.drawString(number,
x - strWidth / 2 + DIAMETER / 2,
y + strHeight);
x += SEPARATION;
}
y += SEPARATION;
}
}
}
You could store the coordinates of the circles in a 2D array as well, and use that to find the location of the strings. One thing to note is that the drawCircle method for some reason wont draw the circle with the given center (the coordinates you give will be the top left corner actually).
Point[][] circleCoords = new Point[width][width]; //suppose Point class has x and y coords
for (int i = 0; i < width; i++) {
for (int j = 0; j < width; j++) {
//the -15 actually centers the circle to the coordianates
circleCoords[i][j] = new Point((j + 1) * 125 - 15, (i + 1) * 125 -15);
canvas.drawCircle(circleCoords[i][j].x , circleCoords[i][j].y, 15, Color.white);
}
}
for (int r = 0; r < width-1; r++) {
for (int c = 0; c < width-1; c++) {
//calculate coords from circleCoords array: halfway between them
int xCoord = (circleCoords[r][c].x + circleCoords[r+1][c].x)/2;
int yCoord = (circleCoords[r][c].y + circleCoords[r][c+1].y)/2;
//wont be out of bounds, becouse this runs to width-1
canvas.drawString(events[r][c], xCoord, yCoord, Color.green );
}
}
This still wont actually be perfectly centered, because the drawString will use the coordinates for top left point as well, and not centerpoint. Maybe I miscalculated something, but this should give you the idea: instead of calculating the coordinates independently, re-use the circle coordinates.
Consider a class that represents a single square. The value is represented by a JLabel which is placed at the bottom using straight-forward layout manager (BorderLayout in this example). You can change or manipulate the layout manager to change to position of the JLabel.
class Square extends JPanel{
private static int WIDTH = 100, HEIGHT = 100, DAIMETER = 30, GAP = 5;
private final JLabel label;
Square(int value) {
setPreferredSize(new Dimension(WIDTH, HEIGHT));
setLayout(new BorderLayout());
setBackground(Color.red);
setOpaque(true);
label = new JLabel(String.valueOf(value), JLabel.RIGHT);
label.setForeground (Color.white);
Border margin = new EmptyBorder(GAP,GAP,GAP,2*GAP); //top left bottom right
label.setBorder(margin);
add(label, BorderLayout.PAGE_END);
}
#Override
public void paintComponent(Graphics g){
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setColor(Color.white);
g2d.fillOval(WIDTH/2 - DAIMETER/2, HEIGHT/2-DAIMETER/2, DAIMETER, DAIMETER);
}
}
Now add 16 instances of this Square to panel representing a board. Its layout manager is set to GridLayout:
class Board extends JPanel{
private static int ROWS = 4, COLS = 4;
Board(){
setLayout(new GridLayout(ROWS, COLS));
for(int index = 0; index < ROWS * COLS; index ++){
add(new Square(index)); //index is used as value for demonstration purposes
}
}
}
Putting it all together (this is a one-file mcve. Copy paste the entire code into SwingMain.java and run )
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.border.Border;
import javax.swing.border.EmptyBorder;
public class SwingMain {
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocation(400,250);
frame.add(new Board());
frame.pack();
frame.setResizable(false);
frame.setVisible(true);
}
}
class Board extends JPanel{
private static int ROWS = 4, COLS = 4;
Board(){
setLayout(new GridLayout(ROWS, COLS));
for(int index = 0; index < ROWS * COLS; index ++){
add(new Square(index)); //index is used as value for demonstration purposes
}
}
}
class Square extends JPanel{
private static int WIDTH = 100, HEIGHT = 100, DAIMETER = 30, GAP = 5;
private final JLabel label;
Square(int value) {
setPreferredSize(new Dimension(WIDTH, HEIGHT));
setLayout(new BorderLayout());
setBackground(Color.red);
setOpaque(true);
label = new JLabel(String.valueOf(value), JLabel.RIGHT);
label.setForeground (Color.white);
Border margin = new EmptyBorder(GAP,GAP,GAP,2*GAP); //top left bottom right
label.setBorder(margin);
add(label, BorderLayout.PAGE_END);
}
#Override
public void paintComponent(Graphics g){
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setColor(Color.white);
g2d.fillOval(WIDTH/2 - DAIMETER/2, HEIGHT/2-DAIMETER/2, DAIMETER, DAIMETER);
}
}
I have to create a simple 2D animation without using various primitives for drawing line, circle etc for the purpose. It has to be done by manipulating pixels and implementing one of the algorithms for drawing line, circle etc by coloring pixels.
I thought of using Turbo C for the purpose, but I use ubuntu. So I tried using dosbox to install and run turbo C but to no avail.
Now my only option is Java. Is it possible to manipulate pixels in Java? I couldn't find myself any good tutorials for the same. It would be great if a sample code for the same can be given.
The class java.awt.BufferedImage has a method setRGB(int x, int y, int rgb) which sets the color of an individual pixel. Additionally, you might want to look at java.awt.Color, especially its getRGB() method, which can convert Colors into integers that you can put into the int rgb parameter of setRGB.
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class DirectDrawDemo extends JPanel {
private BufferedImage canvas;
public DirectDrawDemo(int width, int height) {
canvas = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
fillCanvas(Color.BLUE);
drawRect(Color.RED, 0, 0, width/2, height/2);
}
public Dimension getPreferredSize() {
return new Dimension(canvas.getWidth(), canvas.getHeight());
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.drawImage(canvas, null, null);
}
public void fillCanvas(Color c) {
int color = c.getRGB();
for (int x = 0; x < canvas.getWidth(); x++) {
for (int y = 0; y < canvas.getHeight(); y++) {
canvas.setRGB(x, y, color);
}
}
repaint();
}
public void drawLine(Color c, int x1, int y1, int x2, int y2) {
// Implement line drawing
repaint();
}
public void drawRect(Color c, int x1, int y1, int width, int height) {
int color = c.getRGB();
// Implement rectangle drawing
for (int x = x1; x < x1 + width; x++) {
for (int y = y1; y < y1 + height; y++) {
canvas.setRGB(x, y, color);
}
}
repaint();
}
public void drawOval(Color c, int x1, int y1, int width, int height) {
// Implement oval drawing
repaint();
}
public static void main(String[] args) {
int width = 640;
int height = 480;
JFrame frame = new JFrame("Direct draw demo");
DirectDrawDemo panel = new DirectDrawDemo(width, height);
frame.add(panel);
frame.pack();
frame.setVisible(true);
frame.setResizable(false);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
alt text http://grab.by/grabs/39416148962d1da3de12bc0d95745341.png
Another bit of fun I had today where I used #Jave Cavas, Color, Graphics and #Swing JFrame to create a simply colouring pixels class all we are doing is creating square a JFrame 400×400 pixels (few extra pixels required for the frame it self) and then we extend the Canvas and colour the pixels symmetrically.
package gcclinux.co.uk;
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JFrame;
public class ColouringPixels extends Canvas {
private static final long serialVersionUID = 1L;
private static final int WIDTH = 407; // Additional pixels needed for the frame
private static final int HEIGHT = 427; // Additional pixels needed for the frame
#Override
public void paint(Graphics g) {
super.paint(g);
for (int r = 0; r <= 2; r++) {
for(int y = 0; y < HEIGHT; y++) {
for(int x = 0; x < WIDTH; x++) {
if (x >= 1 && x <= 100 && y >= 1 && y <=100){
g.setColor(Color.WHITE);
} else if (x >= 101 && x <= 200 && y >= 101 && y <=200){
g.setColor(Color.RED);
} else if (x >= 201 && x <= 300 && y >= 201 && y <=300){
g.setColor(Color.WHITE);
} else if (x >= 301 && x <= 399 && y >= 301 && y <=400){
g.setColor(Color.RED);
} else
{
g.setColor(Color.BLUE);
}
g.drawLine(x, y, x, y);
}
}
for(int x = 0; x < HEIGHT; x++) {
for(int y = 0; y < WIDTH; y++) {
if (x >= 1 && x <= 100 && y >= 1 && y <=100){
g.setColor(Color.RED);
} else if (x >= 101 && x <= 200 && y >= 101 && y <=200){
g.setColor(Color.WHITE);
} else if (x >= 201 && x <= 300 && y >= 201 && y <=300){
g.setColor(Color.RED);
} else if (x >= 301 && x <= 399 && y >= 301 && y <=400){
g.setColor(Color.WHITE);
} else
{
g.setColor(Color.BLUE);
}
g.drawLine(x, y, x, y);
}
}
}
try {
Thread.sleep(2000); // Sleep for 2 seconds
System.exit(0); // Closed the program
}catch(InterruptedException ex) {
Thread.currentThread().interrupt();
}
}
public static void main(String[] args) {
JFrame frame = new JFrame("ColouringPixels - Lesson 9");
frame.setSize(WIDTH, HEIGHT);
frame.setResizable(false);
frame.add(new ColouringPixels());
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}
You can accomplish this using java's builtin 2D Graphics package.
Ok so I am very new to Java Swing and a beginner in Java in general. My current problem is I have designed a "cityscape". I am working on a UFO flying around, but my randomly generated buildings continue to get regenerated. I am wondering if there is a way to save my instance of buildings to an ArrayList as I have attempted, and paint that selection from that list each time paint is called. I tried what I thought of and I believe it just crashed it when run, because it didn't even open a JFrame and instead produced errors upon errors. Here is what I have:
CityScape class (the main class):
import java.awt.*;
import javax.swing.*;
public class CityScape extends JPanel
{
Buildings a = new Buildings ();
UFO b = new UFO();
#Override
public void paint (Graphics g)
{
//RememberBuildings.buildingList.get(1).paint(g);
a.paint(g);
b.paint(g);
}
public void move()
{
b.move();
}
public static void main(String[] args) throws InterruptedException
{
JFrame frame = new JFrame("Frame");
CityScape jpe = new CityScape();
frame.add(jpe);
frame.setSize(800, 750);
frame.setBackground(Color.BLACK);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
System.out.println(frame.getContentPane().getSize());
while (true)
{
jpe.move(); //Updates the coordinates
jpe.repaint(); //Calls the paint method
Thread.sleep(10); //Pauses for a moment
}
}
}
Buildings class (the class that generates the buildings):
import java.awt.*;
public class Buildings
{
private int maxX = 784;
private int maxY = 712;
private int width = (int)(Math.random()*100+100);
private int height = (int)(Math.random()*350+100);
private int rows = Math.round((height)/25);
private int columns = Math.round(width/25);
public void addBuilding()
{
RememberBuildings.addBuilding();
}
public void paint(Graphics g)
{
Graphics2D g2d = (Graphics2D) g;
Color transYellow = new Color (255, 255, 0, 59);
g2d.setColor(Color.BLACK);
g2d.fillRect(0, 0, maxX, maxY);
g2d.setColor(Color.WHITE);
g2d.fillRect(5, 5, 25, 25);
int a = 0;
for (int i =10; i<634; i+=(a+10))//buildings
{
g2d.setColor(Color.GRAY);
g2d.drawRect(i, maxY-height, width, height);
g2d.fillRect(i, maxY-height, width, height);
rows = Math.round((height)/25);
columns = Math.round(width/25);
for (int j = 1; j<=columns; j++)//windows
{
for (int k = 1; k<=rows; k++)
{
g2d.setColor(Color.BLACK);
g2d.drawRect(i+5*j+20*(j-1), (maxY-height)+5*k+20*(k-1), 20, 20);
if (Math.random()<0.7)
{
g2d.setColor(Color.YELLOW);
g2d.fillRect(i+5*j+20*(j-1), (maxY-height)+5*k+20*(k-1), 20, 20);
}
else
{
g2d.setColor(Color.BLACK);
g2d.fillRect(i+5*j+20*(j-1), (maxY-height)+5*k+20*(k-1), 20, 20);
g2d.setColor(transYellow);
g2d.fillRect(i+5*j+20*(j-1), (maxY-height)+5*k+20*(k-1), 20, 20);
}
}
}
addBuilding();
a = width;
height = (int)(Math.random()*462+100);
width = (int)(Math.random()*100+100);
}
}
}
RememberBuildings class (the point of this is to add an instance to an ArrayList):
import java.util.*;
public class RememberBuildings
{
public static ArrayList<Buildings> buildingList = new ArrayList<Buildings>();
public static void addBuilding()
{
buildingList.add(new Buildings());
}
}
And finally my UFO class (creates the UFO flying by):
import java.awt.*;
import javax.swing.*;
public class UFO extends JPanel
{
private int x = 20; //x and y coordinates of the ball
private int y = 20;
private int xa = 1;
public void move() //Increase both the x and y coordinates
{
if (x + xa < 0) {
xa = 1;
}
if (x + xa > 784-75)
{
xa = -1;
}
x = x + xa;
}
public void paint(Graphics g)
{
super.paint(g); //Clears the panel, for a fresh start
Graphics2D g2d = (Graphics2D) g;
g2d.setColor(Color.LIGHT_GRAY);
g2d.fillOval(x,y,75,25); //Draw the ball at the desired point
}
}
Avoid overriding paint, use paintComponent instead. Always call the super paint method before you do any custom painting to ensure that the paint chain is maintained. See Painting in AWT and Swing and Performing Custom Painting for more details
Beware, Swing is not thread safe and it's unwise to update any component (or any variable that a component may rely on) from outside the context of the Event Dispatching Thread. A simple solution might be to use a Swing Timer instead of a while (true) loop and Thread.sleep. See How to use Swing Timers for more details.
You should also only create and modify UI components from within the context of the event dispatching thread, see Initial Threads for more details
If you have a problem with your code not working, you should consider providing a runnable example which demonstrates your problem. This is not a code dump, but an example of what you are doing which highlights the problem you are having. This will result in less confusion and better responses. Providing code which is not runnable and is missing classes makes it difficult to know why it's not working and how to fix it.
A few things here:
To address the paintComponent note and view an example, check out this other thread: Concerns about the function of JPanel: paintcomponent()
There seems to be a bit of a disconnect between the logic you've got going and the object-oriented programming logic that I think will help sort things out (for general info on OOP: https://en.wikipedia.org/wiki/Object-oriented_programming):
What You've Got:
The Structure you've got going is as follows:
CityScape :: here's where you've extended JPanel and setup the main function
UFO :: an object class that represents 1 UFO
Building :: a class that has methods for drawing randomized buildings and calling methods in RememberBuildings
RememberBuildings :: I think this is intended to track buildings that have been drawn
The issue here is that your Building class's paint method continually draws multiple newly randomized buildings instead of a set building that retains its structure.
My Suggestion:
There are plenty of solutions to this issue and different ways to implement each solution, but my recommendation is to remodel your Building class in an OOP fashion, meaning that it would represent 1 single building (truer to the name of the class). This would contain a constructor that initializes all of the randomized dimensions of that single building once and draws that single building on the jpanel. Then you would need to keep an array or list of some sort in the cityscape that contains buildings that are part of the cityscape, eliminating the need for a "RememberBuildings" class. so roughly:
CityScape extends JPanel:
variables:
Building[] buildings; //might be useful to use an arraylist/stack/queue instead of an array depending on implementation
UFO craft;
constructor:
setup new Building objects and add to list buildings
initialize craft to new UFO
paintComponent:
calls the paint methods for each building & the ufo craft
Building:
variables:
int x, y; // position of building
int height, width; // of this building
constructor:
initializes x, y // probably needs to be inputed from CityScape with this setup
calc height and width randomly // stored in this.height/width
paint:
paints single building based on it's variables
//side-note, you'll probably need getters for the x/y/width to build each building from CityScape
Everything else should be much the same.
Good Luck !
So, every time Buildings#paint is called, it regenerates all the builds, which is done randomly.
public void paint(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
Color transYellow = new Color(255, 255, 0, 59);
g2d.setColor(Color.BLACK);
g2d.fillRect(0, 0, maxX, maxY);
g2d.setColor(Color.WHITE);
g2d.fillRect(5, 5, 25, 25);
int a = 0;
for (int i = 10; i < 634; i += (a + 10))//buildings
{
g2d.setColor(Color.GRAY);
g2d.drawRect(i, maxY - height, width, height);
g2d.fillRect(i, maxY - height, width, height);
rows = Math.round((height) / 25);
columns = Math.round(width / 25);
for (int j = 1; j <= columns; j++)//windows
{
for (int k = 1; k <= rows; k++) {
g2d.setColor(Color.BLACK);
g2d.drawRect(i + 5 * j + 20 * (j - 1), (maxY - height) + 5 * k + 20 * (k - 1), 20, 20);
if (Math.random() < 0.7) {
g2d.setColor(Color.YELLOW);
g2d.fillRect(i + 5 * j + 20 * (j - 1), (maxY - height) + 5 * k + 20 * (k - 1), 20, 20);
} else {
g2d.setColor(Color.BLACK);
g2d.fillRect(i + 5 * j + 20 * (j - 1), (maxY - height) + 5 * k + 20 * (k - 1), 20, 20);
g2d.setColor(transYellow);
g2d.fillRect(i + 5 * j + 20 * (j - 1), (maxY - height) + 5 * k + 20 * (k - 1), 20, 20);
}
}
}
addBuilding();
a = width;
height = (int) (Math.random() * 462 + 100);
width = (int) (Math.random() * 100 + 100);
}
}
There's two ways you might be able to solve this, which you use will depend on what you want to achieve. You could render the buildings directly to a BufferedImage and simply paint that on each paint cycle or you could cache the information you need in order to re-create the buildings.
The BufferedImage approach is quicker, but can't be animated, so if you want to animate the buildings in some way (make the lights flicker), you will need to build up a series of information which allows you to simply repaint them.
I'm going for the second, as you've asked about painting assets from a ArrayList.
I started by translating your "paint" code into a single concept of a virtual building, which has also has information about it's own lights.
public class Building {
protected static final Color TRANS_YELLOW = new Color(255, 255, 0, 59);
private int x, y, width, height;
private List<Light> lights;
public Building(int x, int y, int width, int height) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
lights = new ArrayList<>(25);
int rows = Math.round((height) / 25);
int columns = Math.round(width / 25);
for (int j = 1; j <= columns; j++)//windows
{
for (int k = 1; k <= rows; k++) {
Color color = null;
if (Math.random() < 0.7) {
color = Color.YELLOW;
} else {
color = TRANS_YELLOW;
}
lights.add(new Light(x + 5 * j + 20 * (j - 1), y + 5 * k + 20 * (k - 1), color));
}
}
}
public void paint(Graphics2D g2d) {
g2d.setColor(Color.GRAY);
g2d.drawRect(x, y, width, height);
g2d.fillRect(x, y, width, height);
for (Light light : lights) {
light.paint(g2d);
}
}
public class Light {
private int x, y;
private Color color;
public Light(int x, int y, Color color) {
this.x = x;
this.y = y;
this.color = color;
}
public void paint(Graphics2D g2d) {
g2d.setColor(Color.BLACK);
g2d.fillRect(x, y, 20, 20);
g2d.setColor(color);
g2d.fillRect(x, y, 20, 20);
}
}
}
This allows you to generate the primary parameters for the Building and simple cache the results and when needed, simply paint it.
For example...
public class Buildings {
private int maxX = 784;
private int maxY = 712;
private List<Building> buildings;
public Buildings() {
buildings = new ArrayList<>(25);
for (int i = 10; i < 634; i += 10)//buildings
{
int width = (int) (Math.random() * 100 + 100);
int height = (int) (Math.random() * 350 + 100);
int x = i;
int y = maxY - height;
buildings.add(new Building(x, y, width, height));
}
}
public void paint(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
for (Building building : buildings) {
building.paint(g2d);
}
}
}
I also changed your UFO class so it no longer extends from JPanel, as it just doesn't need to and is probably the primary cause of confusion with your painting.
I then updated your paint method in your CityScape to use paintComponent instead...
public class CityScape extends JPanel {
Buildings a = new Buildings();
UFO b = new UFO();
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
a.paint(g);
b.paint(g);
}
As a runnable example...
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class CityScape extends JPanel {
Buildings a = new Buildings();
UFO b = new UFO();
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g); //To change body of generated methods, choose Tools | Templates.
a.paint(g);
b.paint(g);
}
public void move() {
b.move();
}
public static void main(String[] args) throws InterruptedException {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Frame");
CityScape jpe = new CityScape();
frame.add(jpe);
frame.setSize(800, 750);
frame.setBackground(Color.BLACK);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
System.out.println(frame.getContentPane().getSize());
Timer timer = new Timer(10, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
jpe.move(); //Updates the coordinates
jpe.repaint(); //Calls the paint method
}
});
timer.start();
}
});
}
public class Buildings {
private int maxX = 784;
private int maxY = 712;
private List<Building> buildings;
public Buildings() {
buildings = new ArrayList<>(25);
for (int i = 10; i < 634; i += 10)//buildings
{
int width = (int) (Math.random() * 100 + 100);
int height = (int) (Math.random() * 350 + 100);
int x = i;
int y = maxY - height;
buildings.add(new Building(x, y, width, height));
}
}
public void paint(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
for (Building building : buildings) {
building.paint(g2d);
}
}
}
public static class Building {
protected static final Color TRANS_YELLOW = new Color(255, 255, 0, 59);
private int x, y, width, height;
private List<Light> lights;
public Building(int x, int y, int width, int height) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
lights = new ArrayList<>(25);
int rows = Math.round((height) / 25);
int columns = Math.round(width / 25);
for (int j = 1; j <= columns; j++)//windows
{
for (int k = 1; k <= rows; k++) {
Color color = null;
if (Math.random() < 0.7) {
color = Color.YELLOW;
} else {
color = TRANS_YELLOW;
}
lights.add(new Light(x + 5 * j + 20 * (j - 1), y + 5 * k + 20 * (k - 1), color));
}
}
}
public void paint(Graphics2D g2d) {
g2d.setColor(Color.GRAY);
g2d.drawRect(x, y, width, height);
g2d.fillRect(x, y, width, height);
for (Light light : lights) {
light.paint(g2d);
}
}
public class Light {
private int x, y;
private Color color;
public Light(int x, int y, Color color) {
this.x = x;
this.y = y;
this.color = color;
}
public void paint(Graphics2D g2d) {
g2d.setColor(Color.BLACK);
g2d.fillRect(x, y, 20, 20);
g2d.setColor(color);
g2d.fillRect(x, y, 20, 20);
}
}
}
public class UFO {
private int x = 20; //x and y coordinates of the ball
private int y = 20;
private int xa = 1;
public void move() //Increase both the x and y coordinates
{
if (x + xa < 0) {
xa = 1;
}
if (x + xa > 784 - 75) {
xa = -1;
}
x = x + xa;
}
public void paint(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
g2d.setColor(Color.LIGHT_GRAY);
g2d.fillOval(x, y, 75, 25); //Draw the ball at the desired point
}
}
}
I've been tasked to make a java replica of Candy Crush Saga.
Right now im quite stuck on the GUI part.
I've decided that each candy will be represented by a JLabel holding the candy icon, and a mouselistener to control the functionality.
What happens is, after i finish running the screen shows, the mouse listeners respond but the image doesn't show, meaning i can press the labels get a response but cannot see the icons. I take this as the labels are on the panel but somehow not visible or the icon is not loaded correctly - although when checking the ImageIcon.toString it shows the path to the file.
Any ideas?
Here is the code:
public class Board extends JPanel {
Candy[][] board;
static final int TILE_SIZE = 55;
static final int TILES_MARGIN = 8;
public Board() {
setFocusable(true);
board = new Candy[13][13];
Candy c;
for (int i = 0; i < 13; i++)
for (int j = 0; j < 13; j++) {
if (i != 0 && i != 1 && j != 0 && j != 1 && i != 11 && i != 12 && j != 11 && j != 12) {
Random rand = new Random();
int randomNum = rand.nextInt((6 - 1) + 1) + 1;
c = new Basic(randomNum, this);
} else {
c = new Basic(0, this);
}
setAt(i, j, c);
}
repaint();
}
public void drawCandy(Graphics g2, Candy candy, int x, int y) {
Graphics2D g = ((Graphics2D) g2);
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_NORMALIZE);
int value = candy.getClr();
int xOffset = offsetCoors(x);
int yOffset = offsetCoors(y);
ImageIcon myImg = candy.switchIcon();
JLabel toAdd = new JLabel(myImg);
toAdd.setIcon(myImg);
toAdd.setLocation(xOffset,yOffset);
toAdd.setSize(TILE_SIZE,TILE_SIZE);
toAdd.addMouseListener(new ButtonPressed(x,y,candy));
toAdd.setVisible(true);
if (value != 0)
add(toAdd);
}
private static int offsetCoors(int arg) {
return (arg-2) * (TILES_MARGIN + TILE_SIZE) + TILES_MARGIN;
}
public void paint(Graphics g) {
super.paint(g);
removeAll();
requestFocusInWindow();
g.setColor(Color.black);
g.fillRect(0, 0, this.getSize().width, this.getSize().height);
for (int x = 2; x < 11; x++) {
for (int y = 2; y < 11; y++) {
drawCandy(g, board[x][y], x, y);
}
}
validate();
}
and the JFrame :
public Game() {
super("Candy Crush Game");
setDefaultLookAndFeelDecorated(true);
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
getContentPane().setLayout(new BorderLayout());
setSize(600, 600);
setResizable(false);
setLocationRelativeTo(null);
this.board = new Board();
this.score = 0;
this.moves = 20;
this.getContentPane().add(board, BorderLayout.CENTER);
setVisible(true);
board.checkSquare(2, 2, 10, 10);
}
I'm quite frustrated, any help will be great!
Instead of overriding paint() method use paintComponent() method for JPanel.
#Overrie
public void paintComponent(Graphics g) {
super.paintComponent(g);
//your custom painting here
}
Read more
Painting in AWT and Swing
paintComponent() vs paint() and JPanel vs Canvas in a paintbrush-type GUI
There might be some issue in reading image icon. My another post might help you.
ImageIcon does not work with me
Instead of creating new JLabel simply change it's icon.