diff options
| author | Chet Haase <chet@google.com> | 2011-01-28 14:13:22 -0800 |
|---|---|---|
| committer | Chet Haase <chet@google.com> | 2011-01-28 15:54:37 -0800 |
| commit | 0dfc39842459daea98e2b551bbecd16d1baca439 (patch) | |
| tree | cc26357111cc235d0676f15515c54a7afd7606d5 /core/java/android/animation | |
| parent | b0c939adfa339c5cbb7f458072119269368b3ba5 (diff) | |
| download | frameworks_base-0dfc39842459daea98e2b551bbecd16d1baca439.zip frameworks_base-0dfc39842459daea98e2b551bbecd16d1baca439.tar.gz frameworks_base-0dfc39842459daea98e2b551bbecd16d1baca439.tar.bz2 | |
Fixed LayoutTransition bug moving multiple views
The problem was that there can be >1 animation spawned for each
view in a container, if there are multiple events that trigger
a transition. These animations would potentially clobber object
layout values, causing problems as successive animations tried to use those
clobbered values to set up their own animation values.
The fix is to track the created animations and cancel them as future
animations on those same objects get created. This mechanism used to
be in the code (the bug came about when that mechanism went away), but
was removed because of memory leaks of never removing animations that
were set up but never started. The new approach also caches pending
animations, but runs a second aniamtor to delete the entries in that
collection just in case.
Change-Id: If60c7d188712334dea69d0794dc6b4ce29ca6c09
Diffstat (limited to 'core/java/android/animation')
| -rw-r--r-- | core/java/android/animation/LayoutTransition.java | 45 |
1 files changed, 36 insertions, 9 deletions
diff --git a/core/java/android/animation/LayoutTransition.java b/core/java/android/animation/LayoutTransition.java index f13d940..d3e10f3 100644 --- a/core/java/android/animation/LayoutTransition.java +++ b/core/java/android/animation/LayoutTransition.java @@ -166,6 +166,7 @@ public class LayoutTransition { * we cache all of the current animations in this map for possible cancellation on * another layout event. */ + private final HashMap<View, Animator> pendingAnimations = new HashMap<View, Animator>(); private final HashMap<View, Animator> currentChangingAnimations = new HashMap<View, Animator>(); private final HashMap<View, Animator> currentVisibilityAnimations = new HashMap<View, Animator>(); @@ -542,6 +543,8 @@ public class LayoutTransition { // reset the inter-animation delay, in case we use it later staggerDelay = 0; + final long duration = (changeReason == APPEARING) ? + mChangingAppearingDuration : mChangingDisappearingDuration; final ViewTreeObserver observer = parent.getViewTreeObserver(); // used for later cleanup if (!observer.isAlive()) { @@ -556,12 +559,6 @@ public class LayoutTransition { // only animate the views not being added or removed if (child != newView) { - // If there's an animation running on this view already, cancel it - Animator currentAnimation = currentChangingAnimations.get(child); - if (currentAnimation != null) { - currentAnimation.cancel(); - currentChangingAnimations.remove(child); - } // Make a copy of the appropriate animation final Animator anim = baseAnimator.clone(); @@ -573,6 +570,30 @@ public class LayoutTransition { // its target object anim.setupStartValues(); + // If there's an animation running on this view already, cancel it + Animator currentAnimation = pendingAnimations.get(child); + if (currentAnimation != null) { + currentAnimation.cancel(); + pendingAnimations.remove(child); + } + // Cache the animation in case we need to cancel it later + pendingAnimations.put(child, anim); + + // For the animations which don't get started, we have to have a means of + // removing them from the cache, lest we leak them and their target objects. + // We run an animator for the default duration+100 (an arbitrary time, but one + // which should far surpass the delay between setting them up here and + // handling layout events which start them. + ValueAnimator pendingAnimRemover = ValueAnimator.ofFloat(0f, 1f). + setDuration(duration+100); + pendingAnimRemover.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + pendingAnimations.remove(child); + } + }); + pendingAnimRemover.start(); + // Add a listener to track layout changes on this view. If we don't get a callback, // then there's nothing to animate. final View.OnLayoutChangeListener listener = new View.OnLayoutChangeListener() { @@ -583,19 +604,25 @@ public class LayoutTransition { anim.setupEndValues(); long startDelay; - long duration; if (changeReason == APPEARING) { startDelay = mChangingAppearingDelay + staggerDelay; staggerDelay += mChangingAppearingStagger; - duration = mChangingAppearingDuration; } else { startDelay = mChangingDisappearingDelay + staggerDelay; staggerDelay += mChangingDisappearingStagger; - duration = mChangingDisappearingDuration; } anim.setStartDelay(startDelay); anim.setDuration(duration); + Animator prevAnimation = currentChangingAnimations.get(child); + if (prevAnimation != null) { + prevAnimation.cancel(); + currentChangingAnimations.remove(child); + } + Animator pendingAnimation = pendingAnimations.get(child); + if (pendingAnimation != null) { + pendingAnimations.remove(child); + } // Cache the animation in case we need to cancel it later currentChangingAnimations.put(child, anim); |
