So I am new in swing and working on the hangman game. So the way it is supposed to work is that the user is prompt to welcome message which will last a 3seconds, disappears, and then sends the user to the next frame. Everything is working perfectly except that when I run it the first frame is still visible and running in the shadow even though it goes to the next one. I have tried to use the dispose method but it's just closing the frame without going to the next one.
Here is my what I have done so far
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
import javax.swing.Timer;
public class PA1test extends JFrame{
public static void main(String[] args) {
// opens the first page
JFrame gui = new JFrame("Hangman");
gui.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
gui.add(new First_PageImage());
gui.pack();
gui.setVisible(true);
// action to open the second page
ActionListener taskPerformer = new ActionListener() {
#Override
public void actionPerformed(ActionEvent evt) {
//...Perform a task...
JFrame secpage = new JFrame("Hangman");
secpage.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
secpage.add(new SecondPage());
secpage.pack();
secpage.setVisible(true);
}
};
// set timer to perform action after 3 seconds
Timer timer = new Timer(3000 ,taskPerformer);
timer.setRepeats(false);
timer.start();
}
}
A few points:
Setting the dispose operation to JFrame.EXIT_ON_CLOSE will exit your entire application. Consider using JFrame.DISPOSE_ON_CLOSE
Sounds like you wish to create a splash screen - consider reading Oracle's tutorial on using the SplashScreen class
If your intent is a splash screen and you wish to use a JFrame or JDialog rather than java's build in SplashScreen from (2), consider designing it to look like a splash screen by removing the decorations (setUndecorated(true)) and centering (setLocationRelativeTo(null);)
In order to dispose/hide the initial JFrame/splash, you need to do so after the Timer has fired, which can be done from within the Timer's ActionListener implementation - in order to access the splash screen instance from within the anonymous class, you must mark it as final.
Pseudo-code:
final JFrame splashScreen = new JFrame("Hangman");//mark as final for visibility's sake
splashScreen.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
ActionListener taskPerformer = new ActionListener() {
#Override
public void actionPerformed(ActionEvent evt) {
//logic to create the main UI frame here
splashScreen.dispose();
}
};
// set timer to perform action after 3 seconds
Timer timer = new Timer(3000 ,taskPerformer);
timer.setRepeats(false);
timer.start();
Try .setVisible(false) on the first frame.
Related
I'm relatively new to Java Swing in general and decided to use Intellij's GUI Designer. For those not familiar with it, Intellij creates 2 files for each GUI "screen". One is the "design" in the form of a .form file where you drag and drop UI components, and one is the "controller".
Anyway, I'm doing a 5-step questionnaire implemented on Java Swing. The first part asks the user what is their favourite fruit, and when a choice button is clicked, the choice is saved and the next JFrame appears with the next question and so on. JFrame1 transitioning to JFrame2 worked fine, and all UI components were shown. However, when I tried to transition from JFrame2 to JFrame3, JFrame3 showed up blank instead.
I've tried to call .pack() before setVisible(true), and then calling .toFront() after that, but that didn't help.
Below shows a section of my code. JFrame1, 2, and 3 all use the same exact code in its constructors and calling of the next JFrame. JFrame1 only differs by a single line which will be stated later in the code.
JFrame1.java
public class Frame1 extends JFrame {
private JPanel mainPanel;
private JLabel narrationLabel;
private JButton option1_btn;
private JButton option2_btn;
private JButton option3_btn;
public Frame1()
{
setTitle("Questionnaire");
setSize(1000, 1000);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Frame2 nextScreen = new Frame2 (); //declare the next screen to show
add(mainPanel); //this is only for the first starting JFrame
//JFrame2 and JFrame3 do not have this
//repeat this for buttons option2_btn and option3_btn
option1_btn.addActionListener(new ActionListener()
{
#Override
public void actionPerformed(ActionEvent e)
{
//swap screens to the next main filler selection screen
mainPanel.setVisible(false);
nextScreen.pack();
add(nextScreen.getPanel());
nextScreen.getPanel().setVisible(true);
}
});
}
public JPanel getPanel()
{
return mainPanel;
}
}
Main.java
public class Main
{
public static void main(String[] args)
{
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JFrame1 frame1 = new JFrame1(); //create start screen GUI
frame1.setVisible(true); //display GUI
}
});
}
}
My hunch tells me it might a concurrency issue, but I'm not sure if I am right, or where is the issue and how to resolve it. If a .form file is needed, I would gladly upload the code for it. Any help appreciated.
I keep getting a expected error at the displayTimer.start(); line... what's the reason for this? I am just trying to understand how to use the swing Timer with two inputs into the constructor, and nothing else fancier. I got this code from: http://albertattard.blogspot.com/2008/09/practical-example-of-swing-timer.html
import java.awt.event.*;
import javax.swing.Timer;
public class Five {
public static void main(String[] args){
ActionListener listener = new ActionListener(){
public void actionPerformed(ActionEvent event){
System.out.println("hello");
}
};
Timer displayTimer = new Timer(1000, listener);
displayTimer.start();
}
}
adding the main method that fixed that error, but now it doesn't seem to be constantly running... it never prints hello.
Probably because the JVM exists before the Timer has a chance to fire. The point of a Timer is to use it with a GUI.
So create a more practical example. First create a JFrame and make the frame visible. Then the JVM will not exit while the frame is visible. Then you can start the Timer.
Read the section from the Swing tutorial on Concurrency for more information on the different Threads used in Swing.
I have a Java program where I plan to take input from GUI, and use that input later for processing in main(). I am using Eclipse.
I am sending an HW object(called HWObj) to the GUI JFrame, and checking for a boolean field in the object to continue processing in main().
InputWindow is custom object which extends JPanel implements ActionListener
It contains a reference to the current JFrame(parentFrame). On clicking a JButton in InputWindow, I have written a custom ActionListener which sets the value of HWObj.check to true and disposes the parentFrame. This should cause execution to resume in main().
Code for HW class is as below :
import java.awt.*;
import javax.swing.*;
public class HW {
//globals
boolean check;
public HW() {
//initialisations
check = false;
}
public static void main(String args[]) {
final HW problem = new HW();
try {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
//Create and set up the window.
JFrame frame = new JFrame("Select folders");
frame.setPreferredSize(new Dimension(640, 480));
frame.setResizable(false);
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
InputWindow Directories = new InputWindow(problem, frame);
Directories.setOpaque(true);
frame.add(Directories);
//Display the window.
frame.pack();
frame.setVisible(true);
}
});
} catch(Exception e) {
System.out.println("Exception:"+e.getLocalizedMessage());
}
while(!problem.finish);
//Do processing on problem
System.out.println("Done");
}
}
The Actionlistener in the gui is as follows:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class InputWindow extends JPanel
implements ActionListener {
private static final long serialVersionUID = 4228345704162790878L;
HW problem;
JFrame parentFrame;
//more globals
public InputWindow(HW problem, JFrame parentFrame) {
super();
this.setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
this.parentFrame = parentFrame;
this.problem = problem;
JButton finishButton = new JButton("Finish");
finishButton.setActionCommand("fin");
finishButton.addActionListener(this);
gbc.gridx = 0;
gbc.gridy = 0;
this.add(finishButton, gbc);
//Initialize buttons and text areas and labels
//Code removed for ease of reading
}
public void actionPerformed(ActionEvent e) {
String command = e.getActionCommand();
if(command.equals("fin")) {
//Do a lot of stuff, then
this.removeAll();
parentFrame.dispose();
problem.check = true;
}
}
}
I have checked, and the control to this function comes normally on button click.
Now, I would expect it to return to main, and exit the while loop, and continue processing.
This does not happen. The debugger in eclipse shows only the main thread running, and when I try to pause it, I see that the thread is stuck in the while loop. But if I try to step through, it exits the while loop as expected, and continues. However, it gets remains stuck in the while loop until I manually try to debug it.
What is the problem? Why is it not resuming the main thread as expected?
How do I resolve this issue?
Your problem is to do with how the Java memory model works. The loop in your main thread will be checking a stale value of check.
When you enter the debugger, the memory is forced to be updated, so that's why it starts working at that point.
If you mark your variable as volatile, that will force the JVM to ensure that all threads are using the up-to-date value:
volatile boolean check;
You can read more about volatile and the Java memory model in the documentation.
It looks like you're using a JFrame where you should be using a modal JDialog. If you use the modal JDialog for an input window, you will know exactly when it is "finished" since code flow will resume from the calling code from right after when the dialog was set visible.
Either that or if you are trying to swapviews, then use a CardLayout to swap your view, and use an observer type pattern to listen for change of state.
I have a frame, where i load a panel into. It works fine, but nothing has focus when it loads. Pressing tab doesn't help. I have to use the mouse to press a textfield.
I've tried: jtextfield1.requestFocus(); and jtextfiel1.requestFocusInWindow(); But it doesn't work.
What am I doing wrong?
The constructor in the JPanel:
public OpretOpdater(BrugerHandler brugerHandler, ReklamationHandler reklamationsHandler) {
initComponents();
jTextFieldOrdnr.requestFocusInWindow();
this.brugerHandler = brugerHandler;
this.rekH = reklamationsHandler;
startUp();
}
Putting the panel in the frame in the GUI:
public static void opret(ReklamationHandler reklamationHandler) {
rHandler = reklamationHandler;
SwingUtilities.invokeLater(opret);
}
static Runnable opret = new Runnable() {
#Override
public void run() {
JFrame f = jframe;
f.getContentPane().removeAll();
JPanel opret = new OpretOpdater(bHandler, rHandler);
f.getContentPane().add(opret);
f.pack();
f.setLocationRelativeTo(null);
}
};
You should call requestFocusInWindow() only when components are visible/shown on a container or after pack() has been called and all components are added to the container or else it wont work.
Also please be sure to create Swing components on Event Dispatch Thread. If you haven't already have read on Concurrency in Swing.
The reason I mention the above is not creating and manipulating Swing components on the EDT can cause random artifacts in the code. i.e focus is not being given etc.
This code below was created to show how calling requestFocusInWindow before a component is visible will not work but calling it after its visible works as expected.
Also note that removing the SwingUtilities block will cause the requestFocusInWindow not to work as expected (i.e we might be given focus or not depending on our luck :P):
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
public class Test {
public Test() {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
JTextField f1 = new JTextField(10);
JTextField f2 = new JTextField(10);
//f2.requestFocusInWindow(); //wont work (if uncomment this remember to comment the one after setVisible or you wont see the reults)
JButton b = new JButton("Button");
JPanel p = new JPanel();
p.add(f1);//by default first added component will have focus
p.add(f2);
p.add(b);
frame.add(p);
//f2.requestFocusInWindow();//wont work
frame.pack();//Realize the components.
//f2.requestFocusInWindow();//will work
frame.setVisible(true);
f2.requestFocusInWindow();//will work
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {//if we remove this block it wont work also (no matter when we call requestFocusInWindow)
#Override
public void run() {
new Test();
}
});
}
}
I would suggest a read on How to Use the Focus Subsystem.
Often it is nice to indicate which field you want to have focus when you create the field and not separate the code by adding the request focus when the frame becomes visible.
Take a look at Dialog Focus which has a solution that is also applicable in this case. Using this approach your code would look like:
JTextField f2 = new JTextField(10);
f2.addAncestorListener( new RequestFocusListener() );
I wonder what is the best approach to make a JOptionPane style plain message box disappear after being displayed for a set amount of seconds.
I am thinking to fire up a separate thread (which uses a timer) from the main GUI thread to do this, so that the main GUI can carry on processing other events etc. But how do I actually make the message box in this separate thread disappear and terminate the thread properly. Thanks.
Edit: so this is what I come up with by following the solutions posted below
package util;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.SwingConstants;
import javax.swing.Timer;
public class DisappearingMessage implements ActionListener
{
private final int ONE_SECOND = 1000;
private Timer timer;
private JFrame frame;
private JLabel msgLabel;
public DisappearingMessage (String str, int seconds)
{
frame = new JFrame ("Test Message");
msgLabel = new JLabel (str, SwingConstants.CENTER);
msgLabel.setPreferredSize(new Dimension(600, 400));
timer = new Timer (this.ONE_SECOND * seconds, this);
// only need to fire up once to make the message box disappear
timer.setRepeats(false);
}
/**
* Start the timer
*/
public void start ()
{
// make the message box appear and start the timer
frame.getContentPane().add(msgLabel, BorderLayout.CENTER);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
timer.start();
}
/**
* Handling the event fired by the timer
*/
public void actionPerformed (ActionEvent event)
{
// stop the timer and kill the message box
timer.stop();
frame.dispose();
}
public static void main (String[] args)
{
DisappearingMessage dm = new DisappearingMessage("Test", 5);
dm.start();
}
}
Now the question is that, as i cam going to create multiple instances of this class throughout the course of the interaction between the user and the main GUI, I wonder whether the dispose() method cleans up everything properly every time. Otherwise, I may end up with accumulating lots of redundant objects in memory. thanks.
I think in your situation, you can't use any of JOptionPane static methods (showX...). You have to create a JOptionPane instance instead, then create a JDialog from it and show that JDialog yourself. Once you have JDialog, you can force its visibility.
// Replace JOptionPane.showXxxx(args) with new JOptionPane(args)
JOptionPane pane = new JOptionPane(...);
final JDialog dialog = pane.createDialog("title");
Timer timer = new Timer(DELAY, new ActionListener() {
public void actionPerformed(ActionEvent e) {
dialog.setVisible(false);
// or maybe you'll need dialog.dispose() instead?
}
});
timer.setRepeats(false);
timer.start();
dialog.setVisible(true);
I haven't tried it so I can't guarantee that it works but I think it should ;-)
Of course, here Timer is javax.swing.Timer, as someone else already mentioned, thus you're sure the action will run in the EDT and you won't have any problem with creating or terminating your own Thread.
Timers have their own threads. I think what you probably should do is create a new Timer (or, preferably, make one that you reuse till you don't need it any more), schedule a task that will ask for the message box to disappear and then have that task add another task to the event queue, which will remove the message box.
There might be a better way though.
In addition:
Yes, using javax.swing.timer would probably be better. The reason I talk about using two tasks in the above is that I assume you will have to execute your hiding method inside of the AWT thread to avoid certain subtle race issues that might arise. If you use javax.swing.Timer you're already executing in the AWT thread, so that point becomes moot.