I have a JProgressBar and want to be able to see it dynamically updated. The progress bar should be able to visibly move from one position to another, not just change without the bar visibly changing (think regular loading bars).
public static void dropHPBar(int before, int after) {
Thread.currentThread.sleep(50);
while (before > after) {
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
GameMain.gW.setHP(before);
b --;
}
and from GameMain.gW:
public void setHP(int x) {
hpBar.setValue(x);
}
Why is the progress bar not being visibly updated?
Calling Thread.sleep in the EDT (event dispatch thread) prevents UI updates. You need to use Swing Worker in order to archive proper concurrency.
From the Oracle website:
Swing consists of three kinds of threads:
Initial threads, the threads that execute initial application code.
The event dispatch thread, where all event-handling code is executed. Most code that interacts with the Swing framework must also
execute on this thread.
Worker threads, also known as background threads, where time-consuming background tasks are executed.
Tasks on the event dispatch thread must finish quickly; if they don't,
unhandled events back up and the user interface becomes unresponsive.
Related
I'm trying to make my GUI wait for 2 seconds after a recent update in the graphics. It means that after I use:
boardLogo.repaint();
boardLogo.revalidate();
I want the GUI to wait before making further computations and then paint them again.
The code:
SwingUtilities.invokeLater(new Runnable() {
#SuppressWarnings("rawtypes")
#Override
public void run() {
SwingWorker swingWorkerExample = new SwingWorker(){
#Override
protected Object doInBackground() throws Exception {
return null;
}
protected void done(){
try {
Thread.sleep(2000); //wait 2 seconds.
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
swingWorkerExample.execute();
}
});
//.... more code here
boardLogo.repaint();
boardLogo.revalidate();
But when I run it - first it executes the last two lines, then waits for 2 seconds...
To be honest, my knowledge about Threads and Java Swing is not the best (especially when it comes to understanding swing worker, which I've tried to read about from Oracle site), so I would appreciate it if the answer will be detailed.
Thanks!
when I run it - first it executes the last two lines, then waits for 2 seconds ... my knowledge about Threads and Java Swing is not the best.
You say, it executes the last two lines. Ever stop to wonder what it is?
It is a thread. Threads are what execute code in a Java program, and every line that gets executed is executed by some thread.
When your code calls invokeLater(), it is submitting a task (i.e., a piece of code) to be executed by Swing's event dispatch thread (EDT); and when your code calls swingWorker.execute() it is submitting a task to be performed by one of Swing's background threads.
The whole point of having more than one thread in a program is that they can be doing different things at the same time.
In your case, you've got the EDT sleeping for two seconds while, at the same time, some other thread is calling boardLogo.repaint(); and boardLogo.revalidate();
There's a couple of important things to know about Swing and multi-threading:
All of your event handlers will be run by the EDT.
An event handler should never do anything that takes more than a small fraction of a second, because your application will not be able to respond to any other events (i.e., it will appear to be "hung") until the handler returns.
No other thread is allowed to interact with any of Swing's GUI components.
One important use-case for invokeLater() is, it's how code running in the background thread can communicate with GUI components. invokeLater() submits a task (i.e., a piece of code) that will be run in the EDT.
The main use-case for SwingWorker is pretty much the opposite of that: It's how an event handler, running in the EDT, can kick off a task that is going to take more than a small fraction of a second to complete.
You can learn about this stuff by working your way through the Swing Concurrency tutorial: https://docs.oracle.com/javase/tutorial/uiswing/concurrency/
My program is to ping a host infinitely and display each ping result on the GUI as RED/GREEN. Here, 'res' is a label that changes its text and background based on the ping result.
The issue is that when i use while(), the GUI becomes unresponsive and the colour of the label does not change, while the ping service continues. How do i solve this issue??
while (true) {
try {
boolean status = InetAddress.getByName(host).isReachable(timeOut);
if (status == true) {
res.setText("ON");
res.setBackground(Color.GREEN);
} else {
res.setText("OFF");
res.setBackground(Color.RED);
}
} catch (IOException ex) {
}
Start by taking a look at Concurrency in Swing
Essentially, from your description, you are blocking the Event Dispatching Thread, preventing it from process new events, including paint events.
Swing is also NOT thread safe, this means you should not update the UI from outside of the context of the EDT.
In this case, you could use a SwingWorker to perform the physical "ping" from within the doInBackground method and make use of the publish/process methods to update the UI.
See Worker Threads and SwingWorker for more details
I im creating a simple testing app that runs a check every hour on the selected directory/s using thread.sleep() through JFileChooser. But when i select the directory and the method runs the ui panel goes grey and the swing bits disappear. The thread seems to be putting the ui to sleep as well as the method its calling.
if (option == JFileChooser.APPROVE_OPTION) {
selectedDirectory = chooser.getSelectedFiles();
try {
while (true) {
runCheck(selectedDirectory);
Thread.sleep(1000*5);//1000 is 1 second
}
} catch (InterruptedException e1) {
Thread.currentThread().interrupt();
e1.printStackTrace();
}
}
Im looking for a way around this issue so that i can print the results of the checks being run in the ui .setText(result)
You are correct about the code putting the UI to sleep. Since sleep is called on the Event Dispatch Thread (the thread responsible for running the gui) the UI stops processing events and 'goes to sleep'.
I think what you want is a javax.swing.Timer.
Timer t = new Timer(1000 * 5, new ActionListener() {
public void actionPerformed(ActionEvent e) {
// do your reoccuring task
}
});
This will cause your reoccurring task to be performed off of the EDT, and thus it wont leave your ui unresponsive.
If the code you have posted runs on the EventDispatchThread, then there is no way Swing can redraw the GUI. You're blocking (sleeping in) the thread that's supposed to handle that!
This is because you are running you check in the main GUI thread and are using an infinite loop. This check is a background task and should be executed in it's own thread so that the GUI can still receive and react to input by the user.
You also do not need to write your own implementation, Java has a Timer object.
Edit: There is also a Swing specific Timer object. This will have the action occur in the GUI thread, so if your task is long, it can cause the GUI to still lock up while the action is occurring (but not while it is waiting).
I im creating a simple testing app that runs a check every hour on the selected directory/s using thread.sleep() through JFileChooser. But when i select the directory and the method runs the ui panel goes grey and the swing bits disappear. The thread seems to be putting the ui to sleep as well as the method its calling.
if (option == JFileChooser.APPROVE_OPTION) {
selectedDirectory = chooser.getSelectedFiles();
try {
while (true) {
runCheck(selectedDirectory);
Thread.sleep(1000*5);//1000 is 1 second
}
} catch (InterruptedException e1) {
Thread.currentThread().interrupt();
e1.printStackTrace();
}
}
Im looking for a way around this issue so that i can print the results of the checks being run in the ui .setText(result)
You are correct about the code putting the UI to sleep. Since sleep is called on the Event Dispatch Thread (the thread responsible for running the gui) the UI stops processing events and 'goes to sleep'.
I think what you want is a javax.swing.Timer.
Timer t = new Timer(1000 * 5, new ActionListener() {
public void actionPerformed(ActionEvent e) {
// do your reoccuring task
}
});
This will cause your reoccurring task to be performed off of the EDT, and thus it wont leave your ui unresponsive.
If the code you have posted runs on the EventDispatchThread, then there is no way Swing can redraw the GUI. You're blocking (sleeping in) the thread that's supposed to handle that!
This is because you are running you check in the main GUI thread and are using an infinite loop. This check is a background task and should be executed in it's own thread so that the GUI can still receive and react to input by the user.
You also do not need to write your own implementation, Java has a Timer object.
Edit: There is also a Swing specific Timer object. This will have the action occur in the GUI thread, so if your task is long, it can cause the GUI to still lock up while the action is occurring (but not while it is waiting).
why in my code, the TextView does not take except the last count.
the result just is: counter= 4
int i =0;
while (i< 5) {
try {
Thread.sleep((i*1000));
mText.setText("counter"+ i);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// mText does not show
mText.setText("counter= "+ i);
i = i +1;
}
You are blocking the UI thread when your app sleeps, so the screen is not redrawn, hence you don't see the text changes until your loop ends.
The UI thread
When an application is launched, the system creates a thread called
"main" for the application. The main thread, also called the UI
thread, is very important because it is in charge of dispatching the
events to the appropriate widgets, including drawing events. It is
also the thread where your application interacts with running
components of the Android UI toolkit.
For instance, if you touch the a button on screen, the UI thread
dispatches the touch event to the widget, which in turn sets its
pressed state and posts an invalidate request to the event queue. The
UI thread dequeues the request and notifies the widget to redraw
itself.
This single-thread model can yield poor performance unless your
application is implemented properly. Specifically, if everything is
happening in a single thread, performing long operations such as
network access or database queries on the UI thread will block the
whole user interface. No event can be dispatched, including drawing
events, while the long operation is underway. From the user's
perspective, the application appears hung. Even worse, if the UI
thread is blocked for more than a few seconds (about 5 seconds
currently) the user is presented with the infamous "application not
responding" (ANR) dialog.
If you want to see how bad this can look, write a simple application
with a button that invokes Thread.sleep(2000) in its OnClickListener.
The button will remain in its pressed state for about 2 seconds before
going back to its normal state. When this happens, it is very easy for
the user to perceive the application as slow.
To summarize, it's vital to the responsiveness of your application's
UI to keep the UI thread unblocked. If you have long operations to
perform, you should make sure to do them in extra threads (background
or worker threads).
More info:
http://developer.android.com/resources/articles/painless-threading.html
That is the problem. And AsyncTask is (one) solution:
AsyncTask
AsyncTask enables proper and easy use of the UI thread. This class
allows to perform background operations and publish results on the UI
thread without having to manipulate threads and/or handlers.
An asynchronous task is defined by a computation that runs on a
background thread and whose result is published on the UI thread.
http://developer.android.com/reference/android/os/AsyncTask.html
First, it would be a good idea to change the text before sleeping.
Second, if you do this in a separate thread, you have, nevertheless, to manipulate a widget in the ui thread. Try this :
instead of
mText.setText("counter"+ i);
try
runOnUIThread( new Runnable() {
public void run() {
mText.setText("counter"+ i);
}
});
Regards,
Stéphane
"while (i <= 5)" will make it go on till five.
Another solution is to make "i" 1 to start with, instead of 0.
Does this help?
Use a for loop (inside a non-UI thread):
for (int i = 0; i < 5; i++}{
try {
Thread.sleep((i*1000));
} catch (InterruptedException e) {
e.printStackTrace();
}
runOnUIThread(new Runnable() {
public void run() {
mText.setText("counter"+ i);
}
});
}
Many answers here gave you all the information you need to achieve what you want but it may be difficult to understand.
I will propose you below a new way to achieve it, maybe easier. But I want to underline that you should understand what people wrote, my previuos post and the one from #LAS_VEGAS at least.
Here is something easy : use a handler.
add a data member like
Handler handler = new Handler();
then on click :
for (int i = 0; i < 5; i++}{
handler.postDelayed( new Runnable() {
public void run() {
mText.setText("counter"+ i);
}
}, i * 1000 );
}
I don't have access to my very own computer right now and fear this may not compile due to a non final variable used inside a annonymous inner class. So you could also try to build a named inner class like this in your main java class :
public class ButtonTimerThread implements Runnable
{
privqte int i =0;
public ButtonTimerThread( int i)
{
this.i = i;
}//cons
public void run()
{
mText.setText("counter"+ i);
}//met
}//class
qnd then you click listener method would look like :
for (int i = 0; i < 5; i++}{
handler.postDelayed( new ButtonTimerThread( i ), i * 1000 );
}
As your handler will be created by the UI thread, it will be able to update UI. The handler offers the advantage to have a delay mechanism that doesn't block the UI thread that is reacting to your click action.
I hope this helps,
Stéphane
Generally, good solutions to problems with sleep() involve stopping using sleep(). Blocking the UI thread is always a very bad idea. It makes you application's UI non-responsive.
In your case you want an event to run every second. The correct way to implement that is with a timer.