So I have an android app where I want to decrement a value and display it in a textview. I start from 1000 and decrement it by 1 from 1 to 1 seconds. This acts as a score that decreases in time if you stay more on the level. This is my code:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.game);
timeText = (TextView) findViewById(R.id.textView5);
runOnUiThread(new Runnable() {
#Override
public void run() {
for(time=1000;time>=0;time--){
try {
TimeUnit.SECONDS.sleep(1);
timeText.setText(String.valueOf(time));
System.out.println(time);
}
catch(Exception e)
{
System.out.println("Error on delay");
}
}}
});
}
My error is that whenever I enter this activity, the screen turns black. The console is printing the values from second to second and if i comment the "for" the textView displays properly the value 1000 (if i declare int time = 1000 of course). I am really not sure what the problem is here. Does somebody know what i'm doing wrong?
You can't just loop on the UI thread like that. Inside Android there's a message loop on the UI thread. When it needs to draw, it sends a message to that message loop. Until you process that message the changes won't appear on screen. And to process a message, your code must finish and return to the message loop.
If you want to do this, you can't use a for loop on the UI thread. You need to send individual messages to a Handler for each draw you want to make.
You are pausing the UI in that for loop.
To achieve what you want, either use a Handler
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
//update textview here
}
},1000);
OR
use a Timer
new Timer().scheduleAtFixedRate(new TimerTask() {
#Override
public void run() {
//update
}
},1000,0);
I don't know much about handlers but you don't need a for loop in timer.
In your case:
long time=1000;
new Timer().scheduleAtFixedRate(new TimerTask() {
#Override
public void run() {
runOnUiThread(new Runnable(){
timeText.setText(String.valueOf(time))
time--;
});
}
},1000,0).start();
Good luck
Related
Currently in my Android Game there is a simple thread that runs, decreasing a horizontal progressbar by 1 every X milliseconds. I'm trying to implement a method so when the progressbar hits 0, a TextView changes to "Game Over". The app crashes whenever this function is called in this way. I have also initialized the variable correctly so the method should have no trouble seeing this TextView.
public class MyThread extends Thread{
#Override
public void run(){
while (counter > 0 && keepRunning){
counter = counter - 1;
android.os.SystemClock.sleep(calculateSleepTick());
mHandler.post(new Runnable() {
#Override
public void run() {
progressTest.setProgress(counter);
}
});
}
isGameOver();
}
}
public void isGameOver(){
scoreText.setText("Game Over");
}
Your isGameOver function sets the text of a UI element. You can't call functions of UI elements on a thread other than main. It needs to be posted to the UI thread to do that.
You cannot update UI in non UI threads. Call isGameOver() this way.
runOnUiThread(new Runnable() {
#Override
public void run() {
isGameOver();
}
});
I'm creating a button to stop a light sensor after grabbing two equal light values for a duration of 2 seconds using the handler's delay function. It works fine at first but when I press the button again, the delay seems to get shorter and shorter, and eventually doesn't happen at all.
stateLx.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
onResume();
stateLx.setEnabled(false);
doub2x.setText(Double.toString(0));
doub1x.setText(Double.toString(0));
//Stabilization Handlers
m_handler = new Handler();
m_handlerTask = new Runnable() {
public void run() {
doub1 = movingValue[0];
doub1x.setText(Double.toString(doub1));
m_handler.postDelayed(this, 1000);
}
};
m2_handler = new Handler();
m2_handlerTask = new Runnable() {
public void run() {
doub2 = movingValue[0];
doub2x.setText(Double.toString(doub2));
m2_handler.postDelayed(this, 2000);
if (doub1 != doub2) {
instructions.setText("unequal");
} else {
instructions.setText("equal");
getInitialLightValue();
reCalculateInitial();
beerLambert(volValues, lxValuesNew);
}
}
};
//Handlers for stabilizer
m_handler.postDelayed(m_handlerTask, 1000);
m2_handler.postDelayed(m2_handlerTask, 3000);
lame.setText(Double.toString(absorbValues[0]));
stateLx.setEnabled(true);
}
});
I've tried putting the handlers both in and out of the onclick function, as well as including the onResume() function at the beginning of the button press, but I'm having no luck. Is there something wrong with my code?
You keep recreating your Handlers and Runnables. This is part of the problem, you should check to see if you are already polling before starting again.
Also, this is a side note -- but it's odd to call lifecycle methods, like onResume() yourself, since it calls up to the super.
I've seen some similar questions and got some information but they stop shy of telling me enough to get it working.
What I'm trying to do is make a simple rhythm game where the player taps a button at regular intervals (ie. beats). I wanted to set up a way of signalling when to tap by having the button change colour, and since this would be a repeated task at regular intervals I want to use a timer object with a schedule method.
But when I try calling on this method it tells me that I can't change the UI in a non UI thread. I've tried a few ways to write a method in the main thread that I can call from the timer object but I get the same error every time. I'm assuming that I just have the wrong idea about what counts as being from the UI thread, so I was hoping someone could clear it up.
Here's a snippet of one way I tried it, just to show what my code looks like:
OnClickListener clickButton = new View.OnClickListener() {
public void onClick(View v) {
if (startBeat == 0){
startBeat = System.nanoTime();
timerStart.scheduleAtFixedRate((new TimerTask()
{
public void run()
{
flashButton();
}
}), 0, beatTime);
timerEnd.schedule(new TimerTask()
{
public void run()
{
unflashButton();
}
}, beatTolerance*2, beatTime);
return;
}
};
public void flashButton(){
beatPrompt.setBackgroundColor(getResources().getColor(R.color.primary1transparent_very));
}
public void unflashButton(){
beatPrompt.setBackgroundColor(getResources().getColor(R.color.primary1));
}
To be clear, this is all contained within my MainActivity class along with the OnCreate class.
if you are in an activity all you need to do is use runOnUiThread() and then place the code to change the ui element in there
public void flashButton(){
runOnUiThread(new Runnable() {
#Override
public void run() {
beatPrompt.setBackgroundColor(getResources().getColor(R.color.primary1transparent_very));
}
});
}
You cannot, under any circumstances, touch a UI object from a non UI thread.
You can accomplish your intent using Handler.sendMessageDelayed
UI can only be touched by the main thread. You should post the actions you are performing on the ui thread via handler or via runOnUiThread
Try something similar to this
timerStart.scheduleAtFixedRate((new TimerTask()
{
public void run()
{
//replace MainActivity with your activity
//if inside a fragment use getActivity()
MainActivity.this.runOnUiThread(new Runnable() {
public void run() {
flashButton();
}
});
}
}), 0, beatTime);
If you are in an Activity you could surround flashButton() with an runOnUiThread.
...
runOnUiThread(new Runnable(){
public void run(){
flashButton();
}
});
...
use android.os.Handler Class. Change your code as follows:
private Handler handler=new Handler();
public void flashButton(){
handler.post(new Runnable(){
public void run(){
beatPrompt.setBackgroundColor(getResources().getColor(R.color.primary1transparent_very));
}
});
}
public void unflashButton(){
handler.post(new Runnable(){
public void run(){
beatPrompt.setBackgroundColor(getResources().getColor(R.color.primary1));
}
});
}
Ok - I know there has got to be a simple solution to this but for the life of me I can't figure it out.
Programming a very basic android activity to simply iterate through 0-99. I have a textview that I want to display the count. What happens is that it simply stays blank until the end and then shows the ending count (99).
Not sure if a textview is the right way to display or what the answer is. Any help is greatly appreciated. Thanks in advance
Try using code like this in onCreate (where number is defined as a field):
textView.post(new Runnable() {
#Override
public void run() {
number++;
textView.setText("counting: " + number);
if (number < 100) {
textView.postDelayed(this, 50);
}
}
});
Edit: code was edited as View classes have post and postDelayed, which propagates call to Handler instance they have internally.
You need to read a bit about Handler class.
Warning: this code leaks Activity for the time of approximatelly 5 seconds and should not be used directly in production code. You need to remove Runnable from the message queue at the appropriate time (maybe in onDestroy, but it depends on your needs).
View.removeCallbacks for anti-memory-leak.
My guess is that your onCreate() has code like this:
for (int i=0;i<100;i++) {
tv.setText(String.valueOf(i));
Thread.sleep(100); // or something to delay for a bit
}
That will give you the output that you are describing.
As with many GUI frameworks, Android's UI is event-driven. Calling setText() does not update the screen. Rather, it puts a message on a queue, asking for the screen to be updated. That queue is processed by the main application thread... the same thread that is calling onCreate() in the first place. Hence, what you are doing is queuing up 100 setText() calls, none of which will be processed until your loop is complete. Applying the 100 of them takes very little time, giving the visual result of only seeing the last change.
User a timer scheduled at a fixed rate. Increment a counter every second. Set the text on the UI thread. cancel the timer when required.
public class MainActivity extends Activity {
TextView _tv;
Timer _t;
int _count=0;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
_tv = (TextView) findViewById( R.id.textView1 );
_t = new Timer();
_tv.setText(""+_count);
_t.scheduleAtFixedRate( new TimerTask() {
#Override
public void run() {
_count++;
runOnUiThread(new Runnable() //run on ui thread
{
public void run()
{
_tv.setText(""+_count);
if(_count==99)
{
_t.cancel();
}
}
});
}
}, 1000, 1000 );
}
#Override
protected void onDestroy() {
// TODO Auto-generated method stub
super.onDestroy();
_t.cancel();
}
}
Use a countdown timer, in below code, onTick() will get called every second, here you can display/update your number each second.
set interval according to your need. Its in mili seconds.
public class TimerActivity extends Activity {
private final long startTime = 100 * 1000;
private final long interval = 1 * 1000;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_timer);
countDownTimer = new MyCountDownTimer(startTime, interval);
countDownTimer.start();
public class MyCountDownTimer extends CountDownTimer {
public MyCountDownTimer(long startTime, long interval) {
super(startTime, interval);
}
#Override
public void onFinish() {
text.setText("Time's up!");
}
#Override
public void onTick(long millisUntilFinished) {
text.setText(100 - millisUntilFinished/1000);
}
}
}
So I have some simple code but it seems to not be working.. any suggestions?
I just want an image to show after a button is pressed then become invisible after 2 seconds.
button.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
firstImage.setVisibility(ImageView.VISIBLE);
// delay of some sort
firstImage.setVisibility(ImageView.INVISIBLE);
}
}
The image never shows, it always stays invisible, should I be implementing this in another way? I've tried handlers.. but it didn't work, unless I did it wrong.
Never make your UI thread sleep!
Do this:
final Handler handler = new Handler();
button.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
firstImage.setVisibility(ImageView.VISIBLE);
handler.postDelayed(new Runnable(){
public void run(){
firstImage.setVisibility(ImageView.INVISIBLE);
}
}, DELAY);
}
}
Where you would set DELAY as 2000 (ms).
Well, you will need to add a delay between the two lines. Use a thread or a timer to do this.
Start a thread on click of a button. In the run method, change the ImageView's visibility to VISIBLE, then put the thread to sleep for n secs, and then change then make it invisible.
To call the imageView's setvisibility method, you will need a hanlder here.
Handler handler = new Handler();
handler.post(new Runnable() {
public void run() {
image.setVisibiliy(VISIBLE);
Thread.sleep(200);
image.setVisibility(INVISIBLE);
}
});
I know this question has already been answered, but I thought I would add an answer for people who like me, stumbled across this looking for a similar result where the delay was caused by a process rather than a "sleep"
button.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
firstImage.setVisibility(ImageView.VISIBLE);
// Run the operation on a new thread
new Thread(new Runnable(){
public void run(){
myMethod();
returnVisibility();
}
}).start();
}
}
private void myMethod() {
// Perform the operation you wish to do before restoring visibility
}
private void returnVisibility() {
// Restore visibility to the object being run on the main UI thread.
MainActivity.this.runOnUiThread(new Runnable() {
#Override
public void run() {
firstImage.setVisibility(ImageView.INVISIBLE);
}
});
}