Starting new transition cancels running transition

The behavior of running a transition is janky and unpredictable,
when there is already a transition running on the same scene root.
Usually, the new transition simply jumps to the end values, or
jumps to the start values for that transition and animates from
there.

A better approach is to cancel any running transition first, the
start the new transition from that point.

Even better would be to blend old/new transitions, or at least adjust
the animation timing according to where/when the previous transition
stopped. In the meantime, this fix is at least better than the
previous approach of ignoring running transitions.

Change-Id: I4f5fabb55f6454f1e9d66589a9a7c36f9fc013fb
This commit is contained in:
Chet Haase
2013-06-04 08:46:42 -07:00
parent 90b8994470
commit e9d32ea13e
3 changed files with 53 additions and 6 deletions

View File

@@ -78,6 +78,10 @@ public abstract class Transition {
private ArrayList<TransitionValues> mPlayStartValuesList = new ArrayList<TransitionValues>();
private ArrayList<TransitionValues> mPlayEndValuesList = new ArrayList<TransitionValues>();
// Track all animators in use in case the transition gets canceled and needs to
// cancel running animators
private ArrayList<Animator> mCurrentAnimators = new ArrayList<Animator>();
// Number of per-target instances of this Transition currently running. This count is
// determined by calls to startTransition() and endTransition()
int mNumInstances = 0;
@@ -401,13 +405,30 @@ public abstract class Transition {
TransitionValues start = mPlayStartValuesList.get(i);
TransitionValues end = mPlayEndValuesList.get(i);
startTransition();
animate(play(sceneRoot, start, end));
runAnimator(play(sceneRoot, start, end));
}
mPlayStartValuesList.clear();
mPlayEndValuesList.clear();
endTransition();
}
private void runAnimator(Animator animator) {
if (animator != null) {
// TODO: could be a single listener instance for all of them since it uses the param
animator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
mCurrentAnimators.add(animation);
}
@Override
public void onAnimationEnd(Animator animation) {
mCurrentAnimators.remove(animation);
}
});
animate(animator);
}
}
/**
* Captures the current scene of values for the properties that this
* transition monitors. These values can be either the start or end
@@ -667,11 +688,6 @@ public abstract class Transition {
animator.setInterpolator(getInterpolator());
}
animator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationCancel(Animator animation) {
cancelTransition();
}
@Override
public void onAnimationEnd(Animator animation) {
endTransition();
@@ -771,6 +787,7 @@ public abstract class Transition {
mEndValues.clear();
mEndIdValues.clear();
mEndItemIdValues.clear();
mCurrentAnimators.clear();
}
}
@@ -781,6 +798,11 @@ public abstract class Transition {
protected void cancelTransition() {
// TODO: how does this work with instances?
// TODO: this doesn't actually do *anything* yet
int numAnimators = mCurrentAnimators.size();
for (int i = 0; i < numAnimators; ++i) {
Animator animator = mCurrentAnimators.get(i);
animator.cancel();
}
onTransitionCancel();
if (mListeners != null && mListeners.size() > 0) {
ArrayList<TransitionListener> tmpListeners =

View File

@@ -302,6 +302,15 @@ public class TransitionGroup extends Transition {
}
}
@Override
protected void cancelTransition() {
super.cancelTransition();
int numTransitions = mTransitions.size();
for (int i = 0; i < numTransitions; ++i) {
mTransitions.get(i).cancelTransition();
}
}
@Override
String toString(String indent) {
String result = super.toString(indent);

View File

@@ -15,6 +15,7 @@
*/
package android.view.transition;
import android.util.ArrayMap;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
@@ -40,6 +41,8 @@ public class TransitionManager {
HashMap<Scene, Transition> mSceneTransitions = new HashMap<Scene, Transition>();
HashMap<Scene, HashMap<Scene, Transition>> mScenePairTransitions =
new HashMap<Scene, HashMap<Scene, Transition>>();
static ArrayMap<ViewGroup, Transition> sRunningTransitions =
new ArrayMap<ViewGroup, Transition>();
/**
* Sets the transition to be used for any scene change for which no
@@ -141,6 +144,11 @@ public class TransitionManager {
final ViewGroup sceneRoot = scene.getSceneRoot();
Transition runningTransition = sRunningTransitions.get(sceneRoot);
if (runningTransition != null) {
runningTransition.cancelTransition();
}
// Capture current values
if (transition != null) {
transition.captureValues(sceneRoot, true);
@@ -159,6 +167,14 @@ public class TransitionManager {
observer.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
public boolean onPreDraw() {
sceneRoot.getViewTreeObserver().removeOnPreDrawListener(this);
// Add to running list, handle end to remove it
sRunningTransitions.put(sceneRoot, transition);
transition.addListener(new Transition.TransitionListenerAdapter() {
@Override
public void onTransitionEnd(Transition transition) {
sRunningTransitions.remove(sceneRoot);
}
});
transition.captureValues(sceneRoot, false);
transition.play(sceneRoot);
return true;