Reputation: 833
first time learning coding language here.. I am struggling in many parts of Java, but I am slowly learning.
I was making an app and had to show dynamically updating text. So the below code is my attempt, it works very well (sort of). The problem is that it does not update the screen while program is running the for loop.
So when I press a button from main activity, it runs an activity called play. It has 14 text fields of 0, and should update them to 7 one by one automatically.
But after running the code below, it just shows fourteen 7s after a moment. (no 0s to start with too!) Any code for showing the screen so that I can put it under setText() ?
I am sorry if this question was asked before but I really couldn't find any answer online.
public class Play extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
setContentView(R.layout.activity_play);
Initialization();
}
public void Initialization() {
TextView[] textViews = new TextView[] {
(TextView)findViewById(R.id.player1_1),
(TextView)findViewById(R.id.player1_2),
(TextView)findViewById(R.id.player1_3),
(TextView)findViewById(R.id.player1_4),
(TextView)findViewById(R.id.player1_5),
(TextView)findViewById(R.id.player1_6),
(TextView)findViewById(R.id.player1_7),
(TextView)findViewById(R.id.player2_1),
(TextView)findViewById(R.id.player2_2),
(TextView)findViewById(R.id.player2_3),
(TextView)findViewById(R.id.player2_4),
(TextView)findViewById(R.id.player2_5),
(TextView)findViewById(R.id.player2_6),
(TextView)findViewById(R.id.player2_7)
};
for (int i = 0; i < 8; i++) {
for (int j = 0; j < 14; j++) {
textViews[j].setText(Integer.toString(i));
try {
Thread.sleep(25);
} catch (InterruptedException ex) {
Thread.currentThread().interrupt();
}
}
}
}
}
Upvotes: 3
Views: 1227
Reputation: 273380
Currently, you call Thread.sleep(25)
on the UI thread. This means that the UI thread will be blocked and will not update your text views. After all that blocking, it is finally unblocked after the for loop, so you can immediately see all the text fields updating.
For better user experience, never tell the UI thread to sleep.
You need something that will do some work after an amount of time. android.os.Handler
is a pretty good choice. It has a postDelayed
method that will do the thing you want it to do after some time.
IMO, directly calling postDelayed
can indeed do your job. But each postDelayed
call contains another, in order to update the next text view after a few seconds. That means you'll end up with 14 layers of nesting! That's not very clean code, is it?
So, let me introduce the Timer
class!
Here it is:
import android.os.Handler;
public class Timer {
private Handler handler;
private boolean paused;
private int interval;
private Runnable task = new Runnable () {
@Override
public void run() {
if (!paused) {
runnable.run ();
Timer.this.handler.postDelayed (this, interval);
}
}
};
private Runnable runnable;
public int getInterval() {
return interval;
}
public void setInterval(int interval) {
this.interval = interval;
}
public void startTimer () {
paused = false;
handler.postDelayed (task, interval);
}
public void stopTimer () {
paused = true;
}
public Timer (Runnable runnable, int interval, boolean started) {
handler = new Handler ();
this.runnable = runnable;
this.interval = interval;
if (started)
startTimer ();
}
}
This thing is pretty cool.
To meet the requirements, let's add a counter
and textViewToUpdate
to your activity class so that we know what number is it and which text view to update next. Oh, and move that textViews
variable to the class level:
int counter = 1;
int textViewToUpdate = 0;
TextView[] textViews;
And initialize the textViews
in onCreate
:
textViews = new TextView[] {
(TextView)findViewById(R.id.player1_1),
(TextView)findViewById(R.id.player1_2),
(TextView)findViewById(R.id.player1_3),
(TextView)findViewById(R.id.player1_4),
(TextView)findViewById(R.id.player1_5),
(TextView)findViewById(R.id.player1_6),
(TextView)findViewById(R.id.player1_7),
(TextView)findViewById(R.id.player2_1),
(TextView)findViewById(R.id.player2_2),
(TextView)findViewById(R.id.player2_3),
(TextView)findViewById(R.id.player2_4),
(TextView)findViewById(R.id.player2_5),
(TextView)findViewById(R.id.player2_6),
(TextView)findViewById(R.id.player2_7)
};
Create a timer in your class first:
Timer t;
In your Initialization
method, create a runnable. This runnable is going to be run every a few seconds.
Runnable r = new Runnable () {
@Override
public void run() {
PlayActivity self = PlayActivity.this;
self.textViews[self.textViewToUpdate].setText(Integer.toString(self.counter));
self.textViewToUpdate++;
if (self.textViewToUpdate == self.textViews.length) {
self.textViewToUpdate = 0;
self.counter++;
if (self.counter == 8) {
self.t.stopTimer();
}
}
}
}
Then, we create the timer and run it:
t = new Timer(r, interval, true);
You can replace interval
with any number you like, maybe 250
(0.25 seconds)?
And BOOM! You did it!
Upvotes: 2
Reputation: 7479
All of this happens way too fast for you to see. As you can easily calculate, 7x25ms = 175ms, which passes really fast and you might not even notice the numbers changing on the screen.
I'd recommend you to increase the delay to, let's say 500ms or something in that range so you might actually see the difference or changing the logic completely (you can for example use CountdownTimer with a Tick time of 500ms and change the text in onTick
method, google it).
If it's just for testing purposes, why wouldn't you add a button and then call Initialization
on that button's click? That way you can observe the values change starting from 0.
Upvotes: 0