How to make button click block in Java Swing? - java

I am very new to Swing.
I have
itemActionButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg) {
itemAction();
}
});
But when the button is clicked, instead of running this action on another thread, I would like the parent's form's thread to wait until it the action is completed before refreshing, allowing additional clicks, etc.
How can I do this?

Code in the ActionListener executes on the EDT, which prevents the GUI from repainting and responding to other events.
If you have a long running task and you don't want to block the EDT then you need to use another Thread.
Read the section from the Swing tutorial on Concurrency for more information and a solution by using a SwingWorker.

Related

How to execute another action while one action is executing?

My gui class extends JFrame. GUI contain two JButton say (start_button and view_button).
JButton start_button;
JButton view_button;
The action to be performed after pressing start_button takes some time(ranging from 2 min to 5 min).
start_button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent evt) {
//...
//action executing. May take 2 to 5 min to complete.
}
});
While this action is executing, I want to see the status log of this action in separate TextPane enclosed inside JDialogue. And this JDialogue is made visible by pressing the view_button.
view_button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent evt) {
myJDialogue.setVisible(true);
}
});
However I am not able to access any of the gui component unless and until the action performed by start_button is finished. And hence I can't check the live status log by pressing view_button.
How can I do so while one action is running ? Please help. Thanks.
You need to execute your long running task in a separate Thread so you don't prevent the GUI from responding to events.
Read the section from the Swing tutorial on Concurrency for more information. Maybe a SwingWorker is the easiest approach as it creates the Thread for you and notifies you when the Thread is finished so you can update the GUI.

Make an ActionListener commit a change to a JButton before it has finished executing the entire ActionListener

In Java, I'm using an ActionListener for an array of JButtons. I would like for an earlier part of the ActionListener to set a new ImageIcon to a JButton, that change to be displayed immediately, then near the end of the ActionListener to set the JButton's ImageIcon back to null after a second long delay.
My problem is that none of the changes that happen to the JButton get displayed in the GUI window that it is set in until the ActionListener is completely finished, making the change in the JButton's ImageIcon unnoticeable. Is there any way to make an ActionListener commit a change to a JButton before it has finished executing the entire ActionListener, or should I be going about this differently?
The reason this is happening:
Swing repaints the buttons on the same thread (EDT) as the ActionListener is ran on. Hence if it is executing you ActionListener it cannot repaint since the thread is busy - as simple as that. You may have noticed that while your action listener is executing you also can't properly move your frames around etc. (GUI freezes up).
The solution:
Move heavy processing outside the EDT. It doesn't belong there anyway. As you could have guessed - use a background thread/thread pool for that. A good guide to it is Swing tutorial for concurrency
Notes:
As portrayed in the guide you do not want to modify components outside the EDT. As such the easiest strategy is to make a Runnable to execute on a background thread, start it, change the picture on the button and return without waiting for the task to finish.
public void actionPerformed(ActionEvent ae) {
Runnable task = new Runnable() {..};
executor.execute(task);
button.setIcon(newIcon);
return;
}
Note that this doesn't lock up the EDT for the task, hence allowing Swing to change the picture immediately.
This of course means that the user has no idea if the task has finished or not (And if there were any exceptions)! It is in the background after all! Hence there is an extra state of your execution: GUI is responsive and non-frozen, button is changed, but task is still running. In most applications this may be a problem (the user will spam the button or your background tasks may interleave). In that case you may want to use a SwingWorker to have a "processing" state as well.
public void actionPerformed(ActionEvent ae) {
new TaskWorker().execute();
button.setIcon(loadingIcon); //Shows loading. Maybe on button, maybe somewhere else.
return;
}
private class TaskWorker extends SwingWorker<Void, T> {
public Void doInBackground() {
//Do your task in the background here
}
protected void done() {
try {
get();
button.setIcon(doneIcon);
catch (<relevant exceptions>) {
button.setIcon(failedIcon);
}
}
}
Here done() is called on the EDT when doInBackground() is finished.
You could create a new Thread or a Thread from a Thread pool if you have one. And with that let the task work on a seperate Thread so that the ActionListener returns immediatly. And then in the other Thread you do your code and repaint the button. This is by the way a threory I'm not sure if it will work.

Thread.sleep(long mills) delaying also previous method

invalid_login_label.setVisible(false);
username_label.setVisible(false);
user_field.setVisible(false);
password_label.setVisible(false);
pass_field.setVisible(false);
access_granted_label.setVisible(true);
Thread.sleep(1000);
this.dispose();
hello world! I'm kinda newbie to java and I'm using netbeans so I have this code in my jframe. what i want is to do is after the system authenticated the user. it will remove the visibility of all fields and display "access granted" for 1000mills but it starts delaying but still not removing the visibility of fields.
Thread.sleep does just that, it causes the current thread to sleep. In this case I assume it's all done from within the context of the Event Dispatching Thread, meaning that it is unable to update the screen, as you've stopped it from processing new events, like repaint events.
Swing is a single threaded environment, that is, all interactions and modifications to the state of the UI are expected to occur from within the context of the Event Dispatching Thread. Anything which blocks this thread, such as extended I/O, long running loops or Thread.sleep will prevent the EDT from processing new events and updating the screen, making your application appear as if it has frozen...
Use a Swing Timer instead
For example...
invalid_login_label.setVisible(false);
username_label.setVisible(false);
user_field.setVisible(false);
password_label.setVisible(false);
pass_field.setVisible(false);
access_granted_label.setVisible(true);
javax.swing.Timer timer = new javax.swing.Timer(1000, new ActionListener() {
public void actionListener(ActionEvent evt) {
dispose();
}
});
timer.setRepeats(false);
timer.start();
Take a look at Concurrency in Swing and How to Use Swing Timers for more details

GUI not updating visually before running ActionEvent

To expound a little more, I have a GUI that looks like:
Then I have an action listener on the OK button that starts like:
//OK Button Action Listener
private void okButtonActionPerformed(ActionEvent e) {
//Enable/Disable Buttons
okButton.setEnabled(false);
cancelButton.setEnabled(true);
updateCheckbox.setEnabled(false);
//Move on to a series of other methods here...
Which should, in theory, make this happen:
However, instead, I get the following until ALL methods and other things connected to the OK button are completed:
This obviously can't happen, because the idea is to make the cancel button available and the OK button and several other tick-boxes unavailable for the duration of the program (Image 2), where, instead, it freezes in a half-state (Image 3). Is there any way to combat this?
Every time you execute logic from the GUI you should be using the SwingWorker in the following way:
SwingWorker myWorker= new SwingWorker<String, Void>() {
#Override
protected String doInBackground() throws Exception {
//Execute your logic
return null;
}
};
myWorker.execute();
If you want to update the GUI from inside this logic use InvokeLater:
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
//To update your GUI
}
});
With this you can be sure that both your logic and your GUI stay responsive.
Edit:
You could also use invokeAndWait if this suits your needs more. Link to related answer
//Move on to a series of other methods here...
Make sure you don't block the GUI thread (EDT) with long-running operations. Instead use SwingWorker.
Are you doing some processing on the same thread that's handling the GUI? You might want to look into a SwingWorker thread to do the heavy stuffin the background so your UI remains responsive if so.
Yup, that's because you have blocked with EDT with your other methods.
You need to use another Thread to do the work in the background otherwise the GUI will be blocked.
In Swing applications it is recommended that any long running tasks are carried out on a SwingWorker.
Take a look at the documentation for an introduction.
A SwingWorker will carry out a task that it is given and it can report back to the GUI when it is done. This will be done in a non-blocking way so that you can still use the GUI while the task it being carried out.
If you want to be able to cancel the background task you need to keep a reference to the SwingWorker so that you can call the cancel method. In this case the work method needs to be interruptable, otherwise the task cannot be cancelled.

