Extra dummy components painted during thread execution - java

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();
}
}

Related

Draw shapes with delay

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);
}
}

Drawing an oval on top of JButton

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());
}
}

Generating specific shapes with random dimensions using a JComboBox to select shapes

I am trying to create a program that uses a JComboBox containing specific shapes (Circle, Square, Oval, Rectangle). After the user clicks on a specified shape, the Panel will display 20 of that shape in random dimensions and locations.
I am having trouble on how to make the shapes have random dimensions and locations. Here is my code so far. Any advice or sources to look at would be appreciated.
Thank you.
import javax.swing.*;
import java.awt.*;
import java.awt.geom.*;
import java.util.*;
import java.awt.event.*;
public class HW1b extends JFrame
{
public HW1b()
{
super("Shapes");
final ComboPanel comboPanel = new ComboPanel();
String[] shapeItems = {"Circle", "Square", "Oval", "Rectangle"};
JComboBox shapeBox = new JComboBox<String>(shapeItems);
shapeBox.addItemListener(new ItemListener()
{
public void itemStateChanged(ItemEvent ie)
{
if (ie.getStateChange() == ItemEvent.SELECTED)
{
String item = (String)ie.getItem();
if(shapeBox.getSelectedItem().equals("Circle"))
comboPanel.makeCircles();
if(shapeBox.getSelectedItem().equals("Square"))
comboPanel.makeSquares();
if(shapeBox.getSelectedItem().equals("Oval"))
comboPanel.makeOvals();
if(shapeBox.getSelectedItem().equals("Rectangle"))
comboPanel.makeRectangles();
}
}
});
JPanel southPanel = new JPanel();
southPanel.add(shapeBox);
setDefaultCloseOperation(EXIT_ON_CLOSE);
getContentPane().add(comboPanel, "Center");
getContentPane().add(southPanel, "South");
setSize( 400, 400 );
setLocation( 200, 200 );
setVisible( true );
}
private class ComboPanel extends JPanel
{
int w, h;
Random rand;
static final int OVAL = 0;
static final int RECTANGLE = 1;
int shapeType = -1;
public ComboPanel()
{
rand = new Random();
setBackground(Color.white);
}
public void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
int width = getWidth();
int height = getHeight();
int x, y;
Shape s = null;
for (int i = 0; i < 20; i++)
{
x = rand.nextInt(width - w);
y = rand.nextInt(width - h);
switch(shapeType)
{
case OVAL: s = new Ellipse2D.Double(x,y,w,h);
break;
case RECTANGLE: s = new Rectangle2D.Double(x,y,w,h);
break;
}
if (shapeType > -1)
g2d.draw(s);
}
}
public void makeCircles()
{
shapeType = OVAL;
w = 75;
h = 75;
repaint();
}
public void makeSquares()
{
shapeType = RECTANGLE;
w = 50;
h = 50;
repaint();
}
public void makeOvals()
{
shapeType = OVAL;
w = 80;
h = 60;
repaint();
}
public void makeRectangles()
{
shapeType = RECTANGLE;
w = 80;
h = 40;
repaint();
}
}
public static void main(String[] args)
{
new HW1b();
}
}
You're hard-coding w and h in your code, and so there's no way for this to vary among your shapes. Instead of doing this, use your Random variable, rand, to select random w and h values that are within some desired range. Myself, I wouldn't create my shapes within the paintComponent method since painting is not fully under my control and can occur when I don't want it to. For instance, in your code, your shapes will vary tremendously if the GUI is resized. Instead I'd create a collection such as an ArrayList<Shape> and fill it with created Shape objects (i.e., Ellipse2D for my circles) when desired, and then iterate through that collection within your paintComponent method, drawing your shapes.
for example...
import java.awt.BorderLayout;
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.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.geom.Ellipse2D;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import javax.swing.*;
public class SomeShapes extends JPanel {
private ShapePanel shapePanel = new ShapePanel();
private JComboBox<MyShape> myShapeCombo = new JComboBox<>(MyShape.values());
public SomeShapes() {
myShapeCombo.setSelectedIndex(-1);
myShapeCombo.addItemListener(new ComboListener());
JPanel bottomPanel = new JPanel();
bottomPanel.add(myShapeCombo);
setLayout(new BorderLayout());
add(shapePanel, BorderLayout.CENTER);
add(bottomPanel, BorderLayout.PAGE_END);
}
private class ComboListener implements ItemListener {
#Override
public void itemStateChanged(ItemEvent e) {
MyShape myShape = (MyShape) e.getItem();
shapePanel.drawShapes(myShape);
}
}
private static void createAndShowGui() {
JFrame frame = new JFrame("SomeShapes");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(new SomeShapes());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
enum MyShape {
OVAL("Oval"), RECTANGLE("Rectangle"), SQUARE("Square"), CIRCLE("Circle");
private String name;
private MyShape(String name) {
this.name = name;
}
public String getName() {
return name;
}
#Override
public String toString() {
return getName();
}
}
class ShapePanel extends JPanel {
private static final int PREF_W = 600;
private static final int PREF_H = PREF_W;
private static final Color SHAPE_COLOR = Color.BLUE;
private static final int SHAPE_COUNT = 20;
private static int MIN = 5;
private static int MAX = 200;
private List<Shape> shapeList = new ArrayList<>();
private Random random = new Random();
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(PREF_W, PREF_H);
}
public void drawShapes(MyShape myShape) {
shapeList.clear(); // empty the shapeList
switch (myShape) {
case OVAL:
drawOval();
break;
case RECTANGLE:
drawRectangle();
break;
// etc...
default:
break;
}
repaint();
}
private void drawOval() {
// for loop to do this times SHAPE_COUNT(20) times.
for (int i = 0; i < SHAPE_COUNT; i++) {
// first create random width and height
int w = random.nextInt(MAX - MIN) + MIN;
int h = random.nextInt(MAX - MIN) + MIN;
// then random location, but taking care so that it
// fully fits into our JPanel
int x = random.nextInt(getWidth() - w);
int y = random.nextInt(getHeight() - h);
// then create new Shape and place in our shapeList.
shapeList.add(new Ellipse2D.Double(x, y, w, h));
}
}
private void drawRectangle() {
// .... etc
}
//.. .. etc
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D)g;
// set rendering hints for smooth ovals
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setColor(SHAPE_COLOR);
// iterate through the shapeList ArrayList
for (Shape shape : shapeList) {
g2d.draw(shape); // and draw each Shape it holds
}
}
}

