So basically I'm trying to create a reversi game. First of all I created a board populated by buttons and attached ID's to them, so I can access them afterwards if needed. Now I am trying to draw a game piece on each of the buttons, however I can't getGraphics() of the button since I read that is a bad idea and also returns null. Keep in mind that I want to keep all of my entities separate: the board, the cell and the piece, since I developing this using MVC pattern.
board.java
import java.awt.GridLayout;
import javax.swing.JPanel;
public class Board extends JPanel {
private static final int sizeOfBoard = 8;
public Board() {
int id =0;
setLayout(new GridLayout(sizeOfBoard,sizeOfBoard));
for (int i = 0; i < sizeOfBoard; i++) {
for (int j = 0; j < sizeOfBoard; j++) {
Cell cell = new Cell(id++);
Disk disk = new Disk();
cell.add(disk);
add(cell);
}
}
setSize(600,500);
setVisible(true);
}
cell.java
import java.awt.Graphics;
import javax.swing.JButton;
import javax.swing.Painter;
public class Cell extends JButton{
private int id;
private boolean taken;
private String colour;
private Painter painter;
public Cell(int id){
this.id = id;
}
public int getId(){
return id;
}
#Override
public void paintComponent(Graphics g){
super.paintComponent(g);
}
}
disk.java
import java.awt.Graphics;
import javax.swing.JComponent;
public class Disk extends JComponent{
#Override
public void paintComponent ( Graphics g ) {
super.paintComponent(g);
g.drawOval(50,50,50,50);
}
}
TL;DR How should I rewrite my code so it would have an oval on each button.
Thanks in advance.
The simplest solution: create your oval or disk images in a BufferedImage, put it into an ImageIcon, and simply swap Icons on your JButton or JLabel via its setIcon(myIcon) method. I'd create 3 ImageIcons if this were my GUI, a blank one for the initial state, and then two different colored ones for the occupied states.
For example:
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.RenderingHints;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import javax.swing.*;
#SuppressWarnings("serial")
public class ReversiPanel extends JPanel {
private static final int SIDES = 8;
private static final int ICON_LENGTH = 60;
private static final Color BG = Color.BLACK;
private static final Color LABEL_COLOR = Color.GREEN.darker();
private JLabel[][] labelGrid = new JLabel[SIDES][SIDES];
private Icon blankIcon;
private Icon blackIcon;
private Icon whiteIcon;
public ReversiPanel() {
blankIcon = createIcon(new Color(0, 0, 0, 0));
blackIcon = createIcon(Color.BLACK);
whiteIcon = createIcon(Color.WHITE);
setBackground(BG);
setBorder(BorderFactory.createEmptyBorder(1, 1, 1, 1));
setLayout(new GridLayout(SIDES, SIDES, 1, 1));
MyMouse myMouse = new MyMouse();
for (int i = 0; i < labelGrid.length; i++) {
for (int j = 0; j < labelGrid[i].length; j++) {
JLabel label = new JLabel(blankIcon);
label.setOpaque(true);
label.setBackground(LABEL_COLOR);
label.addMouseListener(myMouse);
labelGrid[i][j] = label;
add(label);
}
}
}
private Icon createIcon(Color color) {
BufferedImage img = new BufferedImage(ICON_LENGTH, ICON_LENGTH, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = img.createGraphics();
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.setColor(color);
int gap = 4;
int w = ICON_LENGTH - 2 * gap;
int h = w;
g2.fillOval(gap, gap, w, h);
g2.dispose();
return new ImageIcon(img);
}
private class MyMouse extends MouseAdapter {
#Override
public void mousePressed(MouseEvent e) {
JLabel label = (JLabel) e.getSource();
Icon icon = label.getIcon();
if (icon == blankIcon) {
label.setIcon(blackIcon);
} else if (icon == blackIcon) {
label.setIcon(whiteIcon);
} else {
label.setIcon(blankIcon);
}
}
}
private static void createAndShowGui() {
ReversiPanel mainPanel = new ReversiPanel();
JFrame frame = new JFrame("ReversiPanel");
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());
}
}
Related
tldr; How do you use a MouseEvent on a JFrame object(specifically JLabel) to update the displayed image in the JFrame
I am trying to create a program where an image is broken into tiles and on click of one of those tiles, the program moves the tile to the open space in the image. (See Sliding Puzzle for more information).
I currently am able to select an image, break the image into tiles, and "randomly" remove one of the tiles.
My next step would be, on click of one of the tiles, to swap it with the empty tile (I will work on the "eligibility" of tiles to be swapped at a later time, but for now, just want to be able to swap the tile selected with the current blank tile)
To create my image for a 3x3 grid, I split a bufferedImage into 9 equal pieces, "randomly" blank one of the images, and then display the images in jLabels using GridLayout to line them up.
When I add mouseListeners to each jLabel, I am able to see that I am entering the MouseListener as I can see the console log message "Clicked" as shown below, however, am not able to update the image as desired.
How can I use a MouseEvent on these JFrame JLabels to call a method within ImageContainer(i.e. move()) and update the displayed image?
import javax.imageio.ImageIO;
import javax.swing.*;
import javax.swing.filechooser.FileNameExtensionFilter;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.Scanner;
public class ImageJumble extends JPanel {
static int difficulty = 0;
ImageContainer imageContainer;
JFrame jFrame = new JFrame("Image Jumble");
private void run() {
SwingUtilities.invokeLater(this::displayImage);
}
public static void main(String[] args) {
System.out.print("Please enter the difficulty level (1-3): ");
Scanner scanner = new Scanner(System.in);
difficulty = scanner.nextInt();
new ImageJumble().run();
}
private void displayImage() {
JFileChooser fc = new JFileChooser();
fc.setDialogTitle("Please choose an image...");
FileNameExtensionFilter filter = new FileNameExtensionFilter("JPEG", "jpeg", "jpg", "png", "bmp", "gif");
fc.addChoosableFileFilter(filter);
BufferedImage image = null;
if (fc.showOpenDialog(null) == JFileChooser.APPROVE_OPTION) {
File selectedFile = fc.getSelectedFile();
try {
image = ImageIO.read(selectedFile);
} catch (IOException ex) {
ex.printStackTrace();
}
}
jFrame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
jFrame.setSize(image.getWidth(), image.getHeight());
jFrame.setVisible(true);
jFrame.setLayout(new GridLayout(difficulty,difficulty,0,0));
imageContainer = new ImageContainer(image,difficulty);
createImage();
}
private void createImage() {
imageContainer.randomize();
JLabel[] jLabels = new JLabel[difficulty * difficulty];
for(int i = 0; i < jLabels.length; i++) {
JLabel jLabel = new JLabel(new ImageIcon(imageContainer.getBufferedImages().get(i)));
jFrame.add(jLabel);
jLabel.addMouseListener(new MouseAdapter()
{
public void mouseClicked(MouseEvent e)
{
System.out.println("Clicked!");
imageContainer.move(i);
jFrame.removeAll();
createImage();
}
});
}
}
}
import java.awt.*;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
public class ImageContainer {
private List<BufferedImage> bufferedImages = new ArrayList<>();
private int blankLocation = 0;
public ImageContainer(BufferedImage image, int difficulty) {
BufferedImage bi = new BufferedImage(image.getWidth(null), image.getHeight(null), BufferedImage.TYPE_INT_RGB);
Graphics g = bi.createGraphics();
g.drawImage(image, 0, 0, null);
int width = bi.getWidth();
int height = bi.getHeight();
int swidth = width / difficulty;
int sheight = height / difficulty;
for (int i = 0; i < difficulty; i++) {
for (int j = 0; j < difficulty; j++) {
BufferedImage bimg = bi.getSubimage(j * swidth, i * sheight, swidth, sheight);
bufferedImages.add(bimg);
}
}
}
public List<BufferedImage> getBufferedImages() {
return bufferedImages;
}
public void setBufferedImages(List<BufferedImage> bufferedImages) {
this.bufferedImages = bufferedImages;
}
public void randomize() {
int size = bufferedImages.size();
int width = bufferedImages.get(0).getWidth();
int height = bufferedImages.get(0).getHeight();
blankLocation = new Random().nextInt(size);
bufferedImages.set(blankLocation, new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB));
}
public void move(int i) {
bufferedImages.set(blankLocation,bufferedImages.get(i));
blankLocation = i;
}
}
This answer is based on this previous answer, adapted to your code.
It is a one-file mre : the entire code can be copy-pasted to ImageJumble.java file, and run.
It supports DnD from any grid location to any other. You may want to change it. Please note the comments:
import java.awt.Color;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.GridLayout;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.awt.dnd.DnDConstants;
import java.awt.dnd.DragGestureEvent;
import java.awt.dnd.DragGestureListener;
import java.awt.dnd.DragSource;
import java.awt.dnd.DragSourceDragEvent;
import java.awt.dnd.DragSourceDropEvent;
import java.awt.dnd.DragSourceEvent;
import java.awt.dnd.DragSourceListener;
import java.awt.dnd.DropTarget;
import java.awt.dnd.DropTargetAdapter;
import java.awt.dnd.DropTargetDropEvent;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.WindowConstants;
public class ImageJumble extends JPanel {
private static int difficulty = 3;
private static final String FLOWER = "http://www.digitalphotoartistry.com/rose1.jpg";
private static final int GAP = 4;
private JPanel content;
private void run() {
SwingUtilities.invokeLater(this::displayImage);
}
public static void main(String[] args) {
new ImageJumble().run();
}
private void displayImage() {
BufferedImage bi = null;
try {
bi = ImageIO.read(new URL(FLOWER));
} catch (IOException ex) {
ex.printStackTrace();
}
content = new JPanel(new GridLayout(difficulty,difficulty, GAP,GAP));
createImage(bi);
JFrame jFrame = new JFrame("Image Jumble");
jFrame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
jFrame.add(content);
jFrame.pack();
jFrame.setVisible(true);
}
private void createImage( BufferedImage bi) {
ImageContainer imageContainer = new ImageContainer(bi,difficulty);
imageContainer.randomize();
for(int i = 0; i < difficulty * difficulty; i++) {
content.add(new DragDropPane(imageContainer.getBufferedImages().get(i)));;
}
}
}
class ImageContainer {
private final List<BufferedImage> bufferedImages = new ArrayList<>();
private int blankLocation = 0;
public ImageContainer(BufferedImage image, int difficulty) {
BufferedImage bi = new BufferedImage(image.getWidth(null), image.getHeight(null), BufferedImage.TYPE_INT_RGB);
Graphics g = bi.createGraphics();
g.drawImage(image, 0, 0, null);
int width = bi.getWidth();
int height = bi.getHeight();
int swidth = width / difficulty;
int sheight = height / difficulty;
for (int i = 0; i < difficulty; i++) {
for (int j = 0; j < difficulty; j++) {
BufferedImage bimg = bi.getSubimage(j * swidth, i * sheight, swidth, sheight);
bufferedImages.add(bimg);
}
}
}
public List<BufferedImage> getBufferedImages() {
return bufferedImages;
}
public void randomize() {
Collections.shuffle(bufferedImages);//shuffle images
int size = bufferedImages.size();
int width = bufferedImages.get(0).getWidth();
int height = bufferedImages.get(0).getHeight();
blankLocation = new Random().nextInt(size);
bufferedImages.set(blankLocation, new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB));
}
}
class DragDropPane extends JPanel implements DragGestureListener, DragSourceListener {
private JComponent dragable;
private final BufferedImage bi;
public DragDropPane(BufferedImage bi) {
setBackground(Color.BLACK);
this.bi = bi;
var label = new JLabel(new ImageIcon(bi), JLabel.CENTER);
setContent(label);
new MyDropTargetListener(this);
DragSource.getDefaultDragSource().createDefaultDragGestureRecognizer(
this, DnDConstants.ACTION_COPY, this);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(bi.getWidth(), bi.getHeight());
}
public void setContent(JComponent component) {
removeAll();
dragable = component;
add(component);
repaint();
}
//-->DragGestureListener implementation
#Override
public void dragGestureRecognized(DragGestureEvent dge) {
// Create our transferable wrapper
Transferable transferable = new TransferableComponent(dragable);
// Start the "drag" process...
DragSource ds = dge.getDragSource();
ds.startDrag(dge, Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR), transferable, this);
remove(dragable);
revalidate(); repaint();
}
//-->DragSourceListener implementation
#Override
public void dragEnter(DragSourceDragEvent dsde) {}
#Override
public void dragOver(DragSourceDragEvent dsde) {}
#Override
public void dropActionChanged(DragSourceDragEvent dsde) {}
#Override
public void dragExit(DragSourceEvent dse) {}
#Override
public void dragDropEnd(DragSourceDropEvent dsde) {
// If the drop was not successful, we need to
// return the component back to it's previous
// parent
if (!dsde.getDropSuccess()) {
setContent(dragable);
}
}
}
class MyDropTargetListener extends DropTargetAdapter {
private final DragDropPane target;
public MyDropTargetListener(DragDropPane target) {
this.target = target;
new DropTarget(target, DnDConstants.ACTION_COPY, this, true, null);
}
#Override
public void drop(DropTargetDropEvent event) {
try {
var tr = event.getTransferable();
var component = (JComponent) tr.getTransferData(TransferableComponent.component);
if (event.isDataFlavorSupported(TransferableComponent.component)) {
event.acceptDrop(DnDConstants.ACTION_COPY);
target.setContent(component);
event.dropComplete(true);
} else {
event.rejectDrop();
}
} catch (Exception e) {
e.printStackTrace();
event.rejectDrop();
}
}
}
class TransferableComponent implements Transferable {
protected static final DataFlavor component =
new DataFlavor(JComponent.class, "A Component");
protected static final DataFlavor[] supportedFlavors = {
component
};
private final JComponent componentToTransfer;
public TransferableComponent(JComponent componentToTransfer) {
this.componentToTransfer = componentToTransfer;
}
#Override
public DataFlavor[] getTransferDataFlavors() {
return supportedFlavors;
}
#Override
public boolean isDataFlavorSupported(DataFlavor flavor) {
return flavor.equals(component);
}
#Override
public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException {
if (flavor.equals(component)) return componentToTransfer;
else throw new UnsupportedFlavorException(flavor);
}
}
Don't remove/add components. Instead just swap the Icon.
Use a JPanel with a GridLayout that contains a JLabel in each grid
Add an ImageIcon to each JLabel (except for one)
Add a MouseListner to each JLabel.
In the mouseClicked event you get the label that was clicked and remove the ImageIcon and add the Icon to the empty label.
So when you create the board you could have a variable like emptyLabel which would be initialized to the label without the Icon. Then the logic in the mouseClicked might be something like:
JLabel clicked = (JLabel)e.getSource();
emptyLabel.setIcon( clicked.getIcon() );
emptyLabel = clicked;
Create a custom JLabel class to hold the position (i):
class MyImageLabel extends JLabel {
private int position;
public MyImageLabel(Icon image,int position) {
super(image);
this.position=position;
}
public int getPosition()
{
return position;
}
}
Adapt createImage to instantiate this class instead of JLabels and in the mouseListener you can call getPosition():
for(int i = 0; i < jLabels.length; i++) {
MyImageLabel jLabel = new MyImageLabel(new ImageIcon(imageContainer.getBufferedImages().get(i)),i);
jFrame.add(jLabel);
jLabel.addMouseListener(new MouseAdapter()
{
public void mouseClicked(MouseEvent e)
{
System.out.println("Clicked!");
imageContainer.move(jLabel.getPosition());
jFrame.removeAll();
createImage();
}
});
}
My advice is not to call .add and .removeAll on the JFrame object but create a new JPanel with the GridLayout and use jframe.getContentPanel().add(labelsPanel). If it doesn't refresh you can then call revalidate() on your JPanel.
I know there are already hundreds of threads but I just cant understand it..
I have this very simple class thats drawing a grid. I would like to add like a 0.2 second delay after each square. Thread.sleep doesnt work. What is the simplest way?
public Screen() {
repaint();
}
public void paint(Graphics g) {
for(int i = 0; i < 9; i++) {
for(int j = 0; j < 9; j++) {
g.drawRect(50 * i, 50 * j, 50, 50);
//Add delay
}
}
}
The simplest way to achieve delayed drawing is by using a Swing Timer, which is a class that won't block the EDT when executed. This will allow you to create a delay without blocking your UI (and making everything appear at once).
You'll have a single JPanel that's going to handle the painting in the paintComponent(...) method and not paint(...) as you did in your code above. This JPanel will repaint every Rectangle shape from the Shape API.
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class DelayedDrawing {
private JFrame frame;
private JPanel pane;
private Timer timer;
private int xCoord = 0;
private int yCoord = 0;
private static final int GAP = 10;
private static final int WIDTH_HEIGHT = 10;
private static final int ROWS = 5;
private static final int COLS = 5;
private List<Rectangle> rectangles;
#SuppressWarnings("serial")
private void createAndShowGUI() {
//We create the JFrame
frame = new JFrame(this.getClass().getSimpleName());
//We create a list of Rectangles from the Shape API
rectangles = new ArrayList<>();
createRectangle();
//Creates our JPanel that's going to draw every rectangle
pane = new JPanel() {
//Specifies the size of our JPanel
#Override
public Dimension getPreferredSize() {
return new Dimension(150, 150);
}
//This is where the "magic" happens, it iterates over our list and repaints every single Rectangle in it
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
for (Rectangle r : rectangles) {
System.out.println(r.x + " " + r.y);
g2d.draw(r);
}
}
};
//This starts our Timer
timer = new Timer(200, listener);
timer.setInitialDelay(1000);
timer.start();
//We add everything to the frame
frame.add(pane);
frame.pack();
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
//Creates a new Rectangle and adds it to the List
private void createRectangle() {
Rectangle r = new Rectangle(xCoord * WIDTH_HEIGHT + GAP, yCoord * WIDTH_HEIGHT + GAP, WIDTH_HEIGHT, WIDTH_HEIGHT);
rectangles.add(r);
}
//This will be executed everytime the Timer is fired
private ActionListener listener = e -> {
if (xCoord < ROWS) {
if (yCoord < COLS) {
yCoord++;
} else {
yCoord = 0;
xCoord++;
if (xCoord == ROWS) {
timer.stop();
return;
}
}
}
createRectangle();
pane.repaint();
};
public static void main(String[] args) {
SwingUtilities.invokeLater(new DelayedDrawing()::createAndShowGUI);
}
}
In a Java applet, I'm trying to slow down the painting of an image made up of parts, so I wrote a test program to get the basic concept working. I'm using a thread to draw a number of boxes one at a time instead of a timer because I want to be able to click the go button to reset the drawing process at any time.
The problem is, after drawing a box, it moves down a bit and an extra of the label shows up at the top of the screen. When the mouse moves off the button at the bottom, a dummy button also shows up at the top of the screen. The dummy button doesn't respond to clicks (only the real one at the bottom does), it's just there.
I'm still pretty new at this, so any help would be greatly appreciated.
Here's the JApplet class:
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
public class TestDraw extends JApplet implements ActionListener
{
private DrawPanel panel;
private JLabel lbl1;
JButton go;
Thread t;
public void init()
{
lbl1 = new JLabel("hi");
go = new JButton("GO");
go.addActionListener(this);
panel = new DrawPanel();
getContentPane().setBackground(Color.yellow);
add(lbl1, BorderLayout.NORTH);
add(panel, BorderLayout.CENTER);
add(go, BorderLayout.SOUTH);
}
public void actionPerformed(ActionEvent ae){
// tried adding these. didnt help
//panel.validate();
//panel.repaint();
//validate();
panel.resetBoxes();
repaint();
}
public void start(){
t = new Thread(panel);
t.start();
}
}
Here's the DrawPanel Class:
import java.awt.*;
import java.security.SecureRandom;
import javax.swing.*;
public class DrawPanel extends JPanel implements Runnable
{
private SecureRandom randGen = new SecureRandom();
private Box[] boxes;
private int box2draw = 0;
public DrawPanel()
{
setBackground(Color.WHITE);
boxes = new Box[5];
for (int count = 0; count < boxes.length; count++){
int x = randGen.nextInt(300);
int y = randGen.nextInt(300);
int w = randGen.nextInt(300);
int h = randGen.nextInt(300);
Color color = new Color(randGen.nextInt(256), randGen.nextInt(256), randGen.nextInt(256));
boxes[count] = new Box(x,y,w,h,color);
}
}
public void paintComponent(Graphics g)
{
boxes[box2draw].draw(g);
box2draw++;
}
public void resetBoxes(){
boxes = new Box[5];
for (int count = 0; count < boxes.length; count++){
int x = randGen.nextInt(300);
int y = randGen.nextInt(300);
int w = randGen.nextInt(300);
int h = randGen.nextInt(300);
Color color = new Color(randGen.nextInt(256), randGen.nextInt(256), randGen.nextInt(256));
boxes[count] = new Box(x,y,w,h,color);
box2draw = 0;
}
}
public void run(){
while(true){
try{
Thread.sleep(750);
}
catch(InterruptedException e){
JOptionPane.showMessageDialog(null, "interrupted");
}
repaint();
}
}
}
And finally, the Box class:
import java.awt.Color;
import java.awt.Graphics;
public class Box
{
private int x;
private int y;
private int w;
private int h;
private Color color;
public Box(int x,int y,int w,int h,Color color)
{
// initialise instance variables
this.x = x;
this.y=y;
this.w=w;
this.h = h;
this.color=color;
}
public void draw(Graphics g)
{
g.setColor(color);
g.drawRect( x, y, w, h);
}
}
Thank you for your time!
Problems:
You've got code logic within a painting method -- something that you should never do -- including your incrementing an array index. You don't have full control of when or even if this method is called and so program logic does not belong there, just painting. If you need to increment your array index, do it elsewhere, perhaps within your thread's while (true) loop. Also take care not to have the index go beyond the size of the array.
You never call the super's paintComponent method within your override, and this will prevent the component from doing housekeeping painting, probably your main problem.
If you need to display multiple items, then consider either drawing to a BufferedImage and displaying that within paintComponent, or creating a collection of Shape objects and drawing all of them within paintComponent via a for-loop.
I prefer to use the Swing-safer Swing Timer. While it doesn't matter if only calling repaint() if you want to make any other Swing calls intermittently, it makes life much easier and coding safer.
For example
package foo1;
import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Stroke;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;
import javax.swing.AbstractAction;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class TestDraw2 {
#SuppressWarnings("serial")
private static void createAndShowGui() {
final DrawPanel2 drawPanel = new DrawPanel2();
JButton drawButton = new JButton(new AbstractAction("Draw!") {
#Override
public void actionPerformed(ActionEvent e) {
drawPanel.resetBoxes();
}
});
JPanel btnPanel = new JPanel();
btnPanel.add(drawButton);
JFrame frame = new JFrame("TestDraw2");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(drawPanel);
frame.getContentPane().add(btnPanel, BorderLayout.PAGE_END);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
#SuppressWarnings("serial")
class DrawPanel2 extends JPanel {
private static final int BOX_COUNT = 5;
private static final int TIMER_DELAY = 750;
private static final int PREF_W = 600;
private static final int PREF_H = PREF_W;
private Random randGen = new Random();
private Box[] boxes;
private int box2draw = 0;
public DrawPanel2() {
setBackground(Color.YELLOW);
boxes = new Box[BOX_COUNT];
for (int count = 0; count < boxes.length; count++) {
int x = randGen.nextInt(300);
int y = randGen.nextInt(300);
int w = randGen.nextInt(300);
int h = randGen.nextInt(300);
Color color = new Color(randGen.nextInt(256), randGen.nextInt(256),
randGen.nextInt(256));
boxes[count] = new Box(x, y, w, h, color);
}
}
#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);
for (int i = 0; i < box2draw; i++) {
boxes[i].draw(g);
}
}
public void resetBoxes() {
boxes = new Box[BOX_COUNT];
for (int count = 0; count < boxes.length; count++) {
int x = randGen.nextInt(300);
int y = randGen.nextInt(300);
int w = randGen.nextInt(300);
int h = randGen.nextInt(300);
Color color = new Color(randGen.nextInt(256), randGen.nextInt(256),
randGen.nextInt(256));
boxes[count] = new Box(x, y, w, h, color);
box2draw = 0;
}
repaint();
new Timer(TIMER_DELAY, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
box2draw++;
if (box2draw > BOX_COUNT) {
box2draw = BOX_COUNT;
((Timer) e.getSource()).stop();
}
repaint();
}
}).start();
}
}
I cannot get my repaint method to work in my SimonPanel class. At first, I thought it was because I used paint() instead of paintComponent(), but that didn't seem to solve the problem. My
SimonShape.java(Holds the frame and changes the colors of the shape)
public class SimonShape extends JFrame implements KeyListener {
private int level = 1;
// speed of the light up sequence
private int lightUpSpd = 500;
// chooses random color based on numbers 0-3
private int random;
// keeps track of user inputs
private int compCounter = 0;
ArrayList<Integer> comp = new ArrayList<Integer>();
SimonPanel simon = new SimonPanel();
//SimonLabel keyLabel = new SimonLabel();
private Color blue = Color.BLUE.darker();
private Color red = Color.RED.darker();
private Color yellow = Color.YELLOW.darker();
private Color green = Color.GREEN.darker();
public SimonShape ()
{
JLabel label = new JLabel();
setSize(800,800);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLocationRelativeTo(null);
setVisible(true);
simon.setFocusable(true);
simon.setOpaque(true);
simon.addKeyListener(this);
this.add(simon);
setVisible(true);
simon.requestFocusInWindow();
label.setFocusable(true);
label.setOpaque(true);
label.addKeyListener(this);
this.add(label);
setVisible(true);
label.requestFocusInWindow();
randomColorChange();
}
private void randomColorChange()
{
JOptionPane.showMessageDialog(this, "Level " + level);
random = (int) (Math.random() * 4);
comp.add(random);
//light up sequence
for (int i = 0; i < level; i++)
{
if (comp.get(i) == 0) simon.colorChange(0);
else if (comp.get(i) == 1) simon.colorChange(1);
else if (comp.get(i) == 2) simon.colorChange(2);
else if (comp.get(i) == 3) simon.colorChange(3);
}
}
SimonPanel.java (Holds the shape)
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.Arc2D;
import javax.swing.JComponent;
import javax.swing.JPanel;
import javax.swing.Timer;
public class SimonPanel extends JPanel{
private int width = 500;
private int height = 500;
private int x = 150;
private int y = 150;
private int TURN = 45;
private SimonListener listener;
private Timer timer;
private Color blue = Color.BLUE.darker();
private Color red = Color.RED.darker();
private Color yellow = Color.YELLOW.darker();
private Color green = Color.GREEN.darker();
// speed of the light up sequence
private int lightUpSpd = 500;
// chooses random color based on numbers 0-3
private int random;
// keeps track of user inputs
private int compCounter = 0;
public SimonPanel()
{
}
public void colorChange(int color)
{
if (color == 0)
{
//lightUp();
green.brighter();
repaint();
listener = new SimonListener(this,green);
timer = new Timer(lightUpSpd,listener);
System.out.println("green");
timer.start();
}
else if (color == 1)
{
red.brighter();
repaint();
listener = new SimonListener(this,red);
timer = new Timer(lightUpSpd,listener);
System.out.println("red");
timer.start();
}
else if (color == 2)
{
blue.brighter();
repaint();
listener = new SimonListener(this,blue);
timer = new Timer(lightUpSpd,listener);
System.out.println("blue");
timer.start();
}
else if (color == 3)
{
yellow.brighter();
this.repaint();
listener = new SimonListener(this,yellow);
timer = new Timer(lightUpSpd,listener);
System.out.println("yellow");
timer.start();
}
}
public void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
// Blue Section
g2.setStroke(new BasicStroke(1.0f));
g2.setPaint(blue);
g2.fill(new Arc2D.Double(x,y,width,height,180+TURN,90,Arc2D.PIE));
// Red Section
g2.setStroke(new BasicStroke(2.0f));
g2.setPaint(red);
g2.fill(new Arc2D.Double(x,y,width,height,90+TURN,90,Arc2D.PIE));
// Yellow Section
g2.setStroke(new BasicStroke(2.0f));
g2.setPaint(yellow);
g2.fill(new Arc2D.Double(x,y,width,height,-90+TURN,90,Arc2D.PIE));
// Green Section
g2.setStroke(new BasicStroke(2.0f));
g2.setPaint(green);
g2.fill(new Arc2D.Double(x,y,width,height,360+TURN,90,Arc2D.PIE));
}
}
Test Class
public class SimonTest {
public static void main(String[] args)
{
new SimonShape();
}
}
SimonListener.java
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JPanel;
import javax.swing.Timer;
public class SimonListener implements ActionListener {
private JPanel panel;
private Color color;
public SimonListener(JPanel panel, Color color)
{
this.panel = panel;
this.color = color;
}
public void setColor(Color newColor)
{
color = newColor;
}
public void actionPerformed(ActionEvent e)
{
color.brighter();
System.out.println("Called");
panel.repaint();
((Timer) e.getSource()).stop();
}
}
Your "main" problem "seems" to the fact that you are not assiging the changes to the color objects back to anything;
green.brighter();
From the JavaDocs
Creates a new Color that is a brighter version of this Color.
You should be doing something more like
green = green.brighter();
I would also consider having a "base" color from which you can derive bright/darker colors from, but that's me
I am working on a project which is a Checkout Simulation. I have the code to make it work run but i am struggling to understand and implement how to add graphics(in my case a square) once a certain condition is true. For example i have made my code so that it goes through random numbers and if 2,4,6 or 8 has been randomly generated, someone will be added to the queue and the same goes for if they are even numbers 1 or 3, someone is removed from the queue. I basically just want to know how to add a square to the screen once i have met my condition (for example, generating a 4 should add a square to the screen but it doesn't)
ANY help would really be appreciated!
public class MainPanel extends JPanel {
private Queue<String> tillQueue;
private int rndNumber;
private int currentLength;
private ArrayList<Integer> lengthList;
private double mean;
private Random rand;
private int MAXLENGTH;
private static Random r = new Random();
private static Random r2 = new Random();
Color colour;
private static final int IMAGE_SIZE = 600;
private Timer timer;
private int delay;
private JButton startButton;
private JButton stopButton;
private BufferedImage buffer;
JToolBar toolbar;
public MainPanel() {
startButton = new JButton("START");
stopButton = new JButton("STOP");
toolbar = new JToolBar();
toolbar.add(startButton);
toolbar.add(stopButton);
this.buffer = new BufferedImage(IMAGE_SIZE, IMAGE_SIZE, BufferedImage.TYPE_INT_ARGB);
setDoubleBuffered(false);
StartActionHandler start = new StartActionHandler();
StopActionHandler stop = new StopActionHandler();
TimerEvent timerEvt = new TimerEvent();
startButton.addActionListener(start);
stopButton.addActionListener(stop);
delay = 50;
timer = new Timer(delay, timerEvt);
}
public class TimerEvent implements ActionListener {
public void actionPerformed(ActionEvent e) {
//drawNext(buffer.getGraphics());
for (int time = 1; time < 9; time++) {
rndNumber = rand.nextInt(6) + 1; //generates random number
if (rndNumber == 2 || rndNumber == 4 || rndNumber == 6 || rndNumber == 8) {
//time is added to queue
tillQueue.add(String.valueOf(time));
drawNext(buffer.getGraphics());
repaint();
}
}
}
}
public class StartActionHandler implements ActionListener {
public void actionPerformed(ActionEvent e) {
timer.start();
}
}
private void drawNext(Graphics g) {
int x = r.nextInt(IMAGE_SIZE);
int y = r.nextInt(IMAGE_SIZE);
int red = r2.nextInt(255);
int green = r2.nextInt(255);
int blue = r2.nextInt(255);
Color randomColour = new Color(red, green, blue);
g.setColor(randomColour);
g.fillRect(x, y, 10, 10);
repaint();
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(buffer, 0, 0, this);
}
}
Note several changes to get rendering working:
For convenience, use the buffer's createGraphics() method and dispose() it when done.
Initialize the offscreen buffer to a known state.
One instance of Random is usually sufficient.
Limit variable scope to the extent possible, e.g. private class TimerEvent.
Override getPreferredSize() to establish the rendering area size.
As tested:
import java.awt.BorderLayout;
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.image.BufferedImage;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Random;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JToolBar;
import javax.swing.Timer;
/**
* #see https://stackoverflow.com/a/21238669/230513
*/
public class Test {
private void display() {
JFrame f = new JFrame("Test");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(new MainPanel());
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
new Test().display();
}
});
}
private static class MainPanel extends JPanel {
private static final int SIZE = 500;
private static final int DELAY = 100;
private static final Random r = new Random();
private final Queue<String> tillQueue = new LinkedList<>();
private Timer timer;
private JButton startButton;
private JButton stopButton;
private BufferedImage buffer;
private JToolBar toolbar;
public MainPanel() {
super(new BorderLayout());
startButton = new JButton("START");
stopButton = new JButton("STOP");
toolbar = new JToolBar();
toolbar.add(startButton);
toolbar.add(stopButton);
buffer = new BufferedImage(SIZE, SIZE, BufferedImage.TYPE_INT_ARGB);
Graphics2D g = buffer.createGraphics();
g.clearRect(0, 0, SIZE, SIZE);
g.dispose();
StartActionHandler start = new StartActionHandler();
TimerEvent timerEvt = new TimerEvent();
timer = new Timer(DELAY, timerEvt);
startButton.addActionListener(start);
add(new JLabel(new ImageIcon(buffer)));
add(toolbar, BorderLayout.SOUTH);
}
private class TimerEvent implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
for (int time = 1; time < 9; time++) {
if (r.nextInt(6) % 2 == 0) {
tillQueue.add(String.valueOf(time));
drawNext();
}
}
}
}
private class StartActionHandler implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
timer.start();
}
}
private void drawNext() {
Graphics2D g = buffer.createGraphics();
int x = r.nextInt(SIZE);
int y = r.nextInt(SIZE);
g.setColor(new Color(r.nextInt()));
g.fillRect(x, y, 10, 10);
g.dispose();
repaint();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(buffer, 0, 0, this);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(SIZE, SIZE);
}
}
}
How is suposed to work? when you met the condition an item is added to tillQueue, but tillQueue is never readed...
I you want to draw something you can draw it in the method paintComponent.
To draw a rectangle simply use:
http://docs.oracle.com/javase/7/docs/api/java/awt/Graphics.html#drawRect(int, int, int, int)
You can iterate the tillQueue in the paintComponent method and draw the corresponding rectangles.