dynamically change color of custom graphic - java

Problem: graphics aren't repainted until after methods are run.
When button clicked two methods are called. Inside each method is code that is supposed to change the color of the graphic associated with this method (in the UI); when the method starts the graphic is changed from black to green; when the method finishes the color is changed from green to red. Then the next method is called and its graphic should turn green (method is running) and when the method finishes its graphic should be filled with red (method finished).
I created a simple status circle graphic (a 30 px circle with fill color) with 3 color states: black for ready; green for running; red for finished.
I believe the problem has to do with repaint() being on a separate thread and scheduled to run when able? I tried putting the code that updates the graphic inside its own thread-runnable and then using thread.join() to make sure the code had finished running but that didn't work.
EDIT
Edit: removing the code I had used for demonstration and replacing with a single, runnable code sample as per comments. What you'll see if you run the code is after you click the button the graphics don't update when each method is started and stopped, it waits until both methods have run and then repaints the graphics.
package graphicsUpdateDemo;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.Ellipse2D;
import java.beans.Transient;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
/**
* Application entry
*/
public class App{
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new MainFrame();
}
});
}
}
/**
* Main frame
*/
class MainFrame extends JFrame implements SomeListener{
private AddedPanel addedPanel;
// Constructor
public MainFrame(){
// Set frame properties
setSize(500, 500);
setLocationRelativeTo(null);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
setLayout(new FlowLayout(FlowLayout.CENTER, 5, 20));
// Create AddedPanel.
addedPanel = new AddedPanel();
add(addedPanel);
// Set AddedPanel listener to this JFrame.
addedPanel.setSomeListener(this);
}
// AddedPanel listener method
#Override
public void doStuff() {
// run simulated sort methods
sort1();
sort2();
}
// Simulated sort
// .......graphic should turn green as soon as method starts
// .......graphic should turn red as soon as method finishes.
private void sort1() {
// repaint graphic to show method is starting
addedPanel.statusOne.setStatus(SortStatus.running);
// EDIT: Make panel repaint itself.
addedPanel.paintImmediately(0, 0, getWidth(), getHeight());
// Simulate work being done.
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// repaint graphic to show methid is finished
addedPanel.statusOne.setStatus(SortStatus.finished);
// EDIT: Make panel repaint itself.
addedPanel.paintImmediately(0, 0, getWidth(), getHeight());
}
// Simulated sort
// .......graphic should turn green as soon as method starts
// .......graphic should turn red as soon as method finishes.
private void sort2() {
// repaint graphic to show method is starting (green)
addedPanel.statusTwo.setStatus(SortStatus.running);
// EDIT: Make panel repaint itself.
addedPanel.paintImmediately(0, 0, getWidth(), getHeight());
// Simulate work being done.
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// repaint graphic to show method is finished.
addedPanel.statusTwo.setStatus(SortStatus.finished);
// EDIT: Make panel repaint itself.
addedPanel.paintImmediately(0, 0, getWidth(), getHeight());
}
}
/**
* Panel to add to MainFrame
*/
class AddedPanel extends JPanel{
// Button listener
SomeListener listener;
// Button
private JButton aButton = new JButton("Click Me");
// Create Status Circles for showing method state.
public StatusCircles statusOne = new StatusCircles();
public StatusCircles statusTwo = new StatusCircles();
// Constructor.
public AddedPanel(){
setLayout(new BorderLayout(0, 15));
// Add button to panel.
add(aButton, BorderLayout.NORTH);
// Make panel for holding graphics and labels.
JPanel resultsPanel = new JPanel(new GridBagLayout());
GridBagConstraints c = new GridBagConstraints();
c.insets = new Insets(5, 5, 5, 5);
resultsPanel.add(statusOne, c);
c.gridx = 1;
resultsPanel.add(new JLabel("Method A"), c);
c.gridx = 0; c.gridy = 1;
resultsPanel.add(statusTwo, c);
c.gridx = 1;
resultsPanel.add(new JLabel("Method B"), c);
add(resultsPanel, BorderLayout.CENTER);
aButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
if(listener != null){
listener.doStuff();
}
}
});
}
public void setSomeListener(SomeListener listener){
this.listener = listener;
}
}
/**
* Graphic for showing user state of method:
* black for ready
* green for running
* red for finished
*/
class StatusCircles extends JPanel{
private SortStatus sortStatus;
private Ellipse2D.Double statusCircle = new Ellipse2D.Double(2, 2, 25, 25);
// Constructor
public StatusCircles(){
sortStatus = SortStatus.ready;
}
#Override
protected void paintComponent(Graphics g) {
// Cast Graphics to Graphics2D
Graphics2D g2 = (Graphics2D)g;
// Turn on anti aliasing
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
// Set background
g2.setColor(Color.BLACK);
g2.fillRect(0, 0, getWidth(), getHeight());
// Fill status circle with color based on status field
switch(sortStatus){
case ready:
g2.setColor(Color.BLACK);
g2.fill(statusCircle);
break;
case running:
g2.setColor(Color.GREEN);
g2.fill(statusCircle);
break;
case finished:
g2.setColor(Color.RED);
g2.fill(statusCircle);
break;
}
}
#Override
#Transient
public Dimension getPreferredSize() {
return new Dimension(30, 30);
}
// Set state method is in.
public void setStatus(SortStatus status) {
this.sortStatus = status;
repaint();
}
}
/**
* Interface
*/
interface SomeListener{
public void doStuff();
}
/**
* Enum for depicting status of graphic.
*/
enum SortStatus {
ready,
running,
finished
}
EDIT
"The repaint method lodges a request to update the viewing area and returns immediately. Its effect is asynchronous, meaning that it is up to the JVM to execute the paintComponent method on a separate thread." - Introduction to Java programming by Liang.
I think the problem is either A) in my ignorance my program design is doing something no sane programmer would do, and/or B) I don't know how to make the program change graphics colors then after that happens, then continue doing work on whatever thread the work is being done on (EDT, main thread?).
I did run into an answer that suggested never to slow down the "main thread" to wait for things to be drawn; and to instead make icons for each status circle and then swap icons - which I guess would force an immediate redraw of whatever is holding the icon? Wouldn't this, though, suggest there is a way to force an immediate repaint?
Thought experiment: you have a loop that runs 100 times, each iteration takes a second. You want to show the user each iteration by changing the color of a circle to one of a hundred different colors. Would you have to make 100 different icons for this? Or, and what I want to do, is change the fill color of the circle each iteration. ...but how to force the repainting of the circle with each iteration?
EDIT
Don't know if it is the "right" solution but the program now functions as I want it to. I placed these addedPanel.paintImmediately(0, 0, getWidth(), getHeight()); directly after the method calls asking for the graphic color to change. I updated the working example code above, the edits are depicted by "//EDIT: Make panel repaint itself".
EDIT
Now I have more confidence that I am on the right track. I believe I have implemented the things recommended to me. Understanding SwingWorker came really fast once I understood it was basically like Android's asynTask() (that's where I learned it first, that's why I say it like that). And the simulated work via sleeping is occurring in its own thread, off the EDT, so's okay now (?) ((not that I need my program to take a nap)) Here, now, is the full working code:
package graphicsUpdateDemo;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.Ellipse2D;
import java.beans.Transient;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
/**
* Application entry
*/
public class App{
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new MainFrame();
}
});
}
}
/**
* Main frame
*/
class MainFrame extends JFrame implements SomeListener{
private AddedPanel addedPanel;
// Constructor
public MainFrame(){
// Set frame properties
setSize(500, 500);
setLocationRelativeTo(null);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLayout(new FlowLayout(FlowLayout.CENTER, 5, 20));
// Create AddedPanel.
addedPanel = new AddedPanel();
add(addedPanel);
// Set AddedPanel listener to this JFrame.
addedPanel.setSomeListener(this);
// Call setVisible last
setVisible(true);
}
// AddedPanel listener method
#Override
public void doStuff() {
// Call sort1(), when that finishes have it call sort2().
sort1();
}
// Simulated sort
// .......graphic should turn green as soon as method starts
// .......graphic should turn red as soon as method finishes.
private void sort1() {
// repaint graphic to show method is starting
addedPanel.statusOne.setStatus(SortStatus.running);
// Run sort in its own thread.
SwingWorker<Void, Void> worker = new SwingWorker<Void, Void>(){
#Override
protected Void doInBackground() throws Exception {
// Simulate work being done.
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return null;
}
#Override
protected void done() {
// repaint graphic to show methid is finished
addedPanel.statusOne.setStatus(SortStatus.finished);
// Call sort2
sort2();
}
};
worker.execute();
}
// Simulated sort
// .......graphic should turn green as soon as method starts
// .......graphic should turn red as soon as method finishes.
private void sort2() {
// repaint graphic to show method is starting (green)
addedPanel.statusTwo.setStatus(SortStatus.running);
// Run sort in its own thread
SwingWorker<Void, Void> worker = new SwingWorker<Void, Void>(){
#Override
protected Void doInBackground() throws Exception {
// Simulate work being done.
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return null;
}
#Override
protected void done() {
// repaint graphic to show method is finished.
addedPanel.statusTwo.setStatus(SortStatus.finished);
}
};
worker.execute();
}
}
/**
* Panel to add to MainFrame
*/
class AddedPanel extends JPanel{
// Button listener
SomeListener listener;
// Button
private JButton aButton = new JButton("Click Me");
// Create Status Circles for showing method state.
public StatusCircles statusOne = new StatusCircles();
public StatusCircles statusTwo = new StatusCircles();
// Constructor.
public AddedPanel(){
setLayout(new BorderLayout(0, 15));
// Add button to panel.
add(aButton, BorderLayout.NORTH);
// Make panel for holding graphics and labels.
JPanel resultsPanel = new JPanel(new GridBagLayout());
GridBagConstraints c = new GridBagConstraints();
c.insets = new Insets(5, 5, 5, 5);
resultsPanel.add(statusOne, c);
c.gridx = 1;
resultsPanel.add(new JLabel("Method A"), c);
c.gridx = 0; c.gridy = 1;
resultsPanel.add(statusTwo, c);
c.gridx = 1;
resultsPanel.add(new JLabel("Method B"), c);
add(resultsPanel, BorderLayout.CENTER);
aButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
if(listener != null){
listener.doStuff();
}
}
});
}
public void setSomeListener(SomeListener listener){
this.listener = listener;
}
}
/**
* Graphic for showing user state of method:
* black for ready
* green for running
* red for finished
*/
class StatusCircles extends JPanel{
private SortStatus sortStatus;
private Ellipse2D.Double statusCircle = new Ellipse2D.Double(2, 2, 25, 25);
// Constructor
public StatusCircles(){
sortStatus = SortStatus.ready;
}
#Override
protected void paintComponent(Graphics g) {
// Cast Graphics to Graphics2D
Graphics2D g2 = (Graphics2D)g;
// Turn on anti aliasing
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
// Set background
g2.setColor(Color.BLACK);
g2.fillRect(0, 0, getWidth(), getHeight());
// Fill status circle with color based on status field
switch(sortStatus){
case ready:
g2.setColor(Color.BLACK);
g2.fill(statusCircle);
break;
case running:
g2.setColor(Color.GREEN);
g2.fill(statusCircle);
break;
case finished:
g2.setColor(Color.RED);
g2.fill(statusCircle);
break;
}
}
#Override
#Transient
public Dimension getPreferredSize() {
return new Dimension(30, 30);
}
// Set state method is in.
public void setStatus(SortStatus status) {
this.sortStatus = status;
repaint();
}
}
/**
* Interface
*/
interface SomeListener{
public void doStuff();
}
/**
* Enum for depicting status of graphic.
*/
enum SortStatus {
ready,
running,
finished
}

