Why does the animation restart on the stopwatch tick?


I have a class with a view for animation and a stopwatch. This is the content of my onCreate method:

  chronometer = ((Chronometer) findViewById(R.id.clock_time)); chronometer.setOnChronometerTickListener(this); chronometer.start(); v = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE); final SurfaceView surfaceViewTimer = (SurfaceView) findViewById(R.id.surface_view_timer); final RelativeLayout timeView = (RelativeLayout) findViewById(R.id.time_view); ViewTreeObserver observer = timeView.getViewTreeObserver(); observer.addOnGlobalLayoutListener(new OnGlobalLayoutListener() { @Override public void onGlobalLayout() { int animationDuration = getIntent().getIntExtra( MainActivity.TOTAL_TIME, -1); TranslateAnimation surfaceGrowingAnimation = new TranslateAnimation( 0, 0, timeView.getHeight(), 0); surfaceGrowingAnimation.setDuration(animationDuration * 1000); surfaceViewTimer.startAnimation(surfaceGrowingAnimation); 

I have this other method for the chronometer:

 @Override public void onChronometerTick(Chronometer chronometer) { int intervalOfVibration = getIntent().getIntExtra( MainActivity.VIBRATION_INTERVAL, -1); long elapsedMillis = (SystemClock.elapsedRealtime() - ((Chronometer) findViewById(R.id.clock_time)) .getBase()) / 1000; if (((elapsedMillis % intervalOfVibration) == 0) && (elapsedMillis > 0)) v.vibrate(300); } 

But my animation restarts with every tick of the stopwatch.

How can I fix this?

Now I get these errors:

 11-26 17:58:57.908: W/dalvikvm(15596): threadid=3: thread exiting with uncaught exception (group=0x2aaca228) 11-26 17:58:57.908: E/AndroidRuntime(15596): Uncaught handler: thread main exiting due to uncaught exception 11-26 17:58:57.908: E/AndroidRuntime(15596): java.lang.IllegalStateException: This ViewTreeObserver is not alive, call getViewTreeObserver() again 11-26 17:58:57.908: E/AndroidRuntime(15596): at android.view.ViewTreeObserver.checkIsAlive(ViewTreeObserver.java:492) 11-26 17:58:57.908: E/AndroidRuntime(15596): at android.view.ViewTreeObserver.removeGlobalOnLayoutListener(ViewTreeObserver.java:339) 11-26 17:58:57.908: E/AndroidRuntime(15596): at com.activities.TimerActivity$1.onGlobalLayout(TimerActivity.java:64) 11-26 17:58:57.908: E/AndroidRuntime(15596): at android.view.ViewTreeObserver.dispatchOnGlobalLayout(ViewTreeObserver.java:549) 

The answer

Your OnGlobalLayoutListener is called whenever the screen is presented, which includes whenever the Chronometer changes (because it changes the width, perhaps the height, etc., and all invalid).

Use removeOnGlobalLayoutListener(); in your onGlobalLayout() method:

 ViewTreeObserver observer = timeView.getViewTreeObserver(); // Make this final observer.addOnGlobalLayoutListener(new OnGlobalLayoutListener() { @Override @SuppressWarnings("deprecation") public void onGlobalLayout() { // Your code ... if (android.os.Build.VERSION.SDK_INT < 16) // Use old, deprecated method timeView.getViewTreeObserver().removeGlobalOnLayoutListener(this); else // Use new method timeView.getViewTreeObserver().removeOnGlobalLayoutListener(this); } } 

Note : You will need to use removeGlobalOnLayoutListener() instead, for APIs <16. (The method name has been changed to API level 16. They work the same way.)