i'm kind of new with Java graphics, i'm trying to create a simple crossroad GUI interface with 4 traffic lights on it, when using the following classes that I have created - I get a window with a large grey rectangle on it (I assume that since I didn't allocate a traffic lights in the center it has been filled with the default grey background), how do I control the size of the center of the JFrame?
This is what i'm looking to acheive:
This is what i'm getting:
This is the JFrame class.
import java.awt.*;
import javax.swing.*;
public class CrossroadInterface extends JFrame /*implements IAppInterface*/ {
private static final int WIDTH_OF_WINDOW = 400;
private static final int HEIGHT_OF_WINDOW = 400;
//Panels
TrafficLight tLightW, tLightC, tLightE, tLightS, tLightN;
//Other
public CrossroadInterface() {
super("My Crossroad");
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setSize(WIDTH_OF_WINDOW, HEIGHT_OF_WINDOW);
this.setVisible(true);
createInterface();
}
public void createInterface () {
tLightW = new TrafficLight();
tLightE = new TrafficLight();
tLightS = new TrafficLight();
tLightN = new TrafficLight();
this.add(tLightW, BorderLayout.WEST);
this.add(tLightN, BorderLayout.NORTH);
this.add(tLightE, BorderLayout.EAST);
this.add(tLightS, BorderLayout.SOUTH);
}
}
This is the Jpanel class.
import java.awt.Color;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.GridLayout;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class TrafficLight extends JPanel {
private final Color offRed = new Color(128, 0, 0);
private final Color offGreen = new Color(0, 96, 0);
private static final int CAR_DIAMETER = 50;
private static final int PERSON_HEIGHT = 100;
private static final int PERSON_WIDTH = 50;
private int status;
public TrafficLight() {
super();
this.setSize(CAR_DIAMETER, 120);
this.setBackground(Color.BLACK);
status = 0;
this.setVisible(true);
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(offRed);
g.fillOval(this.getX(), this.getY(), CAR_DIAMETER, CAR_DIAMETER);
g.setColor(offGreen);
g.fillOval(this.getX(), this.getY()+CAR_DIAMETER, CAR_DIAMETER, CAR_DIAMETER);
g.setColor(offRed);
g.fillRect(this.getX(), this.getY()+CAR_DIAMETER+PERSON_HEIGHT, PERSON_WIDTH, PERSON_HEIGHT);
g.setColor(offGreen);
g.fillRect(this.getX(), this.getY()+CAR_DIAMETER+2*PERSON_HEIGHT, PERSON_WIDTH, PERSON_HEIGHT);
//drawIlluminatedLights(g);
System.out.println(this.getX()+" "+this.getY());
}
}
EDIT:
Following Hovercraft Full Of Eels' advise, here are my new classes:
import java.awt.*;
import javax.swing.*;
public class CrossroadInterface extends JFrame /*implements IAppInterface*/ {
private static final int WIDTH_OF_WINDOW = 900;
private static final int HEIGHT_OF_WINDOW = 900;
//Panels
TrafficLight tLightW, tLightC, tLightE, tLightS, tLightN;
//Other
public CrossroadInterface() {
super("My Crossroad");
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setSize(WIDTH_OF_WINDOW, HEIGHT_OF_WINDOW);
setLayout(new GridLayout(3,3));
createInterface();
}
public void createInterface () {
tLightW = new TrafficLight();
tLightE = new TrafficLight();
tLightS = new TrafficLight();
tLightN = new TrafficLight();
this.add(new JPanel());
this.add(tLightW);
this.add(new JPanel());
this.add(tLightN);
this.add(new JPanel());
this.add(tLightE);
this.add(new JPanel());
this.add(tLightS);
this.setVisible(true);
}
}
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import javax.swing.JPanel;
public class TrafficLight extends JPanel {
private final Color offRed = new Color(128, 0, 0);
private final Color offGreen = new Color(0, 96, 0);
private static final int CAR_DIAMETER = 50;
private static final int PERSON_HEIGHT = 50;
private static final int PERSON_WIDTH = 50;
private int status;
public TrafficLight() {
super();
status = 0;
this.setPreferredSize(new Dimension(CAR_DIAMETER,2*CAR_DIAMETER+2*PERSON_HEIGHT));
this.setVisible(true);
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(offRed);
g.fillOval(100, 50, CAR_DIAMETER, CAR_DIAMETER);
g.setColor(offGreen);
g.fillOval(100, 50+CAR_DIAMETER, CAR_DIAMETER, CAR_DIAMETER);
g.setColor(offRed);
g.fillRect(100, 50+CAR_DIAMETER+PERSON_HEIGHT, PERSON_WIDTH, PERSON_HEIGHT);
g.setColor(offGreen);
g.fillRect(100, 50+CAR_DIAMETER+2*PERSON_HEIGHT, PERSON_WIDTH, PERSON_HEIGHT);
//drawIlluminatedLights(g);
}
}
Your problem is not that the center of the JFrame is too large, but rather it's because the size of your surrounding JPanels are too small. Understand that most Swing layout managers respect a components preferred size, and use this to set the size of the component. Your other problems include
using getX() and getY() to place your drawings. These values give the location of the JPanel within its container, but that won't help you place your drawing since when you draw within the JPanel the drawing's location is placed relative to the location of the pixel within the JPanel not its container, so using these methods will mess you up.
Calling the JFrame's setVisible(true) before adding all components. This risks not displaying all components.
Making your TrafficLight class extend JPanel. You're far better off using a single JPanel to do all the drawing and have your TrafficLight class not extend from any Swing component but rather be a logical class. Give it a public void draw(Graphics2D g2) method that you can call within the drawing JPanel's paintComponent method.
For example:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.Ellipse2D;
import java.util.ArrayList;
import java.util.EnumMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import javax.swing.*;
#SuppressWarnings("serial")
public class CrossRoads2 extends JPanel {
private static final int PREF_W = 400;
private static final int PREF_H = PREF_W;
private static final int TIMER_DELAY = 100;
List<TrafficLight2> lights = new ArrayList<>();
public CrossRoads2() {
// create a timer to randomly change traffic light state
// and start it
new Timer(TIMER_DELAY, new TimerListener()).start();
// create 4 TrafficLight2 objects and place them at 4
// compass locations, and add to lights ArrayList
int x = (PREF_W - TrafficLight2.getWidth()) / 2;
int y = 0;
lights.add(new TrafficLight2(x, y));
x = 0;
y = (PREF_H - TrafficLight2.getHeight()) / 2;
lights.add(new TrafficLight2(x, y));
x = (PREF_W - TrafficLight2.getWidth());
lights.add(new TrafficLight2(x, y));
x = (PREF_W - TrafficLight2.getWidth()) / 2;
y = (PREF_H - TrafficLight2.getHeight());
lights.add(new TrafficLight2(x, y));
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
// cast g into a Graphics2 object
Graphics2D g2 = (Graphics2D) g;
// for smooth rendering
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
// iterate through the ArrayList, calling the draw method on each light
for (TrafficLight2 light : lights) {
light.draw(g2);
}
}
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
// give our JPanel a decent size
return new Dimension(PREF_W, PREF_H);
}
// ActionListener that randomly changes the LightState of each traffic light
private class TimerListener implements ActionListener {
private Random random = new Random();
#Override
public void actionPerformed(ActionEvent e) {
for (TrafficLight2 light : lights) {
// random number 0 to 2
int randomIndex = random.nextInt(LightState.values().length);
// get one of the LightStates using the index above
LightState lightState = LightState.values()[randomIndex];
// set our light to this state
light.setLightState(lightState);
}
repaint();
}
}
private static void createAndShowGui() {
CrossRoads2 mainPanel = new CrossRoads2();
JFrame frame = new JFrame("Cross Roads");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> createAndShowGui());
}
}
class TrafficLight2 {
private static final int ELLIPSE_W = 40;
private static final int GAP = 4;
private int x;
private int y;
private LightState lightState = LightState.RED; // what color is bright
// map to hold our 3 ellipses, each one corresponding to a LightState
private Map<LightState, Shape> lightMap = new EnumMap<>(LightState.class);
public TrafficLight2(int x, int y) {
// create 3 ellipses, one each for RED, YELLOW, GREEN
// place each one below the previous
// associate each one with one of our RED, YELLOW, or GREEN LightStates
// putting the Ellipse into the map with the light state as key
this.x = x;
this.y = y;
int tempX = x + GAP;
int tempY = y + GAP;
lightMap.put(LightState.RED, new Ellipse2D.Double(tempX, tempY, ELLIPSE_W, ELLIPSE_W));
tempY += ELLIPSE_W + GAP;
lightMap.put(LightState.YELLOW, new Ellipse2D.Double(tempX, tempY, ELLIPSE_W, ELLIPSE_W));
tempY += ELLIPSE_W + GAP;
lightMap.put(LightState.GREEN, new Ellipse2D.Double(tempX, tempY, ELLIPSE_W, ELLIPSE_W));
}
// called by JPanel's paintComponent
public void draw(Graphics2D g2) {
// iterate through the 3 LightStates
for (LightState ltSt : LightState.values()) {
// if the ltSt in the for loop is this traffic light's LightState
// then the display color should be bright
Color c = ltSt == lightState ? ltSt.getColor() :
// other wise the display color should be very dark
ltSt.getColor().darker().darker().darker();
g2.setColor(c);
g2.fill(lightMap.get(ltSt)); // fill the oval with color
g2.setColor(Color.BLACK);
g2.draw(lightMap.get(ltSt)); // draw a black border
}
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public LightState getLightState() {
return lightState;
}
public void setLightState(LightState lightState) {
this.lightState = lightState;
}
// static method for the width of our traffic lights
public static int getWidth() {
return 2 * GAP + ELLIPSE_W;
}
// static method for the height of our traffic lights
public static int getHeight() {
return 4 * GAP + 3 * ELLIPSE_W;
}
}
// enum that encapsulates the 3 possible states of the traffic light
enum LightState {
RED("Red", Color.RED), YELLOW("Yellow", Color.YELLOW), GREEN("Green", Color.GREEN);
private LightState(String text, Color color) {
this.text = text;
this.color = color;
}
private String text;
private Color color;
public String getText() {
return text;
}
public Color getColor() {
return color;
}
}
Related
I'm currently working on a 2D game in Java for school. We have to use an Abstract Factory design pattern. For the 2D implementation I use a factory as follows:
public class Java2DFact extends AbstractFactory {
public Display display;
private Graphics g;
public Java2DFact() {
display = new Display(2000, 1200);
}
#Override
public PlayerShip getPlayership()
{
return new Java2DPlayership(display.panel);
}
In my display class I create a JFrame and Jpanel
public class Display {
public JFrame frame;
public JPanel panel;
public int width, height;
public Display(int width, int height) {
this.width = width;
this.height = height;
frame = new JFrame();
frame.setTitle("SpaceInvaders");
frame.setSize(1200,800);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setResizable(false);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
panel = new JPanel(){
#Override
protected void paintComponent(Graphics g){
super.paintComponent(g);
}
};
panel.setFocusable(true);
frame.add(panel);
}
}
Now from my main gameloop I call the visualize method inside the Java2DPLayership class to visualize my Playership
public class Java2DPlayership extends PlayerShip {
private JPanel panel;
private Graphics2D g2d;
private Image image;
private BufferStrategy bs;
public Java2DPlayership(JPanel panel) {
super();
this.panel = panel;
}
public void visualize() {
try {
image = ImageIO.read(new File("src/Bee.gif"));
Graphics2D g = (Graphics2D) bs.getDrawGraphics();
//g.setColor(new Color(0, 0, 0));
//g.fillRect(10, 10, 12, 8);
g.drawImage(image, (int) super.getMovementComponent().x, (int) super.getMovementComponent().y, null);
Toolkit.getDefaultToolkit().sync();
g.dispose();
panel.repaint();
} catch(Exception e){
System.out.println(e.toString());
}
}
}
My goal is to pass around the JPanel to every entity and let it draw its contents onto the panel before showing it. However I can't seem to figure out how to do this. When using this approach by changing the Graphics of the panel I get a lot of flickering.
Here is a fully functional, albeit simple example, I wrote some time ago. It just has a bunch of balls bouncing off the sides of the panel. Notice that the render method of the Ball class accepts the graphics context from paintComponent. If I had more classes that needed to be rendered, I could have created a Renderable interface and have each class implement it. Then I could have a list of Renderable objects and just go thru them and call the method. But as I also said, that would need to happen quickly to avoid tying up the EDT.
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
public class Bounce extends JPanel {
private static final int COLOR_BOUND = 256;
private final static double INC = 1;
private final static int DIAMETER = 40;
private final static int NBALLS = 20;
private final static int DELAY = 5;
private final static int PANEL_WIDTH = 800;
private final static int PANEL_HEIGHT = 600;
private final static int LEFT_EDGE = 0;
private final static int TOP_EDGE = 0;
private JFrame frame;
private double rightEdge;
private double bottomEdge;
private List<Ball> balls = new ArrayList<>();
private Random rand = new Random();
private List<Long> times = new ArrayList<>();
private int width;
private int height;
public Bounce(int width, int height) {
this.width = width;
this.height = height;
frame = new JFrame("Bounce");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(this);
addComponentListener(new MyComponentListener());
frame.pack();
frame.setLocationRelativeTo(null);
rightEdge = width - DIAMETER;
bottomEdge = height - DIAMETER;
for (int j = 0; j < NBALLS; j++) {
int r = rand.nextInt(COLOR_BOUND);
int g = rand.nextInt(COLOR_BOUND);
int b = rand.nextInt(COLOR_BOUND);
Ball bb = new Ball(new Color(r, g, b), DIAMETER);
balls.add(bb);
}
frame.setVisible(true);
}
public Dimension getPreferredSize() {
return new Dimension(width, height);
}
public static void main(String[] args) {
new Bounce(PANEL_WIDTH, PANEL_HEIGHT).start();
}
public void start() {
/**
* Note: Using sleep gives a better response time than
* either the Swing timer or the utility timer. For a DELAY
* of 5 msecs between updates, the sleep "wakes up" every 5
* to 6 msecs while the other two options are about every
* 15 to 16 msecs. Not certain why this is happening though
* since the other timers are run on threads.
*
*/
Timer timer = new Timer(0,(ae)-> {repaint();
for (Ball b : balls) {
b.updateDirection();
}} );
timer.setDelay(5); // 5 ms.
timer.start();
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
for (Ball ball : balls) {
ball.render(g2d);
}
}
class MyComponentListener extends ComponentAdapter {
public void componentResized(ComponentEvent ce) {
Component comp = ce.getComponent();
rightEdge = comp.getWidth() - DIAMETER;
bottomEdge = comp.getHeight() - DIAMETER;
for (Ball b : balls) {
b.init();
}
}
}
class Ball {
private Color color;
public double x;
private double y;
private double yy;
private int ydir = 1;
private int xdir = 1;
private double slope;
private int diameter;
public Ball(Color color, int diameter) {
this.color = color;
this.diameter = diameter;
init();
}
public void init() {
// Local constants not uses outside of method
// Provides default slope and direction for ball
slope = Math.random() * .25 + .50;
x = (int) (rightEdge * Math.random());
yy = (int) (bottomEdge * Math.random()) + diameter;
xdir = Math.random() > .5 ? -1
: 1;
ydir = Math.random() > .5 ? -1
: 1;
y = yy;
}
public void render(Graphics2D g2d) {
g2d.setColor(color);
g2d.fillOval((int) x, (int) y, diameter, diameter);
}
public void updateDirection() {
x += (xdir * INC);
yy += (ydir * INC);
y = yy * slope;
if (x < LEFT_EDGE || x > rightEdge) {
xdir = -xdir;
}
if (y < TOP_EDGE || y > bottomEdge) {
ydir = -ydir;
}
}
}
}
I am attempting to use jsliders to allow a user to pinpoint the origin of a circle to be drawn on a canvas. I am using a button to show and hide the circle. I am using paint on an inner jpanel so that paint will not write over components. However, the coordinates inside the jpanel are different than the coordinates for the entire frame. So, it is very difficult for me to get the coordinates of the jslider and then translate it to the jpanel to draw the circle. Is there an easy way to figure this out without a ton of guess and check? I am also using the custom layout miglayout. I have included the code for my GUI class as well as my custom JPanel I made so I could mess with the paint method.
public class CircleGUI extends JFrame implements ActionListener {
private MigLayout layout = new MigLayout();
private CustomPanel innerpanel;
private JSlider x,y;
private JColorChooser colorpick;
private JButton state;
private boolean bstate;
CircleGUI() {
initialize();
}
private void initialize() {
Border blackline = BorderFactory.createLineBorder(Color.black);
bstate = false;
x = new JSlider(JSlider.HORIZONTAL,650,325);
x.setPaintTicks(true);
x.setPaintLabels(true);
x.setPreferredSize(new Dimension(650,0));
y = new JSlider(JSlider.HORIZONTAL,650,325);
y.setPaintTicks(true);
y.setPaintLabels(true);
y.setInverted(true);
y.setOrientation(JSlider.VERTICAL);
y.setPreferredSize(new Dimension (0,600));
colorpick = new JColorChooser();
state = new JButton("Show");
state.addActionListener(e -> {
if(!bstate) {
int positionx = x.getValue() - 80;
int positiony = y.getValue();
Color c = colorpick.getColor();
innerpanel.setColor(c);
innerpanel.setX(positionx);
innerpanel.setY(positiony);
innerpanel.repaint();
state.setText("Hide");
bstate = true;
} else {
Color transparent = new Color(0,0,0,0);
innerpanel.setColor(transparent);
innerpanel.repaint();
state.setText("Show");
bstate = false;
}
});
JPanel outerpanel = new JPanel(layout);
innerpanel = new CustomPanel();
innerpanel.setPreferredSize(new Dimension(600,600));
innerpanel.setBorder(blackline);
outerpanel.add(x,"wrap");
outerpanel.add(y,"split 2");
outerpanel.add(innerpanel);
outerpanel.add(state,"wrap");
outerpanel.add(colorpick);
this.setSize(1000, 1000);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.add(outerpanel);
}
#Override
public void actionPerformed(ActionEvent e) {
}
}
public class CustomPanel extends JPanel implements ActionListener {
private Color c;
private int x;
private int y;
public CustomPanel() {
c = null;
}
#Override
public void actionPerformed (ActionEvent e) {
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setPaint(c);
g2.fill(new Ellipse2D.Double(x, y, 100, 100));
}
public void setColor(Color c) {
this.c = c;
}
public void setX(int x) {
this.x = x;
}
public void setY(int y) {
this.y = y;
}
}
Your problem is you are trying to make a one-to-one mapping between the value of the JSlider and the coordinate in your CustomPanel. You should use the JSlider value as a percentage, i.e. minimum value zero and maximum value 100. If you want the circle to appear in the middle of the CustomPanel so you place both JSliders in their mid-points, i.e. both at 50%. Then you calculate 50% of the corresponding dimension to get the coordinate. If the width of CustomPanel is 600, then 50% of 600 is 300 so positionx needs to be 300.
The only thing I changed in your code is the calculation of positionx and positiony.
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.Ellipse2D;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JColorChooser;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.border.Border;
import net.miginfocom.swing.MigLayout;
public class CircleGUI extends JFrame implements ActionListener {
private MigLayout layout = new MigLayout();
private CustomPanel innerpanel;
private JSlider x,y;
private JColorChooser colorpick;
private JButton state;
private boolean bstate;
CircleGUI() {
initialize();
}
private void initialize() {
Border blackline = BorderFactory.createLineBorder(Color.black);
bstate = false;
// x = new JSlider(JSlider.HORIZONTAL, 650, 325);
x = new JSlider(0, 100, 10);
x.setPaintTicks(true);
x.setPaintLabels(true);
x.setPreferredSize(new Dimension(650, 0));
// y = new JSlider(JSlider.HORIZONTAL, 650, 325);
y = new JSlider(0, 100, 10);
y.setPaintTicks(true);
y.setPaintLabels(true);
y.setInverted(true);
y.setOrientation(JSlider.VERTICAL);
y.setPreferredSize(new Dimension(0, 600));
colorpick = new JColorChooser();
state = new JButton("Show");
state.addActionListener(e -> {
if (!bstate) {
int positionx = Math.round(x.getValue() / 100.0f * innerpanel.getSize().width) - 50;
int positiony = Math.round(y.getValue() / 100.0f * innerpanel.getSize().height) - 50;
Color c = colorpick.getColor();
innerpanel.setColor(c);
innerpanel.setX(positionx);
innerpanel.setY(positiony);
innerpanel.repaint();
state.setText("Hide");
bstate = true;
}
else {
Color transparent = new Color(0, 0, 0, 0);
innerpanel.setColor(transparent);
innerpanel.repaint();
state.setText("Show");
bstate = false;
}
});
JPanel outerpanel = new JPanel(layout);
innerpanel = new CustomPanel();
innerpanel.setPreferredSize(new Dimension(600, 600));
innerpanel.setBorder(blackline);
outerpanel.add(x, "wrap");
outerpanel.add(y, "split 2");
outerpanel.add(innerpanel);
outerpanel.add(state, "wrap");
outerpanel.add(colorpick);
this.setSize(1000, 1000);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.add(outerpanel);
}
#Override
public void actionPerformed(ActionEvent e) {
}
public static void main(String[] args) {
EventQueue.invokeLater(() -> {
CircleGUI cg = new CircleGUI();
cg.setVisible(true);
});
}
}
class CustomPanel extends JPanel implements ActionListener {
private Color c;
private int x;
private int y;
public CustomPanel() {
c = null;
}
#Override
public void actionPerformed(ActionEvent e) {
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setPaint(c);
g2.fill(new Ellipse2D.Double(x, y, 100, 100));
}
public void setColor(Color c) {
this.c = c;
}
public void setX(int x) {
this.x = x;
}
public void setY(int y) {
this.y = y;
}
}
I am developing the peg game in java, and I have been stuck on getting a hole to change color when it is clicked on to indicate that it has been selected. I currently have 3 classes, all of which extend JPanel:
Display.java: creates the JFrame and renders the gameboard to the JFrame.
GameBoard.java: Holds the logic for rendering the gameboard.
Hole.java: Renders a hole and holds the logic for the MouseListener. When one clicks on a hole, it should turn blue to indicate that it is selected.
I am able to render the holes onto the gameboard, however, the MouseListener does not work; When I click on a hole, it does not turn blue.
Below are the three classes:
Display.java
import javax.swing.JFrame;
import javax.swing.JPanel;
import java.util.logging.*;
public class Display extends JPanel {
private static final long serialVersionUID = 1L;
private static String staticClassName = Display.class.getName();
private static Logger logger = Logger.getLogger(staticClassName);
private final static Logger LOGGER = Logger.getLogger(staticClassName);
private static String title;
private static int width;
private static int height;
private static JFrame frame;
private static JPanel mainPanel;
private static GameBoard gameBoard;
public Display(String title, int width, int height){
logger.info(staticClassName +".Constructor: Constructing Display");
Display.title = title;
Display.width = width;
Display.height = height;
logger.info(staticClassName +".Constructor: Display constructed.");
initDisplay();
}
private static void initDisplay() {
logger.info(staticClassName +".initDisplay(): Entering method");
try {
logger.info(staticClassName +".initDisplay(): About to create JFrame");
frame = new JFrame(title);
frame.setLocationByPlatform(true);
frame.setVisible(true);
frame.setSize(width, height);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
mainPanel = new JPanel();
logger.info(staticClassName +".initDisplay(): About to create GameBoard object");
gameBoard = new GameBoard();
mainPanel.add(gameBoard);
frame.getContentPane().add(mainPanel);
frame.pack();
} catch (Exception e) {
logger.info(staticClassName +".initDisplay(): Failed to render display: " + e);
}
logger.info(staticClassName +".initDisplay(): Exiting Method");
}
public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
try {
LOGGER.setLevel(Level.INFO);
Display d = new Display("Peg Game", 800, 800);
} catch (Exception e) {
}
}
});
}
}
GameBoard.java
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.geom.Path2D;
import java.util.logging.Logger;
import javax.swing.JPanel;
public class GameBoard extends JPanel {
private static final long serialVersionUID = 1L;
private static String staticClassName = GameBoard.class.getName();
private static Logger logger = Logger.getLogger(staticClassName);
private static final int RGB1 = 223;
private static final int RGB2 = 191;
private static final int RGB3 = 159;
private static final int WIDTH = 800;
private static final int HEIGHT = WIDTH;
private Path2D path = new Path2D.Double();
private static Color boardColor = new Color(RGB1, RGB2, RGB3);
private int [] adjacentHoles1;
public GameBoard() {
logger.info(staticClassName +".Constructor: Constructing GameBoard");
double firstX = (WIDTH / 2.0) * (1 - 1 / Math.sqrt(3));
double firstY = 3.0 * HEIGHT / 4.0;
path.moveTo(firstX, firstY);
path.lineTo(WIDTH - firstX, firstY);
path.lineTo(WIDTH / 2.0, HEIGHT / 4.0);
path.closePath();
logger.info(staticClassName +".Constructor: GameBoard constructed");
}
#Override
protected void paintComponent(Graphics g) {
logger.info(staticClassName +".paintComponent(): Entering method");
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2.setPaint(boardColor);
g2.fill(path);
int xPos = 390;
int yPos = 310;
// I was rendering up to 15 holes, but took them out for purpose of
// shortening my example
for (int i = 0; i < 15; i ++ ) {
if (i == 0) {
// Declare the hole here
Hole hole = new Hole(i, xPos, yPos, adjacentHoles1);
// Render it here
hole.paintComponent(g2);
yPos += 50;
}
}
logger.info(staticClassName +".paintComponent(): Exiting method");
}
#Override
public Dimension getPreferredSize() {
logger.info(staticClassName +".getPreferredSize(): Entering method");
if (isPreferredSizeSet()) {
logger.info(staticClassName +".getPreferredSize(): isPreferredSizeSet() == true. Exiting method");
return super.getPreferredSize();
}
logger.info(staticClassName +".getPreferredSize(): isPreferredSizeSet() == false. Exiting method");
return new Dimension(WIDTH, HEIGHT);
}
}
Hole.java
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.util.logging.Logger;
import javax.swing.JPanel;
public class Hole extends JPanel {
private static final long serialVersionUID = 1235245435988434L;
private static String staticClassName = Hole.class.getName();
private static Logger logger = Logger.getLogger(staticClassName);
private int holeNumber;
private int xPos, yPos;
private final int HOLEWIDTH = 20;
private final int HOLEHEIGHT = HOLEWIDTH;
private static final int RGB1 = 0;
private static final int RGB2 = 0;
private static final int RGB3 = 0;
private static Color defaultCircleColor = new Color(RGB1, RGB2, RGB3);
private static Color selectedCircleColor = Color.BLUE;
private static Color circleColor = defaultCircleColor;
private int [] adjacentHoles;
public Hole (int holeNumber, int xPos, int yPos, int [] adjacentHoles) {
logger.info(staticClassName +".Constructor: Constructing Hole");
this.holeNumber = holeNumber;
this.xPos = xPos;
this.yPos = yPos;
this.adjacentHoles = adjacentHoles;
initComponents();
}
#Override
protected void paintComponent(Graphics g) {
logger.info(staticClassName +".paintComponent(): Entering method");
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2.setPaint(circleColor);
g2.fillOval(xPos,yPos,HOLEWIDTH,HOLEHEIGHT);
logger.info(staticClassName +".paintComponent(): Exiting method");
}
private void initComponents() {
addMouseListener(new java.awt.event.MouseAdapter() {
public void mouseClicked(java.awt.event.MouseEvent event) {
selectHole(event);
}});
}
public void selectHole(java.awt.event.MouseEvent event) {
logger.info(staticClassName +"------------------------------------------------");
logger.info(staticClassName +".selectHole(): Entering method");
if (circleColor == defaultCircleColor) {
circleColor = selectedCircleColor;
repaint();
}
else {
circleColor = defaultCircleColor;
repaint();
}
logger.info(staticClassName +".selectHole(): Exiting method");
logger.info(staticClassName +"------------------------------------------------");
}
#Override
public Dimension getPreferredSize() {
logger.info(staticClassName +".getPreferredSize(): Getting the preferred size of the circle.");
return new Dimension(800, 800);
}
}
As a side note, if you notice that I could be using better coding practices anywhere in my code, please let me know.
So, your basic problem is right here...
for (int i = 0; i < 15; i ++ ) {
if (i == 0) {
// Declare the hole here
Hole hole = new Hole(i, xPos, yPos, adjacentHoles1);
// Render it here
hole.paintComponent(g2);
yPos += 50;
}
}
Hole is component, you should never call any paint method of component, but, you component is also not "live", meaning that it can never receive ANY events.
What you should do instead, is attach the MouseListener to the GameBoard and test if the mouse was clicked at a place there is a hole.
The simplest solution is to make use of the "shapes" API and generate a list of "holes". This can then be simply iterated over to paint and check to see if the mouse was clicked within it.
I've not gone to a great deal of effort in determining the location of the holes, instead I've just laid them out in rows/cols, but you should get the basic idea of the intended solution
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
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.Path2D;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Display extends JPanel {
private static final long serialVersionUID = 1L;
private static String staticClassName = Display.class.getName();
private static Logger logger = Logger.getLogger(staticClassName);
private final static Logger LOGGER = Logger.getLogger(staticClassName);
private static String title;
private static int width;
private static int height;
private static JFrame frame;
private static JPanel mainPanel;
private static GameBoard gameBoard;
public Display(String title, int width, int height) {
logger.info(staticClassName + ".Constructor: Constructing Display");
Display.title = title;
Display.width = width;
Display.height = height;
logger.info(staticClassName + ".Constructor: Display constructed.");
initDisplay();
}
private static void initDisplay() {
frame = new JFrame(title);
frame.setLocationByPlatform(true);
frame.setVisible(true);
frame.setSize(width, height);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
mainPanel = new JPanel();
gameBoard = new GameBoard();
mainPanel.add(gameBoard);
frame.getContentPane().add(mainPanel);
frame.pack();
}
public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
try {
LOGGER.setLevel(Level.INFO);
Display d = new Display("Peg Game", 800, 800);
} catch (Exception e) {
}
}
});
}
public static class GameBoard extends JPanel {
private static final long serialVersionUID = 1L;
private static String staticClassName = GameBoard.class.getName();
private static Logger logger = Logger.getLogger(staticClassName);
private static final int RGB1 = 223;
private static final int RGB2 = 191;
private static final int RGB3 = 159;
private static final int WIDTH = 800;
private static final int HEIGHT = WIDTH;
private Path2D path = new Path2D.Double();
private static Color boardColor = new Color(RGB1, RGB2, RGB3);
private int[] adjacentHoles1;
private Shape[] holes;
private Shape selectedHole;
public GameBoard() {
logger.info(staticClassName + ".Constructor: Constructing GameBoard");
double firstX = (WIDTH / 2.0) * (1 - 1 / Math.sqrt(3));
double firstY = 3.0 * HEIGHT / 4.0;
path.moveTo(firstX, firstY);
path.lineTo(WIDTH - firstX, firstY);
path.lineTo(WIDTH / 2.0, HEIGHT / 4.0);
path.closePath();
logger.info(staticClassName + ".Constructor: GameBoard constructed");
holes = new Shape[15];
int yPos = 310;
int index = 0;
for (int row = 0; row < 3; row++) {
int xPos = 390;
for (int col = 0; col < 5; col++) {
holes[index] = new Ellipse2D.Double(xPos, yPos, 50, 50);
index++;
xPos += 50;
}
yPos += 50;
}
addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
selectedHole = null;
for (Shape hole : holes) {
if (hole.contains(e.getPoint())) {
selectedHole = hole;
break;
}
}
repaint();
}
});
}
#Override
protected void paintComponent(Graphics g) {
logger.info(staticClassName + ".paintComponent(): Entering method");
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2.setPaint(boardColor);
g2.fill(path);
g2.setColor(Color.BLACK);
for (Shape hole : holes) {
g2.fill(hole);
}
if (selectedHole != null) {
g2.setColor(Color.RED);
g2.fill(selectedHole);
}
logger.info(staticClassName + ".paintComponent(): Exiting method");
}
#Override
public Dimension getPreferredSize() {
logger.info(staticClassName + ".getPreferredSize(): Entering method");
if (isPreferredSizeSet()) {
logger.info(staticClassName + ".getPreferredSize(): isPreferredSizeSet() == true. Exiting method");
return super.getPreferredSize();
}
logger.info(staticClassName + ".getPreferredSize(): isPreferredSizeSet() == false. Exiting method");
return new Dimension(WIDTH, HEIGHT);
}
}
}
So, I have multiple objects of the class Square, which is the subclass of JButton. I have an instance of the class Board, which contains a few instances of Square. What I want to do is when I press one of the buttons (squares), draw a shape (a circle) on top of it. For doing that, I have a boolean variable in the Square class, namely isClicked, that basically decides what has to be drawn in the paintComponent method.
The problem is that buttons start to behave in a weird way when I have a few of them. Surprisingly, if there is only one of them, there is no problem at all. At first, I had thought the problem might be related to threads, however, I put the main code into invokeLater method and that did not help at all.
I saw a solution using BufferedImage, but I would like to see if there is any possibility to solve the problem doing it my way.
Sorry for possibly not perfect English.
Square class:
public class Square extends JButton implements ActionListener {
private int number;
private boolean isClicked;
public Square(int x) {
number = x;
isClicked = false;
}
#Override
protected void paintComponent(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
if (!isClicked) {
super.paintComponent(g);
} else {
System.out.println("EXECUTED for: " + number);
g2d.drawOval(this.getX(), this.getY(), 100, 100);
}
}
#Override
public void actionPerformed(ActionEvent e) {
isClicked = !isClicked;
System.out.println(isClicked + " " + number);
repaint();
}
}
Board class:
public class Board extends JPanel {
private static final int BOARD_WIDTH = (int) (TicTacToe.WIDTH * 0.7);
private static final int VERTICAL_LINE_LENGTH = (int) (TicTacToe.WIDTH * 0.5);
private static final int HORIZONTAL_LINE_LENGTH = (int) (TicTacToe.HEIGHT * 0.8);
private static final int STROKE_WIDTH = 5;
private Square[] squares;
public Board() {
}
public void addButtons() {
squares = new Square[9];
for (int i = 0; i < 3; i++) {
Square square = new Square(i);
square.setPreferredSize(new Dimension(30, 30));
square.addActionListener(square);
this.add(square);
squares[i] = square;
((GridLayout)this.getLayout()).setHgap(30);
((GridLayout)this.getLayout()).setVgap(30);
}
}
public Square[] getButtons() {
return squares;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setStroke(new BasicStroke(STROKE_WIDTH));
// Horiztontal lines
g2d.drawLine(0, TicTacToe.HEIGHT / 3,
BOARD_WIDTH, TicTacToe.HEIGHT / 3);
g2d.drawLine(0, 2 * TicTacToe.HEIGHT / 3,
BOARD_WIDTH, 2 * TicTacToe.HEIGHT / 3);
// Vertical lines
g2d.drawLine(BOARD_WIDTH / 3, 0, BOARD_WIDTH / 3,
TicTacToe.HEIGHT);
g2d.drawLine(2 * BOARD_WIDTH / 3, 0, 2 * BOARD_WIDTH / 3,
TicTacToe.HEIGHT);
}
}
Main method:
SwingUtilities.invokeLater(new Runnable() {
public void run() {
Board board = new Board();
board.setPreferredSize(new Dimension((int) (WIDTH * 0.7), HEIGHT));
board.setLayout(new GridLayout(3, 3));
board.addButtons();
GameOptions opt = new GameOptions();
opt.setPreferredSize(new Dimension((int) (WIDTH * 0.3), HEIGHT));
JFrame frame = new JFrame("Tic Tac Toe");
frame.setLayout(new FlowLayout());
frame.add(board);
frame.add(opt);
frame.pack();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
});
Your use of getX() and getY() on the button's drawing code is completely wrong and does not belong. These methods return the location of the button relative to its container, and so while this might work for a button located at the upper left, it will fail for anything else since you'll end up drawing somewhere far away from the button itself, and so many of your drawings will never show.
You'd be much better off not extending JButton but instead simply swapping ImageIcons that display what you want drawn on the JButton. This is much simpler and much more idiot-proof. You set the button's icon by calling .setIcon(myImageIcon) on it, passing in the icon of choice.
But if you absolutely wanted to draw on the button, you'd do so without using getX() or getY(). You'd also probably want to use a JToggleButton as the parent class, since you're toggling state. For example, my MCVE:
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.RenderingHints;
import java.awt.Stroke;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.image.BufferedImage;
import javax.swing.*;
#SuppressWarnings("serial")
public class DrawButtonPanel extends JPanel {
private static final int SIDE = 3;
private static final int GAP = 5;
private static final Color BG = Color.BLACK;
public DrawButtonPanel() {
setBorder(BorderFactory.createEmptyBorder(GAP, GAP, GAP, GAP));
setLayout(new GridLayout(SIDE, SIDE, GAP, GAP));
setBackground(BG);
for (int i = 0; i < SIDE * SIDE; i++) {
// add(new DrawButton1());
DrawButton2 drawButton2 = new DrawButton2(i);
AbstractButton button = drawButton2.getButton();
add(button);
}
}
private static void createAndShowGui() {
JFrame frame = new JFrame("Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(new DrawButtonPanel());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> createAndShowGui());
}
}
class DrawButton2 {
private static final int PREF_W = 200;
private static final int PREF_H = PREF_W;
private static final int GAP = 20;
private static final float STROKE_WIDTH = 15f;
private static final Stroke BASIC_STROKE = new BasicStroke(STROKE_WIDTH);
private static final Color COLOR = Color.RED;
private static final Color BG = Color.LIGHT_GRAY;
private AbstractButton button = new JToggleButton();
private int index;
public DrawButton2(int index) {
this.index = index;
button.setBorderPainted(false);
button.setBorder(null);
button.setIcon(createPlainIcon());
button.setSelectedIcon(createSelectedIcon());
button.addItemListener(new ItemListener() {
#Override
public void itemStateChanged(ItemEvent e) {
if (e.getStateChange() == ItemEvent.SELECTED) {
System.out.println("Index: " + index);
}
}
});
}
public int getIndex() {
return index;
}
private Icon createPlainIcon() {
BufferedImage img = new BufferedImage(PREF_W, PREF_H, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = img.createGraphics();
g2d.setColor(new Color(0, 0, 0, 0));
g2d.fillRect(0, 0, PREF_W, PREF_H);
g2d.dispose();
return new ImageIcon(img);
}
private Icon createSelectedIcon() {
BufferedImage img = new BufferedImage(PREF_W, PREF_H, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = img.createGraphics();
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setStroke(BASIC_STROKE);
g2d.setColor(BG);
g2d.fillRect(0, 0, PREF_W, PREF_H);
g2d.setColor(COLOR);
g2d.drawOval(GAP, GAP, PREF_W - 2 * GAP, PREF_H - 2 * GAP);
g2d.dispose();
return new ImageIcon(img);
}
public AbstractButton getButton() {
return button;
}
}
#SuppressWarnings("serial")
class DrawButton1 extends JToggleButton {
private static final int PREF_W = 200;
private static final int PREF_H = PREF_W;
private static final int GAP = 20;
private static final float STROKE_WIDTH = 15f;
private static final Stroke BASIC_STROKE = new BasicStroke(STROKE_WIDTH);
private static final Color COLOR = Color.RED;
private static final Color BG = Color.LIGHT_GRAY;
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(PREF_W, PREF_H);
}
#Override
protected void paintComponent(Graphics g) {
Graphics2D g2d = (Graphics2D) g.create();
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setStroke(BASIC_STROKE);
if (!isSelected()) {
super.paintComponent(g);
} else {
g2d.setColor(BG);
g2d.fillRect(0, 0, getWidth(), getHeight());
g2d.setColor(COLOR);
g2d.drawOval(GAP, GAP, getWidth() - 2 * GAP, getHeight() - 2 * GAP);
}
g2d.dispose(); // since we created a new one
}
}
Edited to show how to do this with JToggleButton and icons.
edit 2: I decided that it would be easier to understand if I just put the entire code up, so that you can test it.
edit: I realize that what I said was unclear, so I will explain this as best as I can. Basically, I am drawing rectangles on a Graphics page using the fillRect method. The problem is that when I change the size of one, they all change, as they are all being redrawn everytime a new one is drawn. To correct this, I added an array that stores all of the sizes which are input via the scrollwheel in another part of the problem. Anyways, I know that the problem is isolated to the loop that supposedly draws them all a certain size, so I added a loop that in theory should give me a temporary variable each time to use that redraws all of the rectangle's sizes starting at 0 each time the main loop is run. The problem is that this does not in fact redraw the rectangles to their individual sizes, and instead draws them to the current size. I have updated the code part as well.
I am having trouble with a project in Java. What it is supposed to do is change the size of each individual rectangle object by storing it in an array, and then recreating the rectangles based off the length from the array. I (at least I think) do this by creating a variable that should be equal to the SIZE that is changed in another part of the program, and then set that equal to the particular element in the array at i. Anyhow, when I do this, I change all of the lengths to whatever the current length is when I draw a rectangle. I know that the problem is by me using i in the size part, but what would I use? Thanks in advance for any help!
Here is the code:
public class Dots
{
public static void main(String[] args)
{
JFrame frame = new JFrame("Array Rectangles");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
DotsPanel dotsPanel = new DotsPanel();
frame.getContentPane().add(dotsPanel);
//buttons
JButton btnNewButton = new JButton("RED");
btnNewButton.setHorizontalAlignment(SwingConstants.LEFT);
btnNewButton.setVerticalAlignment(SwingConstants.BOTTOM);
btnNewButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
}
});
btnNewButton.setForeground(Color.RED);
dotsPanel.add(btnNewButton);
JButton btnNewButton_1 = new JButton("GREEN");
btnNewButton_1.setForeground(Color.GREEN);
btnNewButton_1.setVerticalAlignment(SwingConstants.BOTTOM);
dotsPanel.add(btnNewButton_1);
JButton btnNewButton_2 = new JButton("BLUE");
btnNewButton_2.setForeground(Color.BLUE);
dotsPanel.add(btnNewButton_2);
JButton btnNewButton_3 = new JButton("BLACK");
btnNewButton_3.setForeground(new Color(0, 0, 0));
dotsPanel.add(btnNewButton_3);
frame.pack();
frame.setVisible(true);
}
}
import java.util.ArrayList;
import java.util.Random;
import javax.swing.JPanel;
import java.awt.*;
import java.awt.event.*;
public class DotsPanel extends JPanel
{
// radius of each dot
private int SIZE = 25;
private int SIZEAccess;
private static final Random generator = new Random();
//used to count amount of dots
private ArrayList<Point> pointList;
int[] sizes = new int [10000];
//Sets up this std. sized panel to listen for mouse events.
public DotsPanel()
{
pointList = new ArrayList<Point>();
addMouseListener (new DotsListener());
addMouseMotionListener(new DotsListener());
addMouseWheelListener(new DotsListener());
setBackground(Color.white);
setPreferredSize(new Dimension(1024, 768));
}
//used to generate a random color
public static Color randomColor() {
return new Color(generator.nextInt(256), generator.nextInt(256), generator.nextInt(256));
}
// Draws all of the dots stored in the list.
public void paintComponent(Graphics page)
{
super.paintComponent(page);
//draws a centered dot of random color
int i = 0;
for (Point spot : pointList)
{
sizes[i] = SIZE;
//SIZEAccess = SIZE;
//sizes[i] = SIZEAccess;
//page.fillRect(spot.x-SIZE, spot.y-SIZE, SIZE*2, SIZE*2);
for (int temp = 0; temp <= i; temp++)
page.fillRect(spot.x-sizes[temp], spot.y-sizes[temp], sizes[temp]*2, sizes[temp]*2);
//page.fillRect(spot.x-SIZE, spot.y-SIZE, SIZE*2, SIZE*2);
//page.setColor(randomColor());
//page.setColor(c)
i++;
}
//displays the amount of rectangles drawn at the top left of screen
page.drawString("Count: " + pointList.size(), 5, 15);
page.drawString("To change the size of the squares, use mouse scroll wheel.", 350, 15);
page.drawString("Size: " + SIZE, 950, 15);
}
// Represents the listener for mouse events.
private class DotsListener implements MouseListener, MouseMotionListener, MouseWheelListener
{
// Adds the current point to the list of points and redraws
// the panel whenever the mouse button is pressed.
public void mousePressed(MouseEvent event)
{
pointList.add(event.getPoint());
repaint();
}
// Provide empty definitions for unused event methods.
public void mouseClicked(MouseEvent event) {
}
public void mouseReleased(MouseEvent event) {
}
public void mouseEntered(MouseEvent event) {
}
public void mouseExited(MouseEvent event) {}
// Adds the current point to the list of points and redraws
// the panel whenever the mouse button is dragged.
public void mouseDragged(MouseEvent event) {
pointList.add(event.getPoint());
repaint();
}
public void mouseMoved(MouseEvent event) {
}
public void mouseWheelMoved(MouseWheelEvent event)
{
int notches = 0;
notches = event.getWheelRotation();
//int
if (notches > 0)
{
SIZE = SIZE + notches;
notches = 0;
}
else if (notches < 0)
{
int tempSIZE = SIZE;
tempSIZE = tempSIZE + notches;
//prevents the program from having dots that increase due to multiplying negatives by negatives
//by making anything less than 1 equal 1
if(tempSIZE < 1)
tempSIZE = 1;
SIZE = tempSIZE;
notches = 0;
}
}
}
//SIZE = SIZE + notches;
}
You appear to have ArrayList's interacting with arrays in a confusing mix that makes it hard for us to follow your logic. This suggests that your logic may be too complex for your own good and that your code might benefit from simplification. Why not instead create a List<Rectangle> such as an ArrayList<Rectangle>, and then simply loop through this list in your paintComponent method, and draw each Rectangle using the Graphics2D object's draw(...) or fill(...) method:
private List<Rectangle> rectangleList = new ArrayList<>();
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
for (Rectangle rectangle : rectangleList) {
g2.fill(rectangle);
}
}
For example:
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Stroke;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.List;
import javax.swing.*;
#SuppressWarnings("serial")
public class Foo extends JPanel {
private static final int PREF_W = 600;
private static final int PREF_H = PREF_W;
private static final Color BACKGROUND = Color.black;
private static final Color FILL_COLOR = Color.pink;
private static final Color DRAW_COLOR = Color.red;
private static final Stroke STROKE = new BasicStroke(3);
private List<Rectangle> rectangleList = new ArrayList<>();
private Point pressPoint = null;
private Point dragPoint = null;
public Foo() {
setBackground(BACKGROUND);
MyMouseAdapter myMouseAdapter = new MyMouseAdapter();
addMouseListener(myMouseAdapter);
addMouseMotionListener(myMouseAdapter);
}
#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;
Stroke oldStroke = g2.getStroke();
g2.setStroke(STROKE);
for (Rectangle rectangle : rectangleList) {
g2.setColor(FILL_COLOR);
g2.fill(rectangle);
g2.setColor(DRAW_COLOR);
g2.draw(rectangle);
}
g2.setStroke(oldStroke);
if (pressPoint != null && dragPoint != null) {
g2.setColor(FILL_COLOR.darker());
int x = Math.min(pressPoint.x, dragPoint.x);
int y = Math.min(pressPoint.y, dragPoint.y);
int width = Math.abs(pressPoint.x - dragPoint.x);
int height = Math.abs(pressPoint.y - dragPoint.y);
g2.drawRect(x, y, width, height);
}
}
private class MyMouseAdapter extends MouseAdapter {
#Override
public void mousePressed(MouseEvent e) {
pressPoint = e.getPoint();
repaint();
}
#Override
public void mouseDragged(MouseEvent e) {
dragPoint = e.getPoint();
repaint();
}
#Override
public void mouseReleased(MouseEvent e) {
dragPoint = e.getPoint();
int x = Math.min(pressPoint.x, dragPoint.x);
int y = Math.min(pressPoint.y, dragPoint.y);
int width = Math.abs(pressPoint.x - dragPoint.x);
int height = Math.abs(pressPoint.y - dragPoint.y);
Rectangle rect = new Rectangle(x, y, width, height);
rectangleList.add(rect);
pressPoint = null;
dragPoint = null;
repaint();
}
}
private static void createAndShowGui() {
JFrame frame = new JFrame("Foo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(new Foo());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}