I have a for loop in which I would like to call the setText method. This works without any errors except for the fact that the text does not change until the script is done running. What I want it to do is change the code every time I call the setText method. I attached my code below, it should be pretty strait forward.
code:
package com.example.zoe.andone;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.MenuItem;
import android.view.View;
import android.widget.TextView;
import java.util.concurrent.TimeUnit;
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void startLoop(View view) {
String string = getString(R.string.hello);
((TextView)findViewById(R.id.hello)).setText("starting...");
for (int i = 0; i < 10; i++) {
String updated = Integer.toString(i);
((TextView)findViewById(R.id.hello)).setText(updated);
try {
Thread.sleep(250);
} catch(InterruptedException ex) {
Thread.currentThread().interrupt();
}
}
((TextView)findViewById(R.id.hello)).setText("done!");
}
}
That's what is called "dropping frames".
It's time to learn!
Before going to the answer, let's explain what's going on here.
You probably already know that android has a frame-rate of 60fps (60 frames per sec), which means that every 1/60 sec the screen is rendered and displayed to the user (in order to provide the best definition). It does that by running some rendering code on your app (and everywhere else where there's something to be rendered) every 1/60 sec to compute how the displayed screen should look like at this particular ms (it's is called a frame).
However, the code doing that is running on the UI thread, and if you're doing anything when the rendering tick kicks in, the android framework simply drops that frame (it won't compute it).
In your case Thread.sleep(250); is what causes your frames to be dropped cause it's holding the UI thread for 250 ms in every iteration of your for loop (so it's 250 ms * (10 - 1), that's many frames).
Dropping a frame means seeing no update on the UI.
Solution
Instead of using Thread.sleep(250); and dropping frames, you should schedule your update tasks to run every 250 ms. That's it.
private int i = 0; // Does the trick!
public void startLoop(View view) {
String string = getString(R.string.hello);
((TextView)findViewById(R.id.hello)).setText("starting...");
final Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
if (i < 10) {
String updated = Integer.toString(i);
i++;
updateHelloOnUiThread(updated);
} else {
updateHelloOnUiThread("done!");
handler.removeCallbacksAndMessages(null);
i = 0; // For future usages
}
}
},250);
}
private void updateHelloOnUiThread(final String text) {
MainActivity.this.runOnUiThread(new Runnable() {
#Override
public void run() {
((TextView)findViewById(R.id.hello)).setText(text);
}
});
}
Further reading
Android UI : Fixing skipped frames
High Performance Android Apps, Chapter 4. Screen and UI Performance
this.runOnUiThread(new Runnable() {
#Override
public void run() {
int globalTimer = 0;
int limitTimer = 10;
int i = 0;
// create Timer
Timer timer = new Timer();
timer.schedule(new TimerTask() {
#Override
public void run() {
globalTimer++;
//run your code
((TextView)findViewById(R.id.hello)).setText(i+"");
i++;
//check if globalTimer is equal to limitTimer
if (globalTimer == limitTimer) {
timer.cancel(); // cancel the Timer
}
}
}, 0, 250);
}
});
The problem is, you're calling Thread.sleep on the UI thread.
If the thread is sleeping, it can't update the UI(what's displayed on the screen).
Related
I'm using this code to change between different images, but the only image that is visualized is the last one. The TextView "vista_aterrizaje" does not show the expected value in each loop.
I have tried to use invalidate() method before the sleep() one, to try to visualize it in each different loop of the for. But it does not work.
establecerImagen() method is used for changing the imageView.
for (int i = 0; i < 15; i++) {
cur_aterrizaje = aterrizajes[rand.nextInt(aterrizajes.length)];
vista_aterrizaje.setText(cur_aterrizaje);
establecerImagen(cur_aterrizaje);
vista_aterrizaje.();
mapa.invalidate();
try{
Thread.sleep(50);
} catch (Exception e) {
e.printStackTrace();
}
}
I think that the problem is in the sleep() method but I don't know how to wait a few seconds after every change of the views in order to visualize every change.
Try using CountDownTimer.
// here interval 1000 stands for 1sec,
//so onTick() will be called 15 times, each call after 1 sec interval. so you can set your own values as per your need.
int i=0;
long interval = 1000;
new CountDownTimer((15 * interval), interval){
#Override
public void onTick(long l) {
vista_aterrizaje.setText(i+"");
establecerImagen(cur_aterrizaje);
i++;
}
#Override
public void onFinish() {
}
}.start();
The parameter to sleep() is in milliseconds. So if you want to show each image for 1 second, the parameter should be 1000.
A better way to do it would be like this:
handler=new Handler();
Runnable r=new Runnable() {
public void run() {
//display your image here -- it will be displayed after a 1-second delay
}
};
handler.postDelayed(r, 1000);
I've been trying to run a countdown timer in a for loop but whenever I do, instead of the countdown timer keeping its timing and carrying out some code in the onTick() method, all the code in the onTick() method gets carried out instantly without the delay. Any way to fix this, or an alternative approach to what i'm trying to achieve?
Here's my code :
public void runFromString(String theString, final TextView textView) {
for (final Lyric lyric : lyricArrayList(theString)) {
final int[] j = {0};
new CountDownTimer(lyric.timeInMillis(), 1000) {
#Override
public void onTick(long l) {
if (j[0] == 0) {
textView.setText(lyric.getLyricText());
j[0] = 1;
}
}
#Override
public void onFinish() {
textView.setText("");
j[0] = 0;
}
}.start();
}
}
I am not 100% clear what you are trying to achieve but your onTick() method is supposed to run every second until it reaches lyric.timeinMillis(). Based on your condition it runs only the first time (since you change the first cell of the vector to 1).
atm i have a problem with my actual Android application.
For explanation:
At first i want to show a Text in a TextView Char by Char. This is my actual Code for this
tvIntro.setText("");
final Handler textHandler = new Handler();
for(int i=0; i<intro.length();i++){
final int finalCount = i;
textHandler.postDelayed(new Runnable() {
#Override
public void run() {
tvIntro.setText(tvIntro.getText() + (intro.charAt(finalCount)+""));
}
}, 150 * i);
}
After the whole text is displayed, i want to play a sound and continuously change the Color of the Screen for 5 Seconds. For this, my code is:
myBackground.setBackgroundColor(Color.RED);// set initial colour
final Thread blink = new Thread(new Runnable() {
public void run() {
while (getRunning()) {
try {
Thread.sleep(100);
if(start[0] !=1){
mp.start();
start[0] = 1;
}
}
catch (InterruptedException e) {
e.printStackTrace();
}
updateColor(myBackground);
whichColor = !whichColor;
}
}
});
private void updateColor(final RelativeLayout myBackground) {
runOnUiThread(new Runnable() {
#Override
public void run() {
if (whichColor)
myBackground.setBackgroundColor(Color.RED);
else
myBackground.setBackgroundColor(Color.GREEN);
}
});
}
All the functions are working, but i want too finish the first handler, before the second handler is executed. Furthermore the Second handler should stop after x seconds.
I have some problems understanding how the handler and the Threads work.
Would be nice if someone of you have a solution for me.
To delay the performing of tasks until a specified thread (or threads) finishes, add this line immediately after the thread you wish to wait on:
myThread.join();
And then immediately follow it with the code you wish to run after it finishes.
For your second problem, you can set a variable to be the value (in milliseconds) for the amount of time you want to wait before ending the thread, and then decrease that value by 100 (or whatever amount you choose to tell it to sleep for) each time the code runs. Have a check for the value to be less than or equal to zero, and then if that condition returns true, end the thread with an interrupt. So basically:
long timeToRun = 5000, sleepTime = 100;
// Your code here...
Thread.sleep(sleepTime);
timeToRun -= sleepTime;
if(timeToRun <= 0) {
myThread.interrupt();
}
There are likely more graceful ways to accomplish this, but at the very least this should solve your problems.
I'm trying to add a string to ListView in JavaFX whilst processing, but it keeps freezing my GUI.
I've tried the following threading - but can't seem to get it to work for a ListView.
Does anybody know how/have an example of how I can update a ListView in JavaFX whilst processing data?
new Thread(new Runnable() {
#Override public void run() {
for (int i=1; i<=1000000; i++) {
final int counter = i;
Platform.runLater(new Runnable() {
#Override public void run() {
recentList.getItems().add(Integer.toString(counter));
}
});
}
}}).start();
Using Platform.runLater() is the correct way to go.
You could, also, store the String result from Integer.toString(counter) in the background thread (not the UI one). By the way, you should use String.valueOf (there is a thread on StackOverflow which talks about it).
I assume your UI is freezing because of the execution speed of the (very simple) loop.
You should also have a look at Concurrency in JavaFX
You can do the animation / ui update after adding those strings in the list or use Platform.runLater only once (not advisable):
Platform.runLater(new Runnable() {
for (int i=1; i<=1000000; i++) {
final int counter = i;
#Override public void run() {
recentList.getItems().add(Integer.toString(counter));
}
}
});
Your GUI hangs because you are blocking the JavaFX application thread by calling Platform.runLater() continuously in your Thread.
You can perform a quick fix by adding a sleep statement inside your for-loop to avoid it.
for (int i=1; i<=1000000; i++) {
final int counter = i;
Platform.runLater(new Runnable() {
#Override public void run() {
recentList.getItems().add(Integer.toString(counter));
}
});
// Add Sleep Time
Thread.sleep(some milli seconds);
}
To use a more proper and advisable way, use an AnimationTimer, as shown in
JavaFX - Concurrency and updating label
I'm attempting to make a basic app and for part of that app I needed a delay after a button is pressed to allow for a text view to show a word for a second, and then change to another word and then have that display for a second and show another word. S0 3 words, 2 delays. i achieve the first delay using postDelayed() which works fine.
#Override
public void onClick(View view) {
// TODO Auto-generated method stub
switch(view.getId())
{
case R.id.bStart:
display.setTextColor(Color.YELLOW);
display.setText("READY");
start.setText("Restart");
timer.postDelayed(makePattern, 1000);
break;
}
}
The app displays "READY" for one second then opens a new thread and then starts the makePattern runnable which is where the problem is.
makePattern = new Runnable()
{
#Override
public void run() {
display.setTextColor(Color.GREEN);
display.setText("GO!");
for(int x = 0; x < level; x++) {
pattern[x] = crazy.nextInt(4);
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
display.setText("IT WORKED");
}
}
};
Here it should be displaying "GO!", pausing the thread for a second and then displaying "IT WORKED". However when I run it on both my phone and the emulator it displays "READY" for 2 seconds and then skips to "IT WORKED" either displaying "GO!" for such a short amount of time that it can't be seen of skipping over it all together. I have already tried moving the setText("GO!") inside of the catch and that didn't work either. Any Help would be much appreciated.
THANK YOU!
You could create a second Runnable and schedule that using postDelayed, like you're doing for the first. In the same place you schedule the first for 1 second later, you could schedule the second to occur 2 seconds later, which would be 1 second after the first. Or, you could schedule it from within the first runnable for 1-second later. See the "OPTION" comments in the code below.
#Override
public void onClick(View view) {
// TODO Auto-generated method stub
switch(view.getId())
{
case R.id.bStart:
display.setTextColor(Color.YELLOW);
display.setText("READY");
start.setText("Restart");
timer.postDelayed(makePattern, 1000);
timer.postDelayed(showItWorked, 2000); // <-- OPTION 1
break;
}
}
makePattern = new Runnable()
{
#Override
public void run() {
display.setTextColor(Color.GREEN);
display.setText("GO!");
for(int x = 0; x < level; x++) {
pattern[x] = crazy.nextInt(4);
}
timer.postDelayed(showItWorked, 1000); // <-- OPTION 2
}
};
showItWorked = new Runnable()
{
#Override
public void run() {
display.setText("IT WORKED");
}
};
And just to clarify what's going wrong in your existing code...
As Mael mentioned in his comment, your use of Thread.sleep is blocking the UI thread. Just because the the code is within a Runnable does not necessarily mean that it's running on another thread. From the Android documentation for Handler.postDelayed (I'm assuming "timer" is a Handler): "Causes the Runnable r to be added to the message queue, to be run after the specified amount of time elapses. The runnable will be run on the thread to which this handler is attached."