Using a GUI to input then draw several shapes

I'm trying to create a GUI that allows the user to input a number, then the GUI creates an array of Points of that size, then draws that number of circles. However, when I run this code, the GUI only displays a black panel and using a system.out.println it says that the number in the JTextField never reaches the panel. How can I alter my code so that the user can input a number in the textfield, press enter, then the GUI draws that number of circles?
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
class ControlPanel extends JPanel{
JLabel inLabel;
JTextField inText;
public ControlPanel(){
setBackground(Color.lightGray);
inLabel = new JLabel("Enter the number of points: ");
inText = new JTextField(20);
add(inLabel);
add(inText);
}
public JTextField getInText(){
return inText;
}
public int getNPoints(){
int nP = Integer.parseInt(inText.getText());
return nP;
}
}
class PiPanel2 extends JPanel{
private Point[] points;
int nPoints;
Dimension d = new Dimension();
private int X = d.width;
private int Y = d.height;
private int w = 5;
private int h = 5;
public PiPanel2(){
setBackground(Color.BLACK);
}
public void setNPoints(int nPoints){
this.nPoints = nPoints;
}
public int getNP(){
return nPoints;
}
public void paintComponent(Graphics g){
int n = getNP();
points = new Point[n];
System.out.println(n);
for(int i = 0; i < n; i++){
points[i] = new Point(Math.random(), Math.random());
}
g.setColor(getBackground());
super.paintComponent(g);
g.setColor(Color.red);
g.drawOval(-X, -Y, X*2, Y*2);
for(int i = 0; i < points.length; i++){
int x = (int)(points[i].getX()*X);
int y = (int)(points[i].getY()*Y);
if(points[i].withinR()){
g.setColor(Color.blue);
g.fillOval(x, y, w, h);
}else if(!points[i].withinR()){
g.setColor(Color.orange);
g.fillOval(x, y, w, h);
}
}
}
}
class PiFrame2 extends JFrame implements ActionListener{
private int X = 600;
private int Y = 600;
ControlPanel control;
PiPanel2 piPan;
public PiFrame2(){
control = new ControlPanel();
piPan = new PiPanel2();
control.getInText().addActionListener(this);
setLayout(new BorderLayout());
// and add the panels to the frame
getContentPane().add(control, BorderLayout.SOUTH);
getContentPane().add(piPan, BorderLayout.CENTER);
// this code enables you to close the window
addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
setSize(X,Y);
setLocation(500,0);
}
public void actionPerformed(ActionEvent e) {
int nPoints = control.getNPoints();
piPan.setNPoints(nPoints);
}
}

Java Graphics Checkout Simulation;

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.

Categories