Showing posts with label endless. Show all posts
Showing posts with label endless. Show all posts

Wednesday, August 25, 2010

Endless flashing TextView animation in Android

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.