I have a ListView<String> list. I can easily get the next item in the ListView with a button using this code:
public void toNext(){
list.getSelectionModel().selectNext();
}
But if I want to go through the ListView automatically with a button, it won't work. I tried the following code:
public void play(){
try {
for (int i = 0; i < list.getItems().size() ; i++) {
list.getSelectionModel().select(i);
Thread.sleep(500);
}
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
This code doesn't select the different strings in the list but just skips to the last string after the given waittime. How can I get it to select each string in the list with a delay of 500 ms?
Consider using an animation:
Timeline animation = new Timeline(
new KeyFrame(Duration.millis(500), e -> list.getSelectionModel().selectNext()));
list.getSelectionModel().select(0);
animation.setCycleCount(list.getItems().size()-1);
animation.play();
Advantages to this approach include
It is lightweight, and doesn't involve creating a new thread of execution. It simply hooks into the existing FX Application Thread and pulse mechanism.
The code is cleaner.
It has additional built-in API, so it is much easier to stop or pause the animation than it would be if you created a new thread to manage this.
The problem is, your loop completely runs in FxApplicationThread.
So, the UI won't update during the loop.
Better would be something like:
public void play(){
Thread t = new Thread(new Runnable() {
#Override
public void run() {
for (int i = 0; i < lijst.getItems().size(); i++) {
final int idx = i;
Platform.runLater(new Runnable() {
#Override
public void run() {
lijst.getSelectionModel().select(idx);
}
});
try {
Thread.sleep(1000);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
}
});
t.start();
}
You have to wrap the call that makes the UI update in the UI thread otherwise all the updates are done in one bunch at the end, hence it jumps to the end of the list.
public void play(){
try {
for (int i = 0; i < list.getItems().size() ; i++) {
final int index = i;
Platform.runLater(() -> {
list.getSelectionModel().select(index)
});
Thread.sleep(500);
}
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
Related
I want to make an option in my application that if you click a button a counter will start and it will count (like 0++) and show the number until user stops it, so far I have this non working code
if (v == btn3) {
counter = 0;
scoreText.setBackgroundColor(Color.RED);
new Thread(new Runnable() {
public void run() {
while (counter < 1000 ) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
counter++;
}
}
}).start();
scoreText.setText(Integer.toString(counter));
}
Currently when i press the button, the text field only turns red and is set to 0, but I can't make it count
In my opinion setText method is outside loop. It could update text view after loop ends. Move it somewhere inside loop.
This should work as per your needs
if (v == btn3) {
counter = 0;
scoreText.setBackgroundColor(Color.RED);
new Thread(new Runnable() {
public void run() {
while (counter < 1000 ) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
runOnUiThread(new Runnable() {
#Override
public void run() {
scoreText.setText(Integer.toString(counter));
}
});
counter++;
}
}
}).start();
}
There is a a class called CountDownTimer which provides some useful functionality. In particular it provides an onTick() method with which you could update your text field at a desired interval.
You would need to create a CountDownTimer to count indefinitely, which you can do by restarting the timer in its onFinish() method, like in this question. But if you set the CountDownTimer for long enough, you probably would never need to restart it.
Something like this...
new CountDownTimer(<REALLY_BIG_NUMBER>, 500) {
int counter = 0;
public void onTick(long millisUntilFinished) {
scoreText.setText(counter++);
}
public void onFinish() {
start();
}
}.start();
I just want to display the values of each iteration in the loop proceeded by 2 seconds delay.
when i run the code, the main thread freezes and after a while only the value of tv_t1: 19 is displayed.
can anyone please let me know what is the error or what i am missing in the below posted code?
NOTE :
I would like to do so on the main thread.
*Code
tv_t1 = (TextView) findViewById(R.id.tv_t1);
tv_t2 = (TextView) findViewById(R.id.tv_t2);
for(int i = 0; i < 20; i++) {
tv_t1.setText("t1:" + i);
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
Thread is Android's main/UI thread, and setting sleep(value) causes the main thread to sleep or
to stop doing its work for a scheduled time. If you want to update the main UI and sleep for a while, it is better to create a new thread and then create Runnable object that overrides Run(). And inside Run() you can call the function that is time consuming or also you can cause the new thread you created to sleep instead of forcing the UI thread to sleep which will cause all the main UI elements to freeze. The Handler in the below code works as intermediate between the thread and the message queue, you can also replace h1 with your tv_1 to post that as message into the queue and subsequently to update your view.
Please try the below code:
Handler h1 = new Handler();
Thread t1;
t1 = new Thread(new Runnable() {
#Override
public void run() {
// TODO Auto-generated method stub
try {
loop();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
t1.start();
protected void loop() throws InterruptedException {
// TODO Auto-generated method stub
for (i = 0; i < 20; i++) {
t1.sleep(2000);
h1.post(new Runnable() {
#Override
public void run() {
// TODO Auto-generated method stub
tv_t1.setText("tv_1: " + i);
}
});
}
}
Thread.sleep on the UI Thread is a bad idea. Use an handler:
private Handler mHandler = new Handler():
private class MyRunnable implmenets Runanble {
private final static int MAX_INT = 20;
private int mCounter = 0;
#Override
public void run() {
if (mCounter == 20) {
mHandler.removeCallbacks(this);
return;
}
mytexts.setText(" " + mCounter);
mCounter++;
mHandler.postDelayed(this, 2000);
}
}
and in your create, after you initialze your views, call
mHandler.postDelayed(new MyRunnable(), 2000);
Maybe this will work out:
for(int i = 0; i < 20; i++) {
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
tv_t1.setText("t1:" + i);
}
}, 2000);
}
And yes if you get a delay when the value of "i" was 0 then set the first text outside the loop and start the loop with i=1 or put an if-else block inside the loop
Hope it helped.
public void playPanel() throws IOException{
for(int i = 0; i<listData.size(); i++){
try {
Thread.sleep(1000L);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
ascii.setText(listData.get(i));
}
}
What I'm trying to do is play through the listData ArrayList type, which was copied from the ascii JTextArea. Its supposed to be an animation, so when they hit play the function displays the first slide, waits a second, then the next slide, etc.
When I run this the only thing that happens is a pause with nothing on the screen changing until it displays only the final slide. I'm not sure what's wrong with it
You should never call Thread.sleep(...) on the Swing event thread unless your goal is to put the entire application to sleep, rendering it useless. Instead, get rid of the for loop, and "loop" using a Swing Timer. Something like this should work, or be close to a functioning solution (caveat: code has not been compiled nor tested):
int delay = 1000;
new Timer(delay, new ActionListener() {
private int i = 0;
#Override
public void actionPerformed(ActionEvent e) {
if (i < listData.size()) {
ascii.setText(listData.get(i));
} else {
((Timer) e.getSource()).stop();
}
i++;
}
}).start();
I have a thread that call's MainActivies method that sets text value of EditText but it fails.
MyRunnable is like that:
public class MyRunnable implements Runnable {
MainActivity mController;
public SoundTrigger(MainActivity pController) {
mController = pController;
}
#Override
public void run() {
for (int i = 0; i < 10000; i++) {
if(i % 100) {
mController.triggered(i);
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
In MainActivity class my triggered method like this:
public void triggered(int nNum) {
EditTextInstance.setText(nNum+"");
}
but it fails. All i need is printing a real time data that is created by a background thread on an edittext component.
make it like this:
#Override
public void run() {
for (int i = 0; i < 10000; i++) {
if(i % 100) {
mController.runOnUiThread(new Runnable() {
public void run() {
mController.triggered(i);
}
});
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
Explanation: you were previously changing an UI element from a Non-UI thread and this is not good. This code should work, BUT it is still not the recommended way to approach the problem. You should use something like a AsyncTask where you have a template method for 'background' operation (non-ui thread operation) and the 'onPostExecute' which already executes on the UI thread.
another additional note: NEVER call Thread.sleep(); on the UI Thread (a.k.a. Main thread)
I'm trying to learn Threads in Swing.
I have a Frame with a JProgressBar (progress), five JButtons (Start, Suspend, Resume, Cancel, Close), and a JLabel (label1).
The frame opens. Only Start is enabled. Start calls my class Progressor:
Updated Again Once and For All
Progressor progressor; //declared as class variable, initialized new in constructor and again in overridden done method
Here's the ButtonListener class:
public class ButtonListener implements ActionListener{
public void actionPerformed(ActionEvent e)
{
if (e.getSource() == jbStart) {
progressor.execute();
label1.setText("Progressing ...");
jbCancel.setEnabled(true);
jbResume.setEnabled(true);
jbSuspend.setEnabled(true);
jbClose.setEnabled(true);
}
if(e.getSource() == jbCancel) {
progressor.cancel(true);
label1.setText("Progress Canceled");
}
if (e.getSource() == jbSuspend) {
label1.setText(progressor.suspendProgress());
}
if (e.getSource() == jbResume) {
label1.setText(progressor.resumeProgress());
}
if (e.getSource() == jbClose) {
dispose();
}
}
}//buttonlistener
Here's the SwingWorker class:
public class Progressor extends SwingWorker<Void, Integer> {
private volatile boolean suspend = false;
private Object lock = new Object();
#Override
protected Void doInBackground() {
for (int i = 0; i <= 10; i++) {
checkForSuspend();
try {
Thread.sleep(1000);
}
catch (InterruptedException e) {
}
publish(i);
}
return null;
}
#Override
protected void process(List<Integer> list) {
int value = list.get(list.size() - 1);
progress.setValue(value);
}
public void checkForSuspend() {
synchronized (lock) {
while (suspend) {
try {
lock.wait();
} catch (InterruptedException ie){
}
}
}
}//checkForSuspend
#Override
protected void done() {
label1.setText("All Done. Press close to exit");
progressor = new Progressor();
}
public synchronized String suspendProgress() {
suspend = true;
return "Progress suspended ...";
}
public synchronized String resumeProgress() {
synchronized (lock) {
suspend = false;
lock.notify();
return "Progress resumed ...";
}
}
}//Progressor class
Everything works except the cancel doesn't doesn't actually cancel the thread (the progress bar continues).
Should I suspend it before canceling?
This How to Pause and Resume a Thread in Java from another Thread question looks very similar to yours and has some nice examples in the answers.
As for your own code and why it does not work:
You create a new progressor on every click. You should be using and controlling one, instead of creating new ones every time.
When suspending your progressor finishes work instead of suspending. As the above question states - you should be looking at the flag at some points of your computation and acting on it. Example:
while (!cancel) {
if (!suspended) {
for (int i = 1; i <= 10; i++) {
Thread.sleep(1000);
publish(i);
}
}
}
The above code will suspend when it next reaches 10 (unless you resumed it before that), and finish when you press cancel (Cancel needs to be added as an extra flag in the obvious manner).
Your thread should run inside a while loop that looks for a boolean to change value from another object, then simply change the state with setPause(true/false) when you click the button:
while(true){
if(object_everyone_can_reference.getPause()){
Thread.sleep(1000);
}
}