I have read several forums and examples on using invalidate() in order to update views immediately but I still do not understand why what I am doing will not work. The code below uses image buttons defined by "red", "blue", "green", and "yellow". I set a 1 second delay between each time I try and change a button's appearance. Please someone tell me what i'm doing wrong.
private void showPattern() {
if (correct == true) {
for (int k = 0; k < temp_basket.length; k++) {
if (temp_basket[k] == 0) {
red.setPressed(true);
red.invalidate();
final Handler handler = new Handler();
Timer t = new Timer();
t.schedule(new TimerTask() {
public void run() {
handler.post(new Runnable() {
public void run() {
red.setPressed(false);
red.invalidate();
}
});
}
}, 1000);
There are 3 more or these blocks after this one that are blue, green, and yellow.
Invaliadate puts a redraw message in the queue
As I see in your code, there are multiple redraws happening on after the other... the OS will try to optimize the rendering by clubbing multiple redraw messages into one (under the condition that there's no other message between them).
What you may want to do is:
private Handler myHandler = new Handler() {
public void handleMessage(Message msg)
{
switch(msg.what) { /* do your work */ }
}
};
Message msg = Message.obtain(myHandler);
msg.what = <some-number>;
msg.obj = <your-data-to-process>
if(myHandler.containsMessage(<same-number>) {
myHandler.removeMessages(<same-number>);
}
myHandler.sendMessage(msg);
You can also try using postInvalidate() method, which will cause the invalidate in the UI thread.
Related
I want to create a basic "pokemon evolution" animation in my app , something like this:
I've been playing with this:
private void evolveAnimation(Pokemon selectedToEvolvePokemon) {
Drawable backgrounds[] = new Drawable[2];
Resources res = getResources();
backgrounds[0] = res.getDrawable(selectedPokemon.getImage(getContext()));
backgrounds[1] = res.getDrawable(selectedToEvolvePokemon.getImage(getContext()));
TransitionDrawable crossfader = new TransitionDrawable(backgrounds);
evolveTransition.setImageDrawable(crossfader);
crossfader.setCrossFadeEnabled(true);
crossfader.startTransition(3000); //The animation starts slow
crossfader.reverseTransition(3000);
crossfader.startTransition(2000);//It gets faster slow
crossfader.reverseTransition(2000);
crossfader.startTransition(1000); //And faster
crossfader.reverseTransition(1000);
}
However , this is not working as expected in the GIF posted below , how can I fix this?
EDIT:
I've changed the method to this:
private void doEvolve(TransitionDrawable crossfader, int durationMills) {
crossfader.startTransition(durationMills);
Handler h = new Handler(Looper.getMainLooper());
h.postDelayed(new Runnable() {
#Override
public void run() {
crossfader.reverseTransition(durationMills);
}
}, durationMills);
if (durationMills != 0) {
doEvolve(crossfader, durationMills - 100);
} else {
crossfader.startTransition(0);
}
}
However , works a bit strange.
currently you are starting all transitions at once, they should be queued
for example you may use some Handler and its postDelayed method, e.g.
crossfader.startTransition(3000);
Handler h = new Handler(Looper.getMainLooper());
h.postDelayed(new Runnable() {
#Override
public void run() {
crossfader.reverseTransition(3000);
}
}, 3000); // will start after startTransition duration
pack this code into some method which will take duration param and some callback informing that start/reverse pattern ended, then call this method again with shorter duration
Hello I'm trying to delay imageviews to give a perception that they are being taken out one by one. I've tried
Thread.Sleep
CountDownTimer
Runnable/Handler
But they either only delay once and both imageviews change at the same time or do not delay at all. For some reference I'm trying to do something like
private void delaycard(final int Card) {
newcard(Card); //Delay this before it is called
}
Willing to try/retry anything at this point
Off the top of my head, try something like this:
Handler h = new Handler();
//for the number of images we have
for (int i = 0; i < numImages; i++) {
//we create a runnable for that action
Runnable r = new Runnable() {
#Override
public void run() {
newCard(card);
}
};
//this is the amount of time to delay it by
delay = delay + 500;
//effectively, we're creating a series of runnables at the same time
//but they activate one after the other in .5s intervals
h.postDelayed(r, delay);
}
put this inside your method
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
newCard(Card);
}
}, 100);
I am making a piano app in which I need to emulate pressing keys in order to show the user the notes of a song. By using a handler, I am able to make a delay pressing up and down a key.
However, I am not able to make a delay between all the keys which have to be touched. I thought something like this, but I only see the last 2 notes get pressed and sound.
final String[] keys = {"1b","2w","9b","10w","11b","12w"};
handler = new Handler(Looper.getMainLooper());
for(int i = 0; i < keys.length - 1; i++) {
final int nextKey = i+1;
//Method in which I used other handler to delay pressing up and down
pressKey(keys[i]);
//Next key i would like to get pressed with a delay
handler.postDelayed(new Runnable() {
#Override
public void run() {
pressKey(keys[nextKey]);
}
},1500);
}
Instead of using for, use a runnable that will keep posting itself with the specified delay:
new Runnable()
{
int nextKey = 0;
#Override
public void run()
{
pressKey(keys[nextKey]);
if (++nextKey < keys.length)
handler.postDelayed(this, 1500);
// else no more keys
}
}.run();
I feel kinda stupid after all the night trying to figure it out. I just had to make a variable with the delay, and increment it in each iteration.
You can do this with a thread if in pressKey method there is no work that needs to be done in the UI thread.
new Thread(
new Runnable(){
#Override
public void run(){
for(int i = 0; i < keys.length - 1; i++) {
pressKey(keys[i]);
Thread.sleep(1500);
}
}
}
).run();
I would like to make a textView disappear after a define amount of time succeeding a change of the text.
I am not sure what the best way to proceed is.
The problem of using Thread.sleep(2000);, is that it would obviously make the whole UI thread sleep. Using Thread.sleep(2000); inside an AsyncTask seems wrong. In addition, it would lead to problems if the user leave the Activity that needs to be updated by the AsyncTask.
There must be a cleaner way to implement that. If you know any: feel free to answer :-)
You don't need to create a handler:
view.postDelayed(new Runnable(){
#Override
public void run()
{
view.setVisibility(View.GONE);
}
}, 10000);
You can use a Runnable for this:
Handler myHandler = new Handler();
Runnable myRunnable = new Runnable() {
#Override
public void run() {
//Hide your view
}
};
Then after your change of text, add the following:
myHandler.postDelayed(myRunnable, TIME_IN_MILLISECONDS);
Hope it helps.
Can you do something like this?
In your activity, create a handler:
Handler handler = new Handler()
{
#Override
public void handleMessage (Message msg)
{
if (msg.what == HIDE_VIEW)
hideView(); // defined elsewhere in your activitry
}
}
And at the point where you want to start the timer, do this:
postDelayed (new Runnable()
{
#Override
public void run()
{
Message msg = handler.obtainMessage();
msg.what = HIDE_VIEW;
msg.obj = null;
handler.sendMessage (msg);
}
}, 10000);
Not sure if you need this level of asynchronousity.
Sorry for the wrong interpretation of the question,
Use
textView .setVisibility(View.INVISIBLE)
within a handler
I am stuck trying to get my timer to work, where I will do one countdown, then go back and do another.
So, on the second call to timer.scheduleAtFixedRate I get an IllegalStateException, but I am cancelling the TimerTask not the Timer.
What I want to do is count down twenty seconds, then ten seconds, then repeat, updating a text box each time, to inform the user what they should be doing.
One issue is that the countdown is faster than once/second.
04-22 01:34:57.118: DEBUG/TestHandler1(404): message called:2:null
04-22 01:34:57.709: DEBUG/TestHandler1(404): message called:3:null
04-22 01:34:57.899: DEBUG/TestHandler1(404): message called:4:null
04-22 01:34:58.198: DEBUG/TestHandler1(404): message called:5:null
Here is my exception:
04-22 01:35:48.529: ERROR/AndroidRuntime(404): java.lang.IllegalStateException: TimerTask is scheduled already
04-22 01:35:48.529: ERROR/AndroidRuntime(404): at java.util.Timer.scheduleImpl(Timer.java:574)
04-22 01:35:48.529: ERROR/AndroidRuntime(404): at java.util.Timer.scheduleAtFixedRate(Timer.java:530)
Here is the code I used for this attempt. When I tried to use a different timertask for each loop it was even worse behavior, as I put the new TimerTask block just before timer.scheduleAtFixedRate, so I went back to this version.
handler = new Handler() {
public void handleMessage(Message msg) {
counterText.setText((new Date()).toString() + " "
+ Integer.toString(cntr));
System.out.println("handleMessage");
}
};
timertask = new TimerTask() {
public void run() {
handler.sendEmptyMessage(0);
cntr++;
if (cntr > maxReps) {
timertask.cancel();
cntr = 0;
}
}
};
doneButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
cntr = 0;
for (MyClass mclass : input.getLoop()) {
labelText.setText(mclass.getName());
for (int y = 0; y < 8; y++) {
maxReps = 20;
timer.scheduleAtFixedRate(timertask, 0, 1000);
maxReps = 10;
labelText.setText("Rest");
timer.scheduleAtFixedRate(timertask, 0, 1000);
}
}
}
});
Hopefully someone can point out a simple mistake that I made.
Above is a snippet of my code from the Activity, so I am including just what I think is needed to show where I am having a problem.
Given this article in the dev guide: http://developer.android.com/resources/articles/timed-ui-updates.html and a similar post here: http://cart.kolix.de/?p=1438 , I would suggest you to change the strategy and use handler.postDelayed(this, 2000);
But, specifically to the code, my guess is that it may get cancelled the wrong task. I don't really understand the rationale for having two calls to timer.scheduleAtFixedRate(timertask, 0, 1000); and setting maxReps = 20; first and then maxReps = 10; in the same loop, because the loop will not wait for the task to complete before continuing.
So, you are sending at least 16 calls to timer.scheduleAtFixedRate() and not all may get cancelled at the right time.
After experimenting with various methods, I finally settled on this solution.
In my OnCreate method I had this code, and it would go 20 seconds then exit.
final Handler handler = new Handler() {
public void handleMessage(Message msg) {
String s = msg.getData().getString("counter");
counterText.setText(s);
Log.d("Activity.handleMessage", s);
}
};
final Runnable runnable = new Runnable() {
#Override
public void run() {
final long start = SystemClock.uptimeMillis();
while (true) {
Message m = new Message();
Bundle b = new Bundle();
b.putString("counter", Integer.toString(cntr));
m.setData(b);
handler.sendMessageDelayed(m, cntr * 1000);
if (cntr++ == maxReps) {
return;
}
}
}
};
doneButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
exerciseText.setText(model.getName());
counterText.setText("0");
maxReps = 20;
handler.postDelayed(runnable, 1000);
});
Part of my difficulty is not thinking about how Android works. I found various approaches that didn't work well.