Seung
Seung

Reputation: 833

Android studio Java update text in the loop

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

Answers (2)

Sweeper
Sweeper

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

Vucko
Vucko

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

Related Questions