A little while ago I needed a flashing text in Android.
Pretty easy you'd think: stick two animations in an AnimationSet, set the repeatMode to Animation.RESTART and repeatCount to Animation.INFITITE like this:
XML
<set xmlns:android="http://schemas.android.com/apk/res/android"
<alpha
xmlns:android="http://schemas.android.com/apk/res/android"
android:fromAlpha="0.0"
android:toAlpha="1.0"
android:duration="1000"
/>
<alpha
xmlns:android="http://schemas.android.com/apk/res/android"
android:fromAlpha="0.9"
android:toAlpha="0.0"
android:duration="1000"
android:startOffset="1000"
/>
</set>
|
Activity code
AnimationSet c = (AnimationSet) AnimationUtils.loadAnimation(this, R.anim.flash);
c.setRepeatMode(Animation.RESTART);
c.setRepeatCount(Animation.INFINITE);
TextView tv = (TextView) findViewById(R.id.flashingTextView);
tv.clearAnimation();
tv.startAnimation(c);
|
and you're done.
But no! It only runs one time, or 1.5 times or whatever; definitely not endlessly.
This pretty basic requirement of looping a set of animations indefinitely is apparently a
known bug. I haven't been able to find out if it's still an outstanding issue today (August 2010) but unless I implemented it incorrectly, it is definitely present in SDK 1.6.
Based on the answers in that thread and the example code
here, I came up with the following solution:
XML
A fadein.xml and a fadeout.xml. See the end of this post for the complete code.
Activity code
// Get ref to what we're going to animate (fade-in and fade-out indefinitely)
tv = (TextView) findViewById(R.id.flashingTextView);
// Setup fadein/out animations
fadeIn = AnimationUtils.loadAnimation(this, R.anim.fadein);
fadeIn.setAnimationListener( myFadeInAnimationListener );
fadeOut = AnimationUtils.loadAnimation(this, R.anim.fadeout);
fadeOut.setAnimationListener( myFadeOutAnimationListener );
// And start with the fade in
launchInAnimation();
public void launchInAnimation() {
tv.startAnimation(fadeIn);
}
public void launchOutAnimation() {
// Launch the second animation :
tv = (TextView) findViewById(R.id.flashingTextView);
tv.startAnimation(fadeOut);
}
|
Two listeners which start the other animation at the end of an animation:
private class LocalFadeInAnimationListener implements AnimationListener {
public void onAnimationEnd(Animation animation) {
tv.post(mLaunchFadeOutAnimation);
}
public void onAnimationRepeat(Animation animation) {
}
public void onAnimationStart(Animation animation) {
}
};
private class LocalFadeOutAnimationListener implements AnimationListener {
public void onAnimationEnd(Animation animation) {
tv.post(mLaunchFadeInAnimation);
}
public void onAnimationRepeat(Animation animation) {
}
public void onAnimationStart(Animation animation) {
}
};
private LocalFadeInAnimationListener myFadeInAnimationListener = new LocalFadeInAnimationListener();
private LocalFadeOutAnimationListener myFadeOutAnimationListener = new LocalFadeOutAnimationListener();
|
Runnables that do the fading
private Runnable mLaunchFadeOutAnimation = new Runnable() {
public void run() {
launchOutAnimation();
}
};
private Runnable mLaunchFadeInAnimation = new Runnable() {
public void run() {
launchInAnimation();
}
};
|
Note that I improved efficiency of the mentioned example code by only setting the animations one time and getting the TextView to animate only once.
That's it!
Note: in the logcat I do get these messages (2-3 times in about 5 minutes):
08-22 15:33:28.078: WARN/SurfaceComposerClient(252): lock_layer timed out (is the CPU pegged?) layer=0, lcblk=0x41048020,
state=00000002 (was 00000043)
08-22 15:33:28.078: WARN/SurfaceComposerClient(252): lock_layer() timed out but didn't appear to need to be locked and
we recovered (layer=0, lcblk=0x41048020, state=00000002)
|
Pretty strange, the CPU doesn't seem to be pegged... It says it recovers, but I guess the warning is there for a reason. Can the code be modified to avoid these warnings?
It might be a
platform bug. Indeed I do see it appear in 1.6 but not in 2.1.
Full code (tested on 1.6) can be downloaded
here.