Start a game from within an action listener

I have a Blackjack game that I've made in Java and I want to signal the start of the game by clicking a button. All my action listeners work just fine and all that, but the problem lies in that I can't figure out how to start the game without it running completely within the actionPerformed method. Obviously, a function continuously running within the actionPerformed method will effectively disable the rest of my GUI. Here's a code snippet....
go.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
// START GAME SOMEHOW but must run outside of action listener
}
});
Obviously, a function continuously running within the actionPerformed method will effectively disable the rest of my GUI.
This is a valid observation and shows that you have understand the fundamental rule when working with Swing.
Your game is most likely event driven (correct me if I'm wrong) so the action performed by the button should just set the program in a new state, waiting for further events. This is nothing that should be time consuming, and is typically done directly by the EDT.
Of course, if you want to do a fancy start-new-game animation, that needs to be performed in a separate thread, in which case you simply start the animation thread (I would recommend using a SwingWorker though) from within the actionPerformed method, and then return.
In code, I imagine it would look something like this:
go.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
// Remove the menu components
someJPanel.removeAll();
// Show the game table
someJPanel.add(new GamePanel());
someJPanel.revalidate();
someJPanel.repaint();
// done. Wait for further user actions.
}
});
You game should probably start in its own thread and manage that itself (hard to say), but to get you going you could start your game in a new "external" thread, something like this in your actionPerformed:
public void actionPerformed(ActionEvent e) {
Thread thread = new Thread("Game thread") {
public void run() {
startGame(); //or however you start your game
}
};
thread.start();
}
I believe that you want to extend javax.swing.SwingWorker.
The non-ui start-up functionality would run in doInBackground and the done method would be called when it finishes to update the ui.
There's even an example in the javadoc Class Description to update a progressbar with the status of what's happening in start-up.

Categories

Resources