Using the approach shown here, let each sort update its display from a separate SwingWorker, while a Supervisor worker monitors a CountDownLatch to determine when all sorts are done.
Addendum: I have never seen an Applet before, nor a SwingWorker…I don't understand why I would need to determine when all the sorts are done…I have edited the question.
The example is also a hybrid.
SwingWorker helps avoid blocking the EDT.
Determining done tells you when to (re-)enable the start button.
Try adding a PropertyChangeListener, shown here, to a ColorIcon, seen here, in your label. Each time you setProgress() in the worker, you'll see a corresponding PropertyChangeEvent.

Related

A loop in action performed method Doesn't Working in java [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 3 months ago.
Improve this question
Question: Write JFrame that when you press the "start" button draws, and keep drawing random colored and sized filled ovals until the "stop" button is pressed.
Problem: loop inside the actionPerformed method() Doesn't Work.
The Code:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class p6 extends JFrame implements ActionListener
{
String str;
JButton start,stop;
int h=0,w=0;
p6()
{
setVisible(true);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setSize(1500,1000);
start= new JButton("Start");
stop= new JButton("Stop");
setLayout(null);
start.setBounds(500, 50, 100,30);
stop.setBounds(610, 50, 100,30);
add(start);
add(stop);
start.addActionListener(this);
stop.addActionListener(this);
}
public void actionPerformed(ActionEvent ae)
{
String c=ae.getActionCommand();
if(c=="Start")
{
while(c!="Stop")
{
h+=20;
w+=20;
}
repaint();
}
str=" "+h+" "+w;
}
public void paint(Graphics g)
{
super.paintComponents(g);
g.drawString(str, 100, 100);
//g.drawOval(100, 100, 100, 100);
g.drawOval((int)Math.random()*2000,(int) Math.random()*2000, w,h);
}
public static void main(String[] args)
{
new p6();
}
}
Let's start with How do I compare strings in Java?. This is pretty basic Java 101 and something you REALLY need to understand.
The move onto Concurrency in Swing. Swing is a single threaded. This means that any long running or blocking operations executed within the context of the Event Dispatching Thread will cause the app to "hang" and prevent any further possible updates or interaction.
Swing is also not thread safe, which means that you should only ever update the UI or state the UI depends on, from within the context of the Event Dispatching Thread.
This might seem like a dilemma, but Swing also provides a useful tool to help work with these constraints, see How to user Swing Timer for more details.
Swing also has a well defined and documented painting process, see Painting in AWT and Swing and Performing Custom Painting to get a better understand of how painting works in Swing and how you're suppose to work with it
It is generally recommended not to override the paint method of top level containers (like JFrame). These tend to be composite components and trying to paint on them tends to end up not working very well.
Image from How to Use Root Panes
And you really should make the time to learn how to use layout managers, see Laying Out Components Within a Container. They will save many hours of frustration. The following examples makes use of both a BorderLayout and CardLayout.
import java.awt.BorderLayout;
import java.awt.CardLayout;
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.util.Random;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private CardLayout cardLayout;
private JPanel showPane;
private DrawingPane drawingPane;
public TestPane() {
setLayout(new BorderLayout());
cardLayout = new CardLayout();
showPane = new JPanel(cardLayout);
showPane.add(new EmptyPane(), "Empty");
drawingPane = new DrawingPane();
showPane.add(drawingPane, "Drawing");
cardLayout.show(showPane, "Empty");
add(showPane);
JButton startButton = new JButton("Start");
JButton stopButton = new JButton("Stop");
stopButton.setEnabled(false);
JPanel actionsPane = new JPanel();
actionsPane.add(startButton);
actionsPane.add(stopButton);
add(actionsPane, BorderLayout.SOUTH);
startButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
startButton.setEnabled(false);
stopButton.setEnabled(true);
drawingPane.start();
cardLayout.show(showPane, "Drawing");
}
});
stopButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
startButton.setEnabled(true);
stopButton.setEnabled(false);
drawingPane.stop();
cardLayout.show(showPane, "Empty");
}
});
}
}
public class EmptyPane extends JPanel {
#Override
public Dimension getPreferredSize() {
return new Dimension(400, 400);
}
}
public class DrawingPane extends JPanel {
private int h = 0, w = 0;
private Timer timer;
private Random rnd = new Random();
public DrawingPane() {
}
public void start() {
if (timer == null) {
timer = new Timer(500, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
h += 20;
w += 20;
repaint();
}
});
}
timer.start();
}
public void stop() {
if (timer != null) {
timer.stop();
}
}
#Override
public Dimension getPreferredSize() {
return new Dimension(400, 400);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setColor(Color.RED);
int x = 0;
int y = 0;
if (w < getWidth() && h < getHeight()) {
x = rnd.nextInt(0, getWidth() - w);
y = rnd.nextInt(0, getHeight() - w);
}
g2d.drawOval(x, y, w, h);
g2d.dispose();
}
}
}
Why make use of CardLayout?
Based on the original code, when not painting, nothing is shown. So I made use of a CardLayout to switch to an "empty" state. Now, it wouldn't be very hard to derive a model to hold the state information and share that between the "empty" and "running" states and show something different on the "empty" state. Equally, we could make use of the glassPane, but I think we're drifting of course.
The purpose is to demonstrate ONE possible solution for showing different states.
Why use two different panes?
First, we don't "need" EmptyPane, we could get away with just an instance of JPanel, but, if you wanted to display something else when not drawing, this is one approach you could take.
The original code had a if statement which means that when not drawing, nothing is painted, so why waste the time and instead just show a "empty" pane instead.
Again, this is just a demonstration of one possible solution.
You have to add these lines before actionPerformed method,
start.setActionCommand("start");
stop.setActionCommand("stop");

