summaryrefslogtreecommitdiffstats
path: root/core/java/android/animation
diff options
context:
space:
mode:
authorChet Haase <chet@google.com>2011-01-28 14:13:22 -0800
committerChet Haase <chet@google.com>2011-01-28 15:54:37 -0800
commit0dfc39842459daea98e2b551bbecd16d1baca439 (patch)
treecc26357111cc235d0676f15515c54a7afd7606d5 /core/java/android/animation
parentb0c939adfa339c5cbb7f458072119269368b3ba5 (diff)
downloadframeworks_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.java45
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);