I have an assignment where I have to create a GUI stopwatch program that has a start, stop, and reset button to the timer.
So far I have a program that compiles correctly but produces the error below when I go to run the program so I'm unsure of how to correct this.
at java.awt.Container.checkNotAWindow(Container.java:492)
at java.awt.Container.addImpl(Container.java:1093)
at java.awt.Container.add(Container.java:419)
at TimerFrame.main(TimerFrame.java:30)
Process completed.
Any help with running my program, or any other improvements that could be made to make the program run more smoothly that would be greatly appreciated. I'm new to learning java so any help is appreciated.
I've included my code down below as well.
Thanks in advance!
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class TimerFrame {
public static void main(String[] args){
new TimerPanel();
JFrame frame = new JFrame("Stopwatch GUI");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
TimerPanel panel = new TimerPanel();
frame.getContentPane().add(panel);
frame.pack();
frame.setVisible(true);
}
}
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.Timer;
public class TimerPanel extends JFrame implements ActionListener {
private int count;
private JButton start;
private JButton stop;
private JButton reset;
private JLabel label;
private Timer timer;
private double time;
private double extra = 0;
public TimerPanel(){
setLayout(new GridLayout(2, 3));
label = new JLabel("00.00", JLabel.CENTER);
add(label);
// Creating a panel (JPanel) for hte buttons to reside in
JPanel buttons = new JPanel();
// initilizing buttons
start = new JButton("Start");
stop = new JButton("Stop");
reset = new JButton("Reset");
// adds the buttons start, stop, and reset to the panel
buttons.add(start);
buttons.add(stop);
buttons.add(reset);
// adds the panel to the frame
add(buttons);
// adding action listeners to the buttons
start.addActionListener(this);
stop.addActionListener(this);
reset.addActionListener(this);
// initilize timer
timer = new Timer(0, this);
timer.setDelay(1000);
setBackground(Color.pink);
setPreferredSize(new Dimension(500, 300));
}
public void actionPerformed(ActionEvent event){
//find the source of the action
if(event.getSource().equals(timer)){
// records the current time event from timer
double currentTime = System.currentTimeMillis();
// finds the elapsed time from time and currentTime
double elapsed = (double) ( currentTime - time) / 1000;
//adds the extra to the elapsed time
elapsed = elapsed + extra;
//displays time in JLabel label
label.setText(String.format("%.1f", elapsed));
} else if (event.getSource().equals(start)){
// the start button has been clicked
if(!timer.isRunning()){
time = System.currentTimeMillis();
timer.start();
}
} else if (event.getSource().equals(stop)) {
// the stop button has been clicked
if(timer.isRunning()){
//record current time in currentTime
double currentTime = System.currentTimeMillis();
// finds the elapsed time from time and currentTime
double elapsed = (double) ( currentTime - time ) / 1000;
// (double) -> casts whatever is produced to be a double
//adds the extra to the elapsed time
elapsed = elapsed + extra;
//stop the timer
timer.stop();
}
} else{
// the reset button has been clicked
// stops the timer before resetting it
if(timer.isRunning()){
timer.stop();
}
// resets the timer
time = System.currentTimeMillis();
extra = 0;
label.setText("00.00");
}
}
}
public class TimerPanel extends JFrame implements ActionListener {
Well, you call your class TimerPanel yet your extend a JFrame.
If the class is a "panel" then it should extend JPanel.
TimerPanel panel = new TimerPanel();
frame.getContentPane().add(panel);
You get the error because you can't add a JFrame to a JFrame.
Any help with running my program, or any other improvements
Why are you trying to write the entire class before doing testing? Testing should be done step by step.
So you start by creating a frame and test that.
Then you add some components to the frame and test if the layout is correct.
Then you add ActionListeners to your buttons (one at a time) and test them.
That way when you have problems you know what you just changed.
Related
So I am making a game that records your reaction time after you see something pop up on the screen, but I am having trouble with getting that reaction time. I want the user to press the up arrow key once they see a blue ball and I want to record their reaction time once they pressed that button.
Here is my code:
public class Game extends JPanel
{
private JLabel start, main, time;
private ImageIcon constant, react;
final int width = 600;
final int height = 600;
private Timer replace;
private Random random;
private int randTime;
private long startTime;
private long stopTime;
private long reactionTime;
private Action upAction;
public Game()
{
setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
setPreferredSize(new Dimension(width, height));
setBackground(Color.black);
start = new JLabel("Click Up Arrow when you see a blue ball");
start.setForeground(Color.white);
start.setAlignmentX(Component.CENTER_ALIGNMENT);
add(start);
constant = new ImageIcon("constantCircle.png");
main = new JLabel(constant);
main.setAlignmentX(Component.CENTER_ALIGNMENT);
randomTime();
replace = new Timer(randTime, timeListener);
startTime = System.currentTimeMillis();
replace.setRepeats(false);
replace.start();
add(main);
time = new JLabel("0");
time.getInputMap().put(KeyStroke.getKeyStroke("UP"), "upAction");
time.getActionMap().put("upAction", upAction);
add(time);
}
public void randomTime()
{
random = new Random();
int max = 8000;
randTime = random.nextInt(max);
}
ActionListener timeListener = new ActionListener()
{
public void actionPerformed (ActionEvent e)
{
react = new ImageIcon("reactCircle.png");
main.setIcon(react);
}
};
public class UpAction extends AbstractAction
{
public void actionPerformed(ActionEvent e)
{
stopTime = System.currentTimeMillis();
reactionTime = stopTime - startTime;
time.setText("" + reactionTime);
}
}
}
I setup a "startTime" using System.currentTimeMillis to get the time after the ball turns blue but I am not sure if that is the correct way to do it.
I also setup a "stopTime" in the "UpAction" class where I want to get the time once the user presses up arrow but I it does not work.
if anything doesn't make sense or isn't clear enough, I'll try my best to elaborate more
I came up with the following GUI.
There are two important principles I want to explain. The first is that creating the GUI is a separate process from updating the GUI. The second is that the game process is a state machine. The game is in six separate states. Here's what I wrote to keep the states in mind.
Sequence of events
Left-click button
Wait 2 - 4 seconds to display the circle.
Capture start time
Left-click button
Capture end time.
Calculate and display reaction time.
Repeat 1 - 6.
So, for the GUI, I created a JFrame and three JPanels; an upper JPanel, a drawing JPanel, and a button JPanel.
I started the Swing application with a call to the SwingUtilities invokeLater method. This method ensures that the Swing components are created and executed on the Event Dispatch Thread.
The JFrame has a default BorderLayout, which I used to place the three JPanels. The JFrame method calls must be executed in a specific order. This is the order I use for all my Swing applications.
The upper JPanel contains the instructions and the reaction time display. A JTextArea is great for displaying instructions. I put the JTextArea inside an inner JPanel using a FlowLayout, which I placed in the upper JPanel using a BorderLayout. Nesting layouts like this is a good way to organize the Swing components in a logical manner.
I put the reaction time Swing components in another inner JPanel, which I placed in the upper JPanel.
I created a drawing JPanel so I wouldn't have to bother with an image.
The button JPanel holds the Submit JButton.
I created two controller classes. One controller class, ButtonListener, responds to the JButton left-clicks. The other controller class, TimerListener, creates the delay for drawing the circle.
The ButtonListener state variable allows me to provide different functionality with the same ActionListener. If you wish, you can write separate ActionListener classes, one for each function.
By separating my code into view and controller classes, I could separate my concerns and focus on one part of the application at a time.
Here's the complete runnable code. I made all the classes inner classes so I could post this code as one block.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class ReactionTimeGame implements Runnable {
public static void main(String[] args) {
SwingUtilities.invokeLater(new ReactionTimeGame());
}
private long reactionTime;
private DrawingPanel drawingPanel;
private JTextField reactionTimeField;
public ReactionTimeGame() {
this.reactionTime = 0L;
}
#Override
public void run() {
JFrame frame = new JFrame("Reaction Time Game");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(createUpperPanel(), BorderLayout.BEFORE_FIRST_LINE);
this.drawingPanel = new DrawingPanel();
frame.add(drawingPanel, BorderLayout.CENTER);
frame.add(createButtonPanel(), BorderLayout.AFTER_LAST_LINE);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
private JPanel createUpperPanel() {
JPanel panel = new JPanel(new BorderLayout());
panel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
JPanel innerPanel = new JPanel(new FlowLayout());
String instructions = "This game will test your reaction time. To play "
+ "the game, left-click on the Submit button. After a random time "
+ "from 2 - 4 seconds, a circle will appear. Left-click the "
+ "Submit button again. Your reaction time will be displayed "
+ "above where the circle was.\n\n"
+ "Left-click the Submit button to start each round of the game.";
JTextArea textArea = new JTextArea(7, 40);
textArea.setEditable(false);
textArea.setText(instructions);
textArea.setLineWrap(true);
textArea.setWrapStyleWord(true);
innerPanel.add(textArea);
panel.add(innerPanel, BorderLayout.BEFORE_FIRST_LINE);
innerPanel = new JPanel(new FlowLayout());
JLabel label = new JLabel("Reaction Time:");
innerPanel.add(label);
reactionTimeField = new JTextField(5);
reactionTimeField.setEditable(false);
updateReactionTime();
innerPanel.add(reactionTimeField);
label = new JLabel("seconds");
innerPanel.add(label);
panel.add(innerPanel, BorderLayout.AFTER_LAST_LINE);
return panel;
}
private JPanel createButtonPanel() {
JPanel panel = new JPanel(new FlowLayout());
panel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
JButton button = new JButton("Submit");
button.addActionListener(new ButtonListener());
panel.add(button);
return panel;
}
public void setReactionTime(long reactionTime) {
this.reactionTime = reactionTime;
}
public void drawCircle() {
drawingPanel.setDrawCircle(true);
drawingPanel.repaint();
}
public void eraseCircle() {
drawingPanel.setDrawCircle(false);
drawingPanel.repaint();
}
public void updateReactionTime() {
double time = 0.001 * reactionTime;
reactionTimeField.setText(String.format("%.3f", time));
}
public class DrawingPanel extends JPanel {
private static final long serialVersionUID = 1L;
private boolean drawCircle;
public DrawingPanel() {
this.drawCircle = false;
this.setPreferredSize(new Dimension(300, 300));
}
public void setDrawCircle(boolean drawCircle) {
this.drawCircle = drawCircle;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (drawCircle) {
int centerX = getWidth() / 2;
int centerY = getHeight() / 2;
int radius = Math.min(getWidth(), getHeight()) * 9 / 20;
int diameter = radius + radius;
g.setColor(Color.MAGENTA);
g.fillOval(centerX - radius, centerY - radius, diameter, diameter);
}
}
}
public class ButtonListener implements ActionListener {
private int state;
private long startTime;
private final Random random;
private Timer timer;
public ButtonListener() {
this.state = 1;
this.random = new Random();
}
#Override
public void actionPerformed(ActionEvent event) {
switch (state) {
case 1:
int delay = random.nextInt(2000) + 2000;
timer = new Timer(delay, new TimerListener(this));
timer.start();
state = 2;
break;
case 2:
setEndTime(System.currentTimeMillis());
eraseCircle();
state = 1;
break;
}
}
public int getState() {
return state;
}
public void setStartTime(long startTime) {
this.startTime = startTime;
}
public void setEndTime(long endTime) {
long elapsedTime = endTime - startTime;
setReactionTime(elapsedTime);
updateReactionTime();
}
}
public class TimerListener implements ActionListener {
private final ButtonListener listener;
public TimerListener(ButtonListener listener) {
this.listener = listener;
}
#Override
public void actionPerformed(ActionEvent event) {
Timer timer = (Timer) event.getSource();
timer.stop();
if (listener.getState() == 2) {
listener.setStartTime(System.currentTimeMillis());
drawCircle();
}
}
}
}
I tried to write clock in Java.
Here is example of my code.
I want to make the background transparent but I don't know how.
I want to place my program on top of all Windows. An example of how that must look :
.
There are two classes in my program. First of the called clock and second is Executor
screen:
.
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.Calendar;
public class clock extends JFrame
{
private static final long serialVersionUID = 1L;
JLabel timeF;
JPanel panel;
public clock()
{
super("Java Clock by Anton" );
setSize (400,90);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
setResizable(true);
setLocationRelativeTo(null);
panel = new JPanel();
panel.setLayout(new FlowLayout());
panel.setOpaque(false);
timeF = new JLabel("0:0:0");
timeF.setFont(new Font("Arial",Font.PLAIN,48));
panel.add(timeF);
add(panel);
Timer t = new Timer(1000,new Listener());
t.start();
}
class Listener implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
Calendar rightNow = Calendar.getInstance();
int hour = rightNow.get(Calendar.HOUR_OF_DAY);
int min = rightNow.get(Calendar.MINUTE);
int sec = rightNow.get(Calendar.SECOND);
timeF.setText(hour + ":" + min + ":" + sec);
}
}
}
Right after you create your JPanel, add the following:
setUndecorated(true);
setVisible(true);
// has the alpha parameter set to transparent.
setBackground(new Color(255,0,0,0));
panel.setOpaque(false);
But once the Frame is undecorated, you won't be able to close it, so you may need to add some additional code in your panel. A mouseListener will work.
Create an inner class
class MyMouseListener extends MouseAdapter {
public void mouseClicked(MouseEvent a) {
System.exit(-1);
}
}
Then
panel.addMouseListener(new MyMouseListener());
When you click on the time, it will exit the program.
Note: You should not be extending JFrame. It is best to create an instance and use that. The rule is, prefer composition over inheritance when it makes sense to do so.
I've recently made a small puzzle game that deals with clicking certain areas. I also made a solver which activates the necessary areas to win. The problem that I am facing is that I would like to pause each time it activates an area to create a sort of "solving animation". My problem is represented here
package experiment;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class ExperimentHere extends JFrame implements ActionListener
{
private static final long serialVersionUID = 1L;
private JButton changeLabelButton;
private JPanel mainPanel;
private JLabel labelToChange;
public ExperimentHere() {
changeLabelButton = new JButton("Set the label");
changeLabelButton.addActionListener(this);
mainPanel = new JPanel();
labelToChange = new JLabel();
labelToChange.setText("This needs to be changed");
mainPanel.add(labelToChange);
mainPanel.add(changeLabelButton);
this.add(mainPanel);
setTitle("Timer Program");
setContentPane(mainPanel);
setPreferredSize(new Dimension(1000, 1000));
pack();
}
public void actionPerformed(ActionEvent e) {
if (e.getSource().equals(changeLabelButton)){
changeLabel();
}
}
public void changeLabel(){
for (int i = 0; i<5; i++){
labelToChange.setText(""+i);
// Pause for 200ms here
}
}
public static void main(String[] args){
ExperimentHere test = new ExperimentHere();
test.setVisible(true);
}
}
I have tried using Timers, but I'm not sure how to format it properly so that it only pauses each time the loop inside of changeLabel() is incremented, because the second paramter in Timer() asks for an ActionListener.
I've also tried using Thread.sleep() but it just freezes my program and then instantly solves it.
Ideally the changeLabel method would increment by 1, set the label to the new String, wait for 200ms, and then increment again.
I have tried using Timers, but I'm not sure how to format it properly so that it only pauses each time the loop inside of changeLabel() is incremented
When you use a Timer you don't use a loop. The point of a Timer is that you start the Timer and it keeps executing until you stop the Timer.
You also don't make methods, you make an Action to invoke whenever the Timer fires.
So you need an instance variable in your class that keeps track of the number of times the Timer has fired (lets call it "timerCounter"). Then you need to create an Action to invoke every time the Timer is fired.
So you create a couple of instance variables:
int timerCounter = 0;
Action action;
Then in the constructor of your class you create an Action something like:
action = new AbstractAction()
{
#Override
public void actionPerformed(ActionEvent e)
{
labelToChange.setText("" + timerCounter);
timerCounter++;
if (timerCounter > 5)
{
Timer timer = (Timer)e.getSource();
timer.stop();
}
}
}
So now in the ActionListenerof your button you can do something like:
timerCounter = 0;
Timer timer = new Timer(200, action);
timer.start();
I am testing how time-consuming is placing components in a panel.
As a test example I am placing some hundreds components to a jPanel. I am looking for a faster way to place them. Here is my test code:
import java.awt.*;
import javax.swing.*;
public class MyPane {
JFrame Myframe;
JPanel Mypanel;
public void createUI()
{
Myframe = new JFrame("Test clicks");
Mypanel = new JPanel();
Myframe.setPreferredSize(new Dimension(600, 600));
Myframe.setMinimumSize(new Dimension(600, 600));
Myframe.add(Mypanel);
Myframe.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Myframe.setVisible(true);
//ADD 100+1 LABELS
long start_time = System.nanoTime();//START TIME
for (int i=0; i<100 ; i++){
JLabel myLabel= new JLabel();
myLabel.setText("label"+ i);
Mypanel.add(myLabel);
}
long end_time = System.nanoTime(); //START TIME
double difference = (end_time - start_time)/1000000;
//ADD EXECUTION TIME LABEL
JLabel MyInfo= new JLabel();
MyInfo.setText("milliseconds:"+ difference);
MyInfo.setBackground(Color.yellow);
MyInfo.setOpaque(true);
Mypanel.add(MyInfo);
Myframe.pack();
}
public static void main(String[] args) {
MyPane overlapPane = new MyPane();
overlapPane.createUI();
}
}
In my pc adding 1000 Jlabels takes 120 millliseconds. Of course execution time will vary greatly depending on hardware, Os etc.
My question is: Is there a faster way to place many components in a panel. Is there a way, for example, to place all of them at once? Would that make any difference?
EDIT: I would like to clarify that putting 1000 labels one just after the other is not what I am trying to achieve in any real world software I am developing, although I have the right to do it. It's just a example, and that I know that in some cases there can be more efficient ways to show large amount of text information on screen. Also, my question is not about software optimization in general, but It's only about testing this specific algorithm.
This adds 1,000,000 viewable objects in 196 nano-seconds.
Like I said, it depends on the context, and is quite an arbitrary question.
import java.awt.*;
import java.util.Vector;
import javax.swing.*;
class ManyObjects {
public static void main(String[] args) {
Runnable r = new Runnable() {
#Override
public void run() {
JPanel gui = new JPanel(new BorderLayout());
Vector<Integer> v = new Vector<Integer>();
long start = System.nanoTime();
for (int ii=0; ii<=1000000; ii++) {
v.add(ii);
}
long end = System.nanoTime();
int duration = (int)((end-start)/1000000);
v.add(duration);
JList<Integer> list = new JList<Integer>(v);
System.out.println("Duration in seconds: " + duration);
JOptionPane.showMessageDialog(null, new JScrollPane(list));
}
};
// Swing GUIs should be created and updated on the EDT
// http://docs.oracle.com/javase/tutorial/uiswing/concurrency
SwingUtilities.invokeLater(r);
}
}
How do I make the two buttons that are displayed reset / pause the timer? The timer works but I want to change the code for the buttons so that they will change the timer instead of outputting to the console. Thank you.
CODE:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class createWindow extends JFrame implements ActionListener
{
public static void main(String[] args)
{
new createWindow();
}//end main
createWindow()
{
super("Frame");
setSize(400,70);
setResizable(false);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setLayout(new FlowLayout());
show();
final JLabel time = new JLabel("Timer");
JButton reset = new JButton("Reset timer");
JButton pause = new JButton("Pause timer");
reset.setActionCommand("resetClicked");
pause.setActionCommand("pauseClicked");
reset.addActionListener(this);
pause.addActionListener(this);
add(pause);
add(time);
add(reset);
long start = System.currentTimeMillis();
while (true)
{
long timer = System.currentTimeMillis() - start;
final int seconds = (int) (timer / 1000);
String display = Integer.toString(seconds);
time.setText(display);
}//end while loop
}//end constructor
#Override
public void actionPerformed(ActionEvent e)
{
String buttonClicked = e.getActionCommand();
if(buttonClicked.equals("resetClicked"))
{
System.out.println("The reset button was clicked"); //Change to reset timer
}
else if(buttonClicked.equals("pauseClicked"))
{
System.out.println("The pause button was clicked"); //Change to pause timer
}
}//end listener
}
Don't use an infinite while loop. This blocks the EDT. Instead use a Swing Timer. This will give you control to start and stop the Timer.
Stopwatch Example
Side Notes:
Don't use JFrame.show as that method is deprecated. Use JFrame.setVisible instead. Also make this call when all components have been added to the frame.
The functionality for the JButtons is sufficiently different to warrant using separate ActionListener instances for each button.
The preferred approach is to use a JFrame instance directly rather then extending it.
Class names in Java begin with uppercase so createWindow would become CreateWindow.