• After 15+ years, we've made a big change: Android Forums is now Early Bird Club. Learn more here.

Apps Problems with Timers/changing a TextView's text

Mekuso

Lurker
Hi! I'm new to Android development, and although I have used Java for some school projects in the past, I consider Lua my "native language". Still, I wanted to learn to make Android apps, and started with something I thought would be quite simple, a simple night clock to my dad. Right now it can show the time, and I tried to make it update the time by using a Timer, which is activated after 5 seconds, and at that time
1. update the TextView's text to match the current time
2. start a new, identical, timer

The code looks like this at this point:

Code:
package com.tboneproductions.nightclock;

import java.util.Calendar;
import java.util.Timer;
import java.util.TimerTask;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.widget.TextView;

public class NightClockformyfatherActivity extends Activity {
	TextView tv;
	Timer t;
	
	class AwesomeTask extends TimerTask {
		private NightClockformyfatherActivity a;
		
		public AwesomeTask(NightClockformyfatherActivity a) {
			
			this.a = a;
			
		}
		
		@Override
		public void run() {
			Log.w("NightClock","timer works");
			a.setClockText(a.tv,Calendar.getInstance());
			a.t.schedule(new AwesomeTask(this.a),5000);
		}
		
		
	}
	
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
		tv = new TextView(this);
        tv.setTextSize(120);
        setClockText(tv,Calendar.getInstance());
        //tv.setText("no timer yet");
        t = new Timer();
        t.schedule(new AwesomeTask(this),5000);
        setContentView(tv);
    }
    
    public void setClockText(TextView tv,Calendar cal){
    	
    	
    	int h = cal.get(Calendar.HOUR_OF_DAY);
    	int m = cal.get(Calendar.MINUTE);
    	String hs;
    	String ms;
    	if (h < 10) {
    		hs = "0" + h;
    	} else {
    		hs = "" + h;
    	}
    	
    	if (m < 10) {
    		ms = "0" + m;
    		
    	} else {
    		ms = "" + m;
    	}
    	
        String s = hs + ":" + ms;
        
        tv.setText(s); //this line here crashes everything for some reason
    }
}

At the marked line near the bottom, the app crashes. The LogCat complains that "Only the original thread that a view hierarcy can touch its views". What does this mean, and how do I work around this? It sounds like the Timer creates a separate thread, which is unable to "touch" the TextView (even though it is given a reference to it). I guess I need to restructure my app somehow? Any ideas or explanations are very welcome :)
 
You cannot modify UI elements from any thread but the UI thread. Because TimerTask is run on a separate thread, you need to create a Handler object and post back to the UI thread, telling it to change the text.
 
You cannot modify UI elements from any thread but the UI thread. Because TimerTask is run on a separate thread, you need to create a Handler object and post back to the UI thread, telling it to change the text.

I see. I've never worked with threads before so I really don't know how to do that. Will experiment a little, but any instructions would be appreciated.

EDIT: Solved it! For those who might be interested, here's how I did it:

Code:
package com.tboneproductions.nightclock;

import java.util.Calendar;
import java.util.Timer;
import java.util.TimerTask;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.widget.TextView;

public class NightClockformyfatherActivity extends Activity {
	TextView tv;
	Timer t;
	Handler h;
	
	class AwesomeTask extends TimerTask {
		private NightClockformyfatherActivity a;
		
		public AwesomeTask(NightClockformyfatherActivity a) {
			this.a = a;
		}
		
		class RunnableLol implements Runnable {
			private NightClockformyfatherActivity a;
			
			public RunnableLol(NightClockformyfatherActivity a) {
				this.a = a;
			}
			
			@Override
			public void run() {
				this.a.setClockText(this.a.tv,Calendar.getInstance());
				this.a.t.schedule(new AwesomeTask(this.a),5000);
				
			}
			
			
		}
		
		@Override
		public void run() {
			//Log.w("NightClock","timer works");
			RunnableLol r = new RunnableLol(this.a);
			this.a.h.post(r);
			
		}
		
		
	}
	
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
		tv = new TextView(this);
        tv.setTextSize(120);
        setClockText(tv,Calendar.getInstance());
        //tv.setText("no timer yet");
        t = new Timer();
        t.schedule(new AwesomeTask(this),5000);
        setContentView(tv);
        h = new Handler();
    }
    
    public void setClockText(TextView tv,Calendar cal){
    	
    	
    	int h = cal.get(Calendar.HOUR_OF_DAY);
    	int m = cal.get(Calendar.MINUTE);
    	String hs;
    	String ms;
    	if (h < 10) {
    		hs = "0" + h;
    	} else {
    		hs = "" + h;
    	}
    	
    	if (m < 10) {
    		ms = "0" + m;
    		
    	} else {
    		ms = "" + m;
    	}
    	
        String s = hs + ":" + ms;
        
        tv.setText(s); //this line here crashes everything for some reason
    }
}
 
Back
Top Bottom