23

I have looked at every discussion and thread I can find on getting this to work but it is not. I have a simple timer that updates a text view (mTimeTextField in the example below). The mUpdateTimeTask run method is being executed correctly (every second) but the UI/text field is not being updated.

I have code based on the info found here:

http://android-developers.blogspot.com/2007/11/stitch-in-time.html http://developer.android.com/resources/articles/timed-ui-updates.html

Here is the code:

package com.something.handlertest;

import com.something.handlertest.R;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.SystemClock;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

public class Test extends Activity {

    private Handler mHandler = new Handler(); 
    private int labelNo    = 0;
    private long currTime  = 0L;
    private long mStartTime = 0L;
    TextView mTimeTextField;

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        mTimeTextField = (TextView)findViewById(R.id.timeTextFieldl);

        Button startButton = (Button)findViewById(R.id.start_button);
        startButton.setOnClickListener(new View.OnClickListener() {

            public void onClick(View v) {
                   if (mStartTime == 0L) {
                        mStartTime = System.currentTimeMillis();
                        mHandler.removeCallbacks(mUpdateTimeTask);
                        mHandler.postDelayed(mUpdateTimeTask, 100);
                   }
            }
        });
    }

    private Runnable mUpdateTimeTask = new Runnable() {
           public void run() {
               final long start = mStartTime;
               //long millis = SystemClock.uptimeMillis() - start;
               long millis = SystemClock.uptimeMillis();
               int seconds = (int) (millis / 1000);
               int minutes = seconds / 60;
               seconds     = seconds % 60;

               //setContentView(mTimeTextField);  This will blow up if I use it

               if (seconds < 10) {
                   mTimeTextField.setText("" + minutes + ":0" + seconds);
               } else {
                   mTimeTextField.setText("" + minutes + ":" + seconds);            
               }

               //mHandler.postAtTime(this,
                   //    start + (((minutes * 60) + seconds + 1) * 1000));

               mHandler.postAtTime(this, 1000);
           }
        };

}

Per some suggestions, I have tried adding:

setContentView(mTimeLabel);

But this will crash complain about the view not having a parent. FYI, I do have a:

setContentView(R.layout.main);

call in my onCreate().

Dave Newton
  • 158,873
  • 26
  • 254
  • 302
bdelliott
  • 463
  • 1
  • 7
  • 12
  • Have you tried runOnUiThread as mentioned here? http://stackoverflow.com/questions/3741458/change-imageview-after-few-seconds/3741643#3741643 – Mathias Conradt Sep 22 '10 at 01:00
  • Setting contentView to a textview is definitely not right. Only setting the content view to a layout makes sense, as you already did onCreate. – Mathias Conradt Sep 22 '10 at 01:11

6 Answers6

19

Replace

mHandler.postAtTime(this, 1000);

with

mHandler.postDelayed(this, 1000);
Fedor
  • 43,261
  • 10
  • 79
  • 89
  • It works!!!! I just made the change you suggested above. I am not sure why the example code by Dick Wall has the use of postAtTime but I am just thankful to have it working. Fedor -- THANKS SO MUCH!!!! – bdelliott Sep 22 '10 at 15:23
  • Well, it might work. But is that what you really want is the question. If you want something to happen at precisely the exact time period between two ticks, postDelayed might not be what you want. postDelayed will run the runnable "x" ms from the time instant the statement is executed. while postAtTime is different. – VJ Vélan Solutions Nov 24 '11 at 09:01
5
  1. You need to do UI updates in the UI thread.
  2. You need to create a Handler in the UI thread to run tasks in the UI thread.

    public class MainActivity extends AppCompatActivity
        private Handler mHandler;
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            mHandler = new Handler(); // This to create the Handler in the UI thread
            // ...
        }
    
david m lee
  • 2,547
  • 26
  • 14
4

This might be more along the lines of what you are looking for:

private Runnable mUpdateTimeTask = new Runnable() {
       public void run() {

           final long start = mStartTime;
           long elapseTime = System.currentTimeMillis() - start;
           int seconds = (int) (elapseTime / 1000);
           int minutes = seconds / 60;
           seconds     = seconds % 60;

           if (seconds < 10) {
               mTimeTextField.setText("" + minutes + ":0" + seconds);
           } else {
               mTimeTextField.setText("" + minutes + ":" + seconds);            
           }

           // add a delay to adjust for computation time
           long delay = (1000 - (elapseTime%1000));

           mHandler.postDelayed(this, delay);
       }
    };

I found this to be a good way to add a time to video's

Laith Alnagem
  • 654
  • 6
  • 13
1

view can only be displayed on UI thread. you have to start the runnable using ru

0
if (seconds < 10) {
    mTimeTextField.setText("" + minutes + ":0" + seconds);
} else {
    mTimeTextField.setText("" + minutes + ":" + seconds);            
}

shorter is:

mTimeTextField.setText(minutes + ":" + (seconds < 10 ? "0" : "") + seconds);

but better way is using String.format^

mTimeTextField.setText(String.format("%d:%s%d", minutes, (seconds < 10 ? "0" : ""), seconds);
bask0xff
  • 145
  • 4
  • 13
0

Please ensure R.id.timeTextFieldl is a correct id.

Fedor
  • 43,261
  • 10
  • 79
  • 89