Deleting a line in java when using paint(Graphics p)?

I have drawn a line using the below function:
public void paint(Graphics p) {
super.paint(p);
p.drawLine(600, 200, 580, 250);
}
I am wondering is there a way that I can delete this line?
Then is it possible to call this function in the main() method of a program?
You can use a flag to know whether the line is displaying or not.
As I said before you need to build your GUI towards JPanels and not JFrames. Also overriding paintComponent and not paint method.
For example, the following program displays a line or hides it when you click the JButton, adapt that logic to your own program with your own conditions.
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.Line2D;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class LineDrawer {
private JFrame frame;
private JButton button;
public static void main(String[] args) {
SwingUtilities.invokeLater(new LineDrawer()::createAndShowGui); //Put our program on the EDT
}
private void createAndShowGui() {
frame = new JFrame(getClass().getSimpleName());
MyPane pane = new MyPane(); //Create an instance of our custom JPanel class
button = new JButton("Hide/Show");
button.addActionListener(e -> {
pane.setShowLine(!pane.isShowLine()); //Change the state of the flag to its inverse: true -> false / false -> true
});
frame.add(pane);
frame.add(button, BorderLayout.SOUTH);
frame.pack();
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
//Our custom class that handles painting.
#SuppressWarnings("serial")
class MyPane extends JPanel {
private boolean showLine;
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
if (showLine) { //If true, show line
g2d.draw(new Line2D.Double(50, 50, 100, 50));
}
}
#Override
public Dimension getPreferredSize() {
return new Dimension(300, 300); //For the size of our JPanel
}
public boolean isShowLine() {
return showLine;
}
public void setShowLine(boolean showLine) {
this.showLine = showLine;
this.repaint(); //Everytime we set a new state to showLine, repaint to make the changes visible
}
}
}
I can't post a GIF right now, but the program itself works. Btw the above code is called a Minimal, Complete and Verifiable Example and on your next questions you're encouraged to post one in order to get specific, faster and better answers to your questions.

There is a cardlayout error in my code which states that i have an issue with mouse clicked [duplicate]

This question already has answers here:
What is a NullPointerException, and how do I fix it?
(12 answers)
Closed 4 years ago.
I have an issue with the card layout. It sates that (at GameWindow$2.mouseClicked(GameWindow.java:80)) There's an error. My card layout looks like this
((CardLayout) getContentPane().getLayout()).show(getContentPane(), "game");
Can someone please help me figure the issue out. I have two frames one that contains the game and the other contains the button to get the game stage. Can someone please do this one thing for me.
The layout is Null for the card layout
// Import necessary GUI classes
import java.awt.CardLayout;
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.HashSet;
import java.util.Set;
import javax.swing.JLabel;
import javax.swing.Timer;
// "Extend" the JFrame class so we can customize it but still keep all its features
public class GameWindow extends JFrame implements ActionListener, KeyListener {
// Serial version UIDs are used to differentiate between different object
// versions--don't worry about it!
private static final long serialVersionUID = 1L;
JLabel Player = new JLabel();
int playerSpeed = 1;
int FPS = 30;
// The keys set holds the keys being pressed
private final Set<Integer> keys = new HashSet<>();
// This main method runs when the program is executed
public static void main(String[] args) {
// This method runs a new "runnable" program after a brief pause that allows the
// main program to exit
EventQueue.invokeLater(new Runnable() {
public void run() {
// The try/catch block prevents errors from crashing the program
try {
GameWindow window = new GameWindow(); // Create and setup the main game window
window.setVisible(true); // show the new window
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
/**
* Constructor: runs when a GameWindow object is created (instantiated)
*/
public GameWindow() {
// Run the parent class constructor
super();
// Allow the panel to get focus
setFocusable(true);
// Don't let keys change the focus
setFocusTraversalKeysEnabled(false);
setBounds(100, 100, 250, 360); // the window will appear at (100, 100) and 250w by 260h
setTitle("Banana Jumper 1.0"); // the window will have this title
setResizable(false); // the window can't be resized
setLocationRelativeTo(null); // the window will move to the centre of the screen
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // when the window is closed, the program will exit
getContentPane().setLayout(new CardLayout()); // the window can swap between "stages" added as JPanels
// Make a new screen with a button and put it in the window
JPanel startStage = new JPanel(); // Create a new JPanel and add it to the card layout
startStage.setSize(getWidth(), getHeight()); // make the new JPanel fit the window
startStage.setBackground(Color.BLUE); // set the JPanel background to blue
JButton playButton = new JButton("Play"); // Add a button to the panel
playButton.addMouseListener(new MouseAdapter() { // Set the button to switch to the game stage
#Override
public void mouseClicked(MouseEvent arg0) {
((CardLayout) getContentPane().getLayout()).show(getContentPane(), "game");
setLayout(null);
}
});
startStage.add(playButton); // add the button to the stage
add(startStage, "start"); // add the stage to the window
// Create a second JPanel with band add it to the card layout
JPanel gameStage = new JPanel();
gameStage.setSize(getWidth(), getHeight());
gameStage.setVisible(true);
gameStage.setBackground(Color.RED);
// Add a button to the panel
JButton mainButton = new JButton("Back to Main Menu");
// Set the button to switch to the start stage
mainButton.addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent arg0) {
((CardLayout) getContentPane().getLayout()).show(getContentPane(), "start");
}
});
gameStage.add(mainButton);
// Put the stages in the window
add(startStage, "start");
add(gameStage, "game"); // put the stage in the window
// Setup the movable box
Player.setBounds(10, 10, 10, 10);
Player.setVisible(true);
Player.setBackground(Color.BLUE);
// Opaque makes the background visible
Player.setOpaque(true);
// Setup the key listener
addKeyListener(this);
// Null layout allows moving objects!!!
setLayout(null);
add(Player);
// Set the timer
Timer tm = new Timer(1000 / FPS, this);
tm.start();
}
#Override
public void actionPerformed(ActionEvent arg0) {
// Move up if W is pressed
if (keys.contains(KeyEvent.VK_W)) {
Player.setLocation(Player.getX(), Player.getY() - playerSpeed);
}
// Move right if D is pressed
if (keys.contains(KeyEvent.VK_D)) {
Player.setLocation(Player.getX() + playerSpeed, Player.getY());
}
// Move down if S is pressed
if (keys.contains(KeyEvent.VK_S)) {
Player.setLocation(Player.getX(), Player.getY() + playerSpeed);
}
// Move left if A is pressed
if (keys.contains(KeyEvent.VK_A)) {
Player.setLocation(Player.getX() - playerSpeed, Player.getY());
}
}
#Override
public void keyPressed(KeyEvent e) {
// Add the key to the list
// of pressed keys
if (!keys.contains(e.getKeyCode())) {
keys.add(e.getKeyCode());
}
}
#Override
public void keyReleased(KeyEvent e) {
// Remove the key from the
// list of pressed keys
keys.remove((Integer) e.getKeyCode());
}
#Override
public void keyTyped(KeyEvent e) {
}
}
You've set the layout to null - setLayout(null);, which is the equivalent of saying getContentPane().setLayout(null)
Observations...
Avoid things like startStage.setSize(getWidth(), getHeight()); // make the new JPanel fit the window - let the layout manager do it's job
startStage.setVisible(true); // show the JPanel is pointless, as Swing components are visible by default
Avoid extending from JFrame. Theres lots of reasons (your problem is a highlight of one), but you're not actually adding a new/re-usable functionality to the class and you're locking your self into a single use case. Better to start with a JPanel and add it to what ever container you want.
Don't use KeyListener, use the Key Bindings API instead. It will solve all the issues related to KeyListener, reliably
I would also avoid using "components" (like JLabel) as a game entity. You will have better luck and greater flexibility following a custom painting route

Time Delay using Thread.sleep() for paintComponent(Graphics g) not working as expected

I am making an Animated ProgressBar, in which i used multiple fillRect() method of class javax.swing.Graphics.
To put a delay after each rectangle is painted, I am using Thread.sleep(500) method for making a delay, (Suggested by many Forums, for making a delay).
The Problem is, instead of making a delay of 0.5sec after each Rectangle box is displayed, it takes the whole delay required by all the rectangles, in the start, and then displays the final image, thats the Progress Bar.
Question 1
TO make a delay for every single bar, i put the delay "Thread.sleep(500)" along with the bars "fillRect()" in a single for() loop, i would like to know, Why does it takes all the delay in the beginning and then dislplays the completed ProgressBar.
Question 2
How can i change my code, so that the delay can occur simultaneously with each rectangle bar, so when i run the program it should generate an Animated Progress Bar.
Code:
import javax.swing.JOptionPane;
import java.awt.Graphics;
import javax.swing.JFrame;
import javax.swing.JPanel;
import java.awt.Color;
class DrawPanel extends JPanel
{
public paintComponent(Graphics g)
{
super.paintComponent(g);
g.setColor(new Color(71,12,3));
g.fillRect(35,30,410,90);
for ( int i=1; i<=40; i+=2)
{
Color c = new Color (12*i/2,8*i/2,2*i/2);
g.setColor(c);
g.fillRect( 30+10*i,35,20,80);
try
{ Thread.sleep(500); }
catch(InterruptedException ex)
{ Thread.currentThread().interrupt(); }
}
}
}
class ProgressBar
{
public static void main (String []args)
{
DrawPanel panel = new DrawPanel();
JFrame app = new JFrame();
app.setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE);
app.add(panel);
app.setSize(500,200);
app.setVisible(true);
}
}
Your help is highly appreciated, Thankyou.
Don't block the EDT (Event Dispatch Thread) - the GUI will 'freeze' when that happens. Instead of calling Thread.sleep(n) implement a Swing Timer for repeated tasks. See Concurrency in Swing for more details. Also be sure to check the progress bar tutorial linked by #Brian. It contains working examples.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
class DrawPanel extends JPanel
{
int i = 0;
public DrawPanel() {
ActionListener animate = new ActionListener() {
public void actionPerformed(ActionEvent ae) {
repaint();
}
};
Timer timer = new Timer(50,animate);
timer.start();
}
public void paintComponent(Graphics g)
{
super.paintComponent(g);
g.setColor(new Color(71,12,3));
g.fillRect(35,30,410,90);
Color c = new Color (12*i/2,8*i/2,2*i/2);
g.setColor(c);
g.fillRect( 30+10*i,35,20,80);
i+=2;
if (i>40) i = 0;
}
}
class ProgressBar
{
public static void main (String []args)
{
DrawPanel panel = new DrawPanel();
JFrame app = new JFrame();
app.setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE);
app.add(panel);
app.setSize(500,200);
app.setVisible(true);
}
}
I really wouldn't do this. The Swing refresh thread isn't supposed to be used like this. You're much better off using another thread (perhaps using a TimerTask) and redrawing rectangles upon demand.
Check out the Oracle ProgressBar tutorial for more info, code etc.

Make text appear briefly in a JPanel

I am trying to make text appear briefly before it disappears. It would be along the lines of
1) Set color to black
2) wait x amount of seconds
3) set color to background color
The method I call is repaint(), which then calls paintComponent(Graphics painter). repaint() is called only if I press the space-bar.
I thought of trying repaint();Thread.sleep(1000);repaint(); (I do catch the Interrupt exception, just not shown), but it only calls paintComponent once per space-bar .
Is there an easy way to do this or is this something that is a bit challenging?
I would use a Swing Timer to schedule the repainting of the text.
Also, I would just use a JLabel to display the text. Then you just use the setText(...) method to change the text as you wish and the component will repaint itself.
You need to override the paint method in your panel and make it implement Runnable so that you can turn off the text after a few seconds. Here is some sample code:
import java.awt.Color;
import java.awt.Graphics;
import java.io.Exception;
import java.util.Timer;
import java.util.TimerTask;
import javax.swing.JFrame;
import javax.swing.JPanel;
class MyPanel extends JPanel implements Runnable{
private final static String TEXT = "HELLO WORLD";
private boolean on = true;
#Override
public void paint(Graphics g) {
super.paint(g);
if(on){
g.drawString(TEXT, 20, 20);
}
}
#Override
public void run() {
for(int i = 0 ; i< 2 ; i++){
paintImmediately(0, 0, getWidth(), getHeight());
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
on = false;
}
}
}
public class App {
public static void main(String[] args) throws Exception {
JFrame f = new JFrame();
final MyPanel p = new MyPanel();
f.add(p);
f.setSize(100,100);
f.setVisible(true);
Thread t = new Thread(p);
t.start();
}
}

Categories

Resources