am d1477e74
: Merge "Better Transition interruption" into klp-dev
* commit 'd1477e746065450b1900398e103f4715ccf81b35': Better Transition interruption
This commit is contained in:
@ -29496,19 +29496,18 @@ package android.view.transition {
|
|||||||
public abstract class Transition implements java.lang.Cloneable {
|
public abstract class Transition implements java.lang.Cloneable {
|
||||||
ctor public Transition();
|
ctor public Transition();
|
||||||
method public void addListener(android.view.transition.Transition.TransitionListener);
|
method public void addListener(android.view.transition.Transition.TransitionListener);
|
||||||
method protected void cancelTransition();
|
method protected void cancel();
|
||||||
method protected abstract void captureValues(android.view.transition.TransitionValues, boolean);
|
method protected abstract void captureValues(android.view.transition.TransitionValues, boolean);
|
||||||
method public android.view.transition.Transition clone();
|
method public android.view.transition.Transition clone();
|
||||||
method public long getDuration();
|
method public long getDuration();
|
||||||
method public android.animation.TimeInterpolator getInterpolator();
|
method public android.animation.TimeInterpolator getInterpolator();
|
||||||
method public java.util.ArrayList<android.view.transition.Transition.TransitionListener> getListeners();
|
method public java.util.ArrayList<android.view.transition.Transition.TransitionListener> getListeners();
|
||||||
|
method public java.lang.String getName();
|
||||||
method public long getStartDelay();
|
method public long getStartDelay();
|
||||||
method public int[] getTargetIds();
|
method public int[] getTargetIds();
|
||||||
method public android.view.View[] getTargets();
|
method public android.view.View[] getTargets();
|
||||||
|
method public java.lang.String[] getTransitionProperties();
|
||||||
method protected android.view.transition.TransitionValues getTransitionValues(android.view.View, boolean);
|
method protected android.view.transition.TransitionValues getTransitionValues(android.view.View, boolean);
|
||||||
method protected void onTransitionCancel();
|
|
||||||
method protected void onTransitionEnd();
|
|
||||||
method protected void onTransitionStart();
|
|
||||||
method protected android.animation.Animator play(android.view.ViewGroup, android.view.transition.TransitionValues, android.view.transition.TransitionValues);
|
method protected android.animation.Animator play(android.view.ViewGroup, android.view.transition.TransitionValues, android.view.transition.TransitionValues);
|
||||||
method public void removeListener(android.view.transition.Transition.TransitionListener);
|
method public void removeListener(android.view.transition.Transition.TransitionListener);
|
||||||
method public android.view.transition.Transition setDuration(long);
|
method public android.view.transition.Transition setDuration(long);
|
||||||
@ -29521,6 +29520,8 @@ package android.view.transition {
|
|||||||
public static abstract interface Transition.TransitionListener {
|
public static abstract interface Transition.TransitionListener {
|
||||||
method public abstract void onTransitionCancel(android.view.transition.Transition);
|
method public abstract void onTransitionCancel(android.view.transition.Transition);
|
||||||
method public abstract void onTransitionEnd(android.view.transition.Transition);
|
method public abstract void onTransitionEnd(android.view.transition.Transition);
|
||||||
|
method public abstract void onTransitionPause(android.view.transition.Transition);
|
||||||
|
method public abstract void onTransitionResume(android.view.transition.Transition);
|
||||||
method public abstract void onTransitionStart(android.view.transition.Transition);
|
method public abstract void onTransitionStart(android.view.transition.Transition);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -29567,6 +29568,7 @@ package android.view.transition {
|
|||||||
method protected android.animation.Animator appear(android.view.ViewGroup, android.view.transition.TransitionValues, int, android.view.transition.TransitionValues, int);
|
method protected android.animation.Animator appear(android.view.ViewGroup, android.view.transition.TransitionValues, int, android.view.transition.TransitionValues, int);
|
||||||
method protected void captureValues(android.view.transition.TransitionValues, boolean);
|
method protected void captureValues(android.view.transition.TransitionValues, boolean);
|
||||||
method protected android.animation.Animator disappear(android.view.ViewGroup, android.view.transition.TransitionValues, int, android.view.transition.TransitionValues, int);
|
method protected android.animation.Animator disappear(android.view.ViewGroup, android.view.transition.TransitionValues, int, android.view.transition.TransitionValues, int);
|
||||||
|
method public boolean isVisible(android.view.transition.TransitionValues);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -5356,6 +5356,18 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether layout calls on this container are currently being
|
||||||
|
* suppressed, due to an earlier call to {@link #suppressLayout(boolean)}.
|
||||||
|
*
|
||||||
|
* @return true if layout calls are currently suppressed, false otherwise.
|
||||||
|
*
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
public boolean isLayoutSuppressed() {
|
||||||
|
return mSuppressLayout;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
|
@ -19,6 +19,7 @@ package android.view.transition;
|
|||||||
import android.animation.Animator;
|
import android.animation.Animator;
|
||||||
import android.animation.AnimatorListenerAdapter;
|
import android.animation.AnimatorListenerAdapter;
|
||||||
import android.animation.ObjectAnimator;
|
import android.animation.ObjectAnimator;
|
||||||
|
import android.animation.ValueAnimator;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
@ -35,6 +36,7 @@ public class Fade extends Visibility {
|
|||||||
private static boolean DBG = Transition.DBG && false;
|
private static boolean DBG = Transition.DBG && false;
|
||||||
|
|
||||||
private static final String LOG_TAG = "Fade";
|
private static final String LOG_TAG = "Fade";
|
||||||
|
private static final String PROPNAME_ALPHA = "android:fade:alpha";
|
||||||
private static final String PROPNAME_SCREEN_X = "android:fade:screenX";
|
private static final String PROPNAME_SCREEN_X = "android:fade:screenX";
|
||||||
private static final String PROPNAME_SCREEN_Y = "android:fade:screenY";
|
private static final String PROPNAME_SCREEN_Y = "android:fade:screenY";
|
||||||
|
|
||||||
@ -74,26 +76,51 @@ public class Fade extends Visibility {
|
|||||||
/**
|
/**
|
||||||
* Utility method to handle creating and running the Animator.
|
* Utility method to handle creating and running the Animator.
|
||||||
*/
|
*/
|
||||||
private Animator runAnimation(View view, float startAlpha, float endAlpha,
|
private Animator createAnimation(View view, float startAlpha, float endAlpha,
|
||||||
Animator.AnimatorListener listener) {
|
AnimatorListenerAdapter listener) {
|
||||||
|
if (startAlpha == endAlpha) {
|
||||||
|
// run listener if we're noop'ing the animation, to get the end-state results now
|
||||||
|
if (listener != null) {
|
||||||
|
listener.onAnimationEnd(null);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
final ObjectAnimator anim = ObjectAnimator.ofFloat(view, "alpha", startAlpha, endAlpha);
|
final ObjectAnimator anim = ObjectAnimator.ofFloat(view, "alpha", startAlpha, endAlpha);
|
||||||
if (listener != null) {
|
if (listener != null) {
|
||||||
anim.addListener(listener);
|
anim.addListener(listener);
|
||||||
|
anim.addPauseListener(listener);
|
||||||
}
|
}
|
||||||
// TODO: Maybe extract a method into Transition to run an animation that handles the
|
|
||||||
// duration/startDelay stuff for all subclasses.
|
|
||||||
return anim;
|
return anim;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void captureValues(TransitionValues values, boolean start) {
|
protected void captureValues(TransitionValues values, boolean start) {
|
||||||
super.captureValues(values, start);
|
super.captureValues(values, start);
|
||||||
|
float alpha = values.view.getAlpha();
|
||||||
|
values.values.put(PROPNAME_ALPHA, alpha);
|
||||||
int[] loc = new int[2];
|
int[] loc = new int[2];
|
||||||
values.view.getLocationOnScreen(loc);
|
values.view.getLocationOnScreen(loc);
|
||||||
values.values.put(PROPNAME_SCREEN_X, loc[0]);
|
values.values.put(PROPNAME_SCREEN_X, loc[0]);
|
||||||
values.values.put(PROPNAME_SCREEN_Y, loc[1]);
|
values.values.put(PROPNAME_SCREEN_Y, loc[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Animator play(ViewGroup sceneRoot, TransitionValues startValues,
|
||||||
|
TransitionValues endValues) {
|
||||||
|
Animator animator = super.play(sceneRoot, startValues, endValues);
|
||||||
|
if (animator == null && startValues != null && endValues != null) {
|
||||||
|
boolean endVisible = isVisible(endValues);
|
||||||
|
final View endView = endValues.view;
|
||||||
|
float endAlpha = endView.getAlpha();
|
||||||
|
float startAlpha = (Float) startValues.values.get(PROPNAME_ALPHA);
|
||||||
|
if ((endVisible && startAlpha < endAlpha && (mFadingMode & Fade.IN) != 0) ||
|
||||||
|
(!endVisible && startAlpha > endAlpha && (mFadingMode & Fade.OUT) != 0)) {
|
||||||
|
animator = createAnimation(endView, startAlpha, endAlpha, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return animator;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Animator appear(ViewGroup sceneRoot,
|
protected Animator appear(ViewGroup sceneRoot,
|
||||||
TransitionValues startValues, int startVisibility,
|
TransitionValues startValues, int startVisibility,
|
||||||
@ -102,15 +129,11 @@ public class Fade extends Visibility {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
final View endView = endValues.view;
|
final View endView = endValues.view;
|
||||||
|
// if alpha < 1, just fade it in from the current value
|
||||||
|
if (endView.getAlpha() == 1.0f) {
|
||||||
endView.setAlpha(0);
|
endView.setAlpha(0);
|
||||||
final Animator.AnimatorListener endListener = new AnimatorListenerAdapter() {
|
|
||||||
@Override
|
|
||||||
public void onAnimationEnd(Animator animation) {
|
|
||||||
// Always end animation with full alpha, in case it's canceled mid-stream
|
|
||||||
endView.setAlpha(1);
|
|
||||||
}
|
}
|
||||||
};
|
return createAnimation(endView, endView.getAlpha(), 1, null);
|
||||||
return runAnimation(endView, 0, 1, endListener);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -129,7 +152,7 @@ public class Fade extends Visibility {
|
|||||||
}
|
}
|
||||||
View overlayView = null;
|
View overlayView = null;
|
||||||
View viewToKeep = null;
|
View viewToKeep = null;
|
||||||
if (endView == null) {
|
if (endView == null || endView.getParent() == null) {
|
||||||
// view was removed: add the start view to the Overlay
|
// view was removed: add the start view to the Overlay
|
||||||
view = startView;
|
view = startView;
|
||||||
overlayView = view;
|
overlayView = view;
|
||||||
@ -167,7 +190,7 @@ public class Fade extends Visibility {
|
|||||||
final View finalOverlayView = overlayView;
|
final View finalOverlayView = overlayView;
|
||||||
final View finalViewToKeep = viewToKeep;
|
final View finalViewToKeep = viewToKeep;
|
||||||
final ViewGroup finalSceneRoot = sceneRoot;
|
final ViewGroup finalSceneRoot = sceneRoot;
|
||||||
final Animator.AnimatorListener endListener = new AnimatorListenerAdapter() {
|
final AnimatorListenerAdapter endListener = new AnimatorListenerAdapter() {
|
||||||
@Override
|
@Override
|
||||||
public void onAnimationEnd(Animator animation) {
|
public void onAnimationEnd(Animator animation) {
|
||||||
finalView.setAlpha(startAlpha);
|
finalView.setAlpha(startAlpha);
|
||||||
@ -179,8 +202,22 @@ public class Fade extends Visibility {
|
|||||||
finalSceneRoot.getOverlay().remove(finalOverlayView);
|
finalSceneRoot.getOverlay().remove(finalOverlayView);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAnimationPause(Animator animation) {
|
||||||
|
if (finalOverlayView != null) {
|
||||||
|
finalSceneRoot.getOverlay().remove(finalOverlayView);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAnimationResume(Animator animation) {
|
||||||
|
if (finalOverlayView != null) {
|
||||||
|
finalSceneRoot.getOverlay().add(finalOverlayView);
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
return runAnimation(view, startAlpha, endAlpha, endListener);
|
return createAnimation(view, startAlpha, endAlpha, endListener);
|
||||||
}
|
}
|
||||||
if (viewToKeep != null) {
|
if (viewToKeep != null) {
|
||||||
// TODO: find a different way to do this, like just changing the view to be
|
// TODO: find a different way to do this, like just changing the view to be
|
||||||
@ -193,12 +230,42 @@ public class Fade extends Visibility {
|
|||||||
final View finalOverlayView = overlayView;
|
final View finalOverlayView = overlayView;
|
||||||
final View finalViewToKeep = viewToKeep;
|
final View finalViewToKeep = viewToKeep;
|
||||||
final ViewGroup finalSceneRoot = sceneRoot;
|
final ViewGroup finalSceneRoot = sceneRoot;
|
||||||
final Animator.AnimatorListener endListener = new AnimatorListenerAdapter() {
|
final AnimatorListenerAdapter endListener = new AnimatorListenerAdapter() {
|
||||||
|
boolean mCanceled = false;
|
||||||
|
float mPausedAlpha = -1;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAnimationPause(Animator animation) {
|
||||||
|
if (finalViewToKeep != null && !mCanceled) {
|
||||||
|
finalViewToKeep.setVisibility(finalVisibility);
|
||||||
|
}
|
||||||
|
mPausedAlpha = finalView.getAlpha();
|
||||||
|
finalView.setAlpha(startAlpha);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAnimationResume(Animator animation) {
|
||||||
|
if (finalViewToKeep != null && !mCanceled) {
|
||||||
|
finalViewToKeep.setVisibility(View.VISIBLE);
|
||||||
|
}
|
||||||
|
finalView.setAlpha(mPausedAlpha);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAnimationCancel(Animator animation) {
|
||||||
|
mCanceled = true;
|
||||||
|
if (mPausedAlpha >= 0) {
|
||||||
|
finalView.setAlpha(mPausedAlpha);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onAnimationEnd(Animator animation) {
|
public void onAnimationEnd(Animator animation) {
|
||||||
|
if (!mCanceled) {
|
||||||
finalView.setAlpha(startAlpha);
|
finalView.setAlpha(startAlpha);
|
||||||
|
}
|
||||||
// TODO: restore view offset from overlay repositioning
|
// TODO: restore view offset from overlay repositioning
|
||||||
if (finalViewToKeep != null) {
|
if (finalViewToKeep != null && !mCanceled) {
|
||||||
finalViewToKeep.setVisibility(finalVisibility);
|
finalViewToKeep.setVisibility(finalVisibility);
|
||||||
}
|
}
|
||||||
if (finalOverlayView != null) {
|
if (finalOverlayView != null) {
|
||||||
@ -206,7 +273,7 @@ public class Fade extends Visibility {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
return runAnimation(view, startAlpha, endAlpha, endListener);
|
return createAnimation(view, startAlpha, endAlpha, endListener);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -25,8 +25,6 @@ import android.graphics.Bitmap;
|
|||||||
import android.graphics.Canvas;
|
import android.graphics.Canvas;
|
||||||
import android.graphics.Rect;
|
import android.graphics.Rect;
|
||||||
import android.graphics.drawable.BitmapDrawable;
|
import android.graphics.drawable.BitmapDrawable;
|
||||||
import android.util.ArrayMap;
|
|
||||||
import android.util.Log;
|
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
@ -42,6 +40,13 @@ public class Move extends Transition {
|
|||||||
private static final String PROPNAME_PARENT = "android:move:parent";
|
private static final String PROPNAME_PARENT = "android:move:parent";
|
||||||
private static final String PROPNAME_WINDOW_X = "android:move:windowX";
|
private static final String PROPNAME_WINDOW_X = "android:move:windowX";
|
||||||
private static final String PROPNAME_WINDOW_Y = "android:move:windowY";
|
private static final String PROPNAME_WINDOW_Y = "android:move:windowY";
|
||||||
|
private static String[] sTransitionProperties = {
|
||||||
|
PROPNAME_BOUNDS,
|
||||||
|
PROPNAME_PARENT,
|
||||||
|
PROPNAME_WINDOW_X,
|
||||||
|
PROPNAME_WINDOW_Y
|
||||||
|
};
|
||||||
|
|
||||||
int[] tempLocation = new int[2];
|
int[] tempLocation = new int[2];
|
||||||
boolean mResizeClip = false;
|
boolean mResizeClip = false;
|
||||||
boolean mReparent = false;
|
boolean mReparent = false;
|
||||||
@ -49,6 +54,11 @@ public class Move extends Transition {
|
|||||||
|
|
||||||
private static RectEvaluator sRectEvaluator = new RectEvaluator();
|
private static RectEvaluator sRectEvaluator = new RectEvaluator();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String[] getTransitionProperties() {
|
||||||
|
return sTransitionProperties;
|
||||||
|
}
|
||||||
|
|
||||||
public void setResizeClip(boolean resizeClip) {
|
public void setResizeClip(boolean resizeClip) {
|
||||||
mResizeClip = resizeClip;
|
mResizeClip = resizeClip;
|
||||||
}
|
}
|
||||||
@ -146,12 +156,33 @@ public class Move extends Transition {
|
|||||||
if (view.getParent() instanceof ViewGroup) {
|
if (view.getParent() instanceof ViewGroup) {
|
||||||
final ViewGroup parent = (ViewGroup) view.getParent();
|
final ViewGroup parent = (ViewGroup) view.getParent();
|
||||||
parent.suppressLayout(true);
|
parent.suppressLayout(true);
|
||||||
anim.addListener(new AnimatorListenerAdapter() {
|
TransitionListener transitionListener = new TransitionListenerAdapter() {
|
||||||
|
boolean mCanceled = false;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onAnimationEnd(Animator animation) {
|
public void onTransitionCancel(Transition transition) {
|
||||||
|
parent.suppressLayout(false);
|
||||||
|
mCanceled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onTransitionEnd(Transition transition) {
|
||||||
|
if (!mCanceled) {
|
||||||
parent.suppressLayout(false);
|
parent.suppressLayout(false);
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onTransitionPause(Transition transition) {
|
||||||
|
parent.suppressLayout(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onTransitionResume(Transition transition) {
|
||||||
|
parent.suppressLayout(true);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
addListener(transitionListener);
|
||||||
}
|
}
|
||||||
return anim;
|
return anim;
|
||||||
} else {
|
} else {
|
||||||
@ -191,12 +222,33 @@ public class Move extends Transition {
|
|||||||
if (view.getParent() instanceof ViewGroup) {
|
if (view.getParent() instanceof ViewGroup) {
|
||||||
final ViewGroup parent = (ViewGroup) view.getParent();
|
final ViewGroup parent = (ViewGroup) view.getParent();
|
||||||
parent.suppressLayout(true);
|
parent.suppressLayout(true);
|
||||||
anim.addListener(new AnimatorListenerAdapter() {
|
TransitionListener transitionListener = new TransitionListenerAdapter() {
|
||||||
|
boolean mCanceled = false;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onAnimationEnd(Animator animation) {
|
public void onTransitionCancel(Transition transition) {
|
||||||
|
parent.suppressLayout(false);
|
||||||
|
mCanceled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onTransitionEnd(Transition transition) {
|
||||||
|
if (!mCanceled) {
|
||||||
parent.suppressLayout(false);
|
parent.suppressLayout(false);
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onTransitionPause(Transition transition) {
|
||||||
|
parent.suppressLayout(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onTransitionResume(Transition transition) {
|
||||||
|
parent.suppressLayout(true);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
addListener(transitionListener);
|
||||||
}
|
}
|
||||||
anim.addListener(new AnimatorListenerAdapter() {
|
anim.addListener(new AnimatorListenerAdapter() {
|
||||||
@Override
|
@Override
|
||||||
|
@ -22,7 +22,6 @@ import android.animation.TimeInterpolator;
|
|||||||
import android.util.ArrayMap;
|
import android.util.ArrayMap;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.util.LongSparseArray;
|
import android.util.LongSparseArray;
|
||||||
import android.util.Pair;
|
|
||||||
import android.util.SparseArray;
|
import android.util.SparseArray;
|
||||||
import android.view.SurfaceView;
|
import android.view.SurfaceView;
|
||||||
import android.view.TextureView;
|
import android.view.TextureView;
|
||||||
@ -60,6 +59,8 @@ public abstract class Transition implements Cloneable {
|
|||||||
private static final String LOG_TAG = "Transition";
|
private static final String LOG_TAG = "Transition";
|
||||||
static final boolean DBG = false;
|
static final boolean DBG = false;
|
||||||
|
|
||||||
|
private String mName = getClass().getName();
|
||||||
|
|
||||||
long mStartDelay = -1;
|
long mStartDelay = -1;
|
||||||
long mDuration = -1;
|
long mDuration = -1;
|
||||||
TimeInterpolator mInterpolator = null;
|
TimeInterpolator mInterpolator = null;
|
||||||
@ -69,29 +70,29 @@ public abstract class Transition implements Cloneable {
|
|||||||
private TransitionValuesMaps mEndValues = new TransitionValuesMaps();
|
private TransitionValuesMaps mEndValues = new TransitionValuesMaps();
|
||||||
TransitionGroup mParent = null;
|
TransitionGroup mParent = null;
|
||||||
|
|
||||||
|
// Per-animator information used for later canceling when future transitions overlap
|
||||||
|
private static ThreadLocal<ArrayMap<Animator, AnimationInfo>> sRunningAnimators =
|
||||||
|
new ThreadLocal<ArrayMap<Animator, AnimationInfo>>();
|
||||||
|
|
||||||
// Scene Root is set at play() time in the cloned Transition
|
// Scene Root is set at play() time in the cloned Transition
|
||||||
ViewGroup mSceneRoot = null;
|
ViewGroup mSceneRoot = null;
|
||||||
|
|
||||||
// Used to carry data between setup() and play(), cleared before every scene 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
|
// Track all animators in use in case the transition gets canceled and needs to
|
||||||
// cancel running animators
|
// cancel running animators
|
||||||
private ArrayList<Animator> mCurrentAnimators = new ArrayList<Animator>();
|
private ArrayList<Animator> mCurrentAnimators = new ArrayList<Animator>();
|
||||||
|
|
||||||
// Number of per-target instances of this Transition currently running. This count is
|
// Number of per-target instances of this Transition currently running. This count is
|
||||||
// determined by calls to startTransition() and endTransition()
|
// determined by calls to start() and end()
|
||||||
int mNumInstances = 0;
|
int mNumInstances = 0;
|
||||||
|
|
||||||
|
// Whether this transition is currently paused, due to a call to pause()
|
||||||
|
boolean mPaused = false;
|
||||||
|
|
||||||
// The set of listeners to be sent transition lifecycle events.
|
// The set of listeners to be sent transition lifecycle events.
|
||||||
ArrayList<TransitionListener> mListeners = null;
|
ArrayList<TransitionListener> mListeners = null;
|
||||||
|
|
||||||
// The set of animators collected from calls to play(), to be run in runAnimations()
|
// The set of animators collected from calls to play(), to be run in runAnimations()
|
||||||
ArrayMap<Pair<TransitionValues, TransitionValues>, Animator> mAnimatorMap =
|
ArrayList<Animator> mAnimators = new ArrayList<Animator>();
|
||||||
new ArrayMap<Pair<TransitionValues, TransitionValues>, Animator>();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a Transition object with no target objects. A transition with
|
* Constructs a Transition object with no target objects. A transition with
|
||||||
@ -115,6 +116,14 @@ public abstract class Transition implements Cloneable {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the duration set on this transition. If no duration has been set,
|
||||||
|
* the returned value will be negative, indicating that resulting animators will
|
||||||
|
* retain their own durations.
|
||||||
|
*
|
||||||
|
* @return The duration set on this transition, if one has been set, otherwise
|
||||||
|
* returns a negative number.
|
||||||
|
*/
|
||||||
public long getDuration() {
|
public long getDuration() {
|
||||||
return mDuration;
|
return mDuration;
|
||||||
}
|
}
|
||||||
@ -131,6 +140,14 @@ public abstract class Transition implements Cloneable {
|
|||||||
mStartDelay = startDelay;
|
mStartDelay = startDelay;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the startDelay set on this transition. If no startDelay has been set,
|
||||||
|
* the returned value will be negative, indicating that resulting animators will
|
||||||
|
* retain their own startDelays.
|
||||||
|
*
|
||||||
|
* @return The startDealy set on this transition, if one has been set, otherwise
|
||||||
|
* returns a negative number.
|
||||||
|
*/
|
||||||
public long getStartDelay() {
|
public long getStartDelay() {
|
||||||
return mStartDelay;
|
return mStartDelay;
|
||||||
}
|
}
|
||||||
@ -147,10 +164,43 @@ public abstract class Transition implements Cloneable {
|
|||||||
mInterpolator = interpolator;
|
mInterpolator = interpolator;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the interpolator set on this transition. If no interpolator has been set,
|
||||||
|
* the returned value will be null, indicating that resulting animators will
|
||||||
|
* retain their own interpolators.
|
||||||
|
*
|
||||||
|
* @return The interpolator set on this transition, if one has been set, otherwise
|
||||||
|
* returns null.
|
||||||
|
*/
|
||||||
public TimeInterpolator getInterpolator() {
|
public TimeInterpolator getInterpolator() {
|
||||||
return mInterpolator;
|
return mInterpolator;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the set of property names used stored in the {@link TransitionValues}
|
||||||
|
* object passed into {@link #captureValues(TransitionValues, boolean)} that
|
||||||
|
* this transition cares about for the purposes of canceling overlapping animations.
|
||||||
|
* When any transition is started on a given scene root, all transitions
|
||||||
|
* currently running on that same scene root are checked to see whether the
|
||||||
|
* properties on which they based their animations agree with the end values of
|
||||||
|
* the same properties in the new transition. If the end values are not equal,
|
||||||
|
* then the old animation is canceled since the new transition will start a new
|
||||||
|
* animation to these new values. If the values are equal, the old animation is
|
||||||
|
* allowed to continue and no new animation is started for that transition.
|
||||||
|
*
|
||||||
|
* <p>A transition does not need to override this method. However, not doing so
|
||||||
|
* will mean that the cancellation logic outlined in the previous paragraph
|
||||||
|
* will be skipped for that transition, possibly leading to artifacts as
|
||||||
|
* old transitions and new transitions on the same targets run in parallel,
|
||||||
|
* animating views toward potentially different end values.</p>
|
||||||
|
*
|
||||||
|
* @return An array of property names as described in the class documentation for
|
||||||
|
* {@link TransitionValues}. The default implementation returns <code>null</code>.
|
||||||
|
*/
|
||||||
|
public String[] getTransitionProperties() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method is called by the transition's parent (all the way up to the
|
* This method is called by the transition's parent (all the way up to the
|
||||||
* topmost Transition in the hierarchy) with the sceneRoot and start/end
|
* topmost Transition in the hierarchy) with the sceneRoot and start/end
|
||||||
@ -210,8 +260,6 @@ public abstract class Transition implements Cloneable {
|
|||||||
if (DBG) {
|
if (DBG) {
|
||||||
Log.d(LOG_TAG, "play() for " + this);
|
Log.d(LOG_TAG, "play() for " + this);
|
||||||
}
|
}
|
||||||
mPlayStartValuesList.clear();
|
|
||||||
mPlayEndValuesList.clear();
|
|
||||||
ArrayMap<View, TransitionValues> endCopy =
|
ArrayMap<View, TransitionValues> endCopy =
|
||||||
new ArrayMap<View, TransitionValues>(endValues.viewValues);
|
new ArrayMap<View, TransitionValues>(endValues.viewValues);
|
||||||
SparseArray<TransitionValues> endIdCopy =
|
SparseArray<TransitionValues> endIdCopy =
|
||||||
@ -316,6 +364,7 @@ public abstract class Transition implements Cloneable {
|
|||||||
startValuesList.add(start);
|
startValuesList.add(start);
|
||||||
endValuesList.add(end);
|
endValuesList.add(end);
|
||||||
}
|
}
|
||||||
|
ArrayMap<Animator, AnimationInfo> runningAnimators = getRunningAnimators();
|
||||||
for (int i = 0; i < startValuesList.size(); ++i) {
|
for (int i = 0; i < startValuesList.size(); ++i) {
|
||||||
TransitionValues start = startValuesList.get(i);
|
TransitionValues start = startValuesList.get(i);
|
||||||
TransitionValues end = endValuesList.get(i);
|
TransitionValues end = endValuesList.get(i);
|
||||||
@ -345,14 +394,46 @@ public abstract class Transition implements Cloneable {
|
|||||||
// TODO: what to do about targetIds and itemIds?
|
// TODO: what to do about targetIds and itemIds?
|
||||||
Animator animator = play(sceneRoot, start, end);
|
Animator animator = play(sceneRoot, start, end);
|
||||||
if (animator != null) {
|
if (animator != null) {
|
||||||
mAnimatorMap.put(new Pair(start, end), animator);
|
// Save animation info for future cancellation purposes
|
||||||
// Note: we've already done the check against targetIDs in these lists
|
View view = null;
|
||||||
mPlayStartValuesList.add(start);
|
TransitionValues infoValues = null;
|
||||||
mPlayEndValuesList.add(end);
|
if (end != null) {
|
||||||
|
view = end.view;
|
||||||
|
String[] properties = getTransitionProperties();
|
||||||
|
if (view != null && properties != null && properties.length > 0) {
|
||||||
|
infoValues = new TransitionValues();
|
||||||
|
infoValues.view = view;
|
||||||
|
TransitionValues newValues = endValues.viewValues.get(view);
|
||||||
|
if (newValues != null) {
|
||||||
|
for (int j = 0; j < properties.length; ++j) {
|
||||||
|
infoValues.values.put(properties[j],
|
||||||
|
newValues.values.get(properties[j]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int numExistingAnims = runningAnimators.size();
|
||||||
|
for (int j = 0; j < numExistingAnims; ++j) {
|
||||||
|
Animator anim = runningAnimators.keyAt(j);
|
||||||
|
AnimationInfo info = runningAnimators.get(anim);
|
||||||
|
if (info.values != null && info.view == view &&
|
||||||
|
((info.name == null && getName() == null) ||
|
||||||
|
info.name.equals(getName()))) {
|
||||||
|
if (info.values.equals(infoValues)) {
|
||||||
|
// Favor the old animator
|
||||||
|
animator = null;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
view = (start != null) ? start.view : null;
|
||||||
|
}
|
||||||
|
if (animator != null) {
|
||||||
|
AnimationInfo info = new AnimationInfo(view, getName(), infoValues);
|
||||||
|
runningAnimators.put(animator, info);
|
||||||
|
mAnimators.add(animator);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if (DBG) {
|
|
||||||
View view = (end != null) ? end.view : start.view;
|
|
||||||
Log.d(LOG_TAG, " No change for view " + view);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -389,6 +470,15 @@ public abstract class Transition implements Cloneable {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static ArrayMap<Animator, AnimationInfo> getRunningAnimators() {
|
||||||
|
ArrayMap<Animator, AnimationInfo> runningAnimators = sRunningAnimators.get();
|
||||||
|
if (runningAnimators == null) {
|
||||||
|
runningAnimators = new ArrayMap<Animator, AnimationInfo>();
|
||||||
|
sRunningAnimators.set(runningAnimators);
|
||||||
|
}
|
||||||
|
return runningAnimators;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is called internally once all animations have been set up by the
|
* This is called internally once all animations have been set up by the
|
||||||
* transition hierarchy. \
|
* transition hierarchy. \
|
||||||
@ -396,28 +486,27 @@ public abstract class Transition implements Cloneable {
|
|||||||
* @hide
|
* @hide
|
||||||
*/
|
*/
|
||||||
protected void runAnimations() {
|
protected void runAnimations() {
|
||||||
if (DBG && mPlayStartValuesList.size() > 0) {
|
if (DBG) {
|
||||||
Log.d(LOG_TAG, "runAnimations (" + mPlayStartValuesList.size() + ") on " + this);
|
Log.d(LOG_TAG, "runAnimations() on " + this);
|
||||||
}
|
}
|
||||||
startTransition();
|
start();
|
||||||
// Now walk the list of TransitionValues, calling play for each pair
|
ArrayMap<Animator, AnimationInfo> runningAnimators = getRunningAnimators();
|
||||||
for (int i = 0; i < mPlayStartValuesList.size(); ++i) {
|
// Now start every Animator that was previously created for this transition in play()
|
||||||
TransitionValues start = mPlayStartValuesList.get(i);
|
for (Animator anim : mAnimators) {
|
||||||
TransitionValues end = mPlayEndValuesList.get(i);
|
|
||||||
Animator anim = mAnimatorMap.get(new Pair(start, end));
|
|
||||||
if (DBG) {
|
if (DBG) {
|
||||||
Log.d(LOG_TAG, " anim: " + anim);
|
Log.d(LOG_TAG, " anim: " + anim);
|
||||||
}
|
}
|
||||||
startTransition();
|
if (runningAnimators.containsKey(anim)) {
|
||||||
runAnimator(anim);
|
start();
|
||||||
|
runAnimator(anim, runningAnimators);
|
||||||
}
|
}
|
||||||
mPlayStartValuesList.clear();
|
}
|
||||||
mPlayEndValuesList.clear();
|
mAnimators.clear();
|
||||||
mAnimatorMap.clear();
|
end();
|
||||||
endTransition();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void runAnimator(Animator animator) {
|
private void runAnimator(Animator animator,
|
||||||
|
final ArrayMap<Animator, AnimationInfo> runningAnimators) {
|
||||||
if (animator != null) {
|
if (animator != null) {
|
||||||
// TODO: could be a single listener instance for all of them since it uses the param
|
// TODO: could be a single listener instance for all of them since it uses the param
|
||||||
animator.addListener(new AnimatorListenerAdapter() {
|
animator.addListener(new AnimatorListenerAdapter() {
|
||||||
@ -427,6 +516,7 @@ public abstract class Transition implements Cloneable {
|
|||||||
}
|
}
|
||||||
@Override
|
@Override
|
||||||
public void onAnimationEnd(Animator animation) {
|
public void onAnimationEnd(Animator animation) {
|
||||||
|
runningAnimators.remove(animation);
|
||||||
mCurrentAnimators.remove(animation);
|
mCurrentAnimators.remove(animation);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -690,12 +780,113 @@ public abstract class Transition implements Cloneable {
|
|||||||
return values;
|
return values;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pauses this transition, sending out calls to {@link
|
||||||
|
* TransitionListener#onTransitionPause(Transition)} to all listeners
|
||||||
|
* and pausing all running animators started by this transition.
|
||||||
|
*
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
public void pause() {
|
||||||
|
ArrayMap<Animator, AnimationInfo> runningAnimators = getRunningAnimators();
|
||||||
|
int numOldAnims = runningAnimators.size();
|
||||||
|
for (int i = numOldAnims - 1; i >= 0; i--) {
|
||||||
|
Animator anim = runningAnimators.keyAt(i);
|
||||||
|
anim.pause();
|
||||||
|
}
|
||||||
|
if (mListeners != null && mListeners.size() > 0) {
|
||||||
|
ArrayList<TransitionListener> tmpListeners =
|
||||||
|
(ArrayList<TransitionListener>) mListeners.clone();
|
||||||
|
int numListeners = tmpListeners.size();
|
||||||
|
for (int i = 0; i < numListeners; ++i) {
|
||||||
|
tmpListeners.get(i).onTransitionPause(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mPaused = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resumes this transition, sending out calls to {@link
|
||||||
|
* TransitionListener#onTransitionPause(Transition)} to all listeners
|
||||||
|
* and pausing all running animators started by this transition.
|
||||||
|
*
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
public void resume() {
|
||||||
|
if (mPaused) {
|
||||||
|
ArrayMap<Animator, AnimationInfo> runningAnimators = getRunningAnimators();
|
||||||
|
int numOldAnims = runningAnimators.size();
|
||||||
|
for (int i = numOldAnims - 1; i >= 0; i--) {
|
||||||
|
Animator anim = runningAnimators.keyAt(i);
|
||||||
|
anim.resume();
|
||||||
|
}
|
||||||
|
if (mListeners != null && mListeners.size() > 0) {
|
||||||
|
ArrayList<TransitionListener> tmpListeners =
|
||||||
|
(ArrayList<TransitionListener>) mListeners.clone();
|
||||||
|
int numListeners = tmpListeners.size();
|
||||||
|
for (int i = 0; i < numListeners; ++i) {
|
||||||
|
tmpListeners.get(i).onTransitionResume(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mPaused = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called by TransitionManager to play the transition. This calls
|
* Called by TransitionManager to play the transition. This calls
|
||||||
* play() to set things up and create all of the animations and then
|
* play() to set things up and create all of the animations and then
|
||||||
* runAnimations() to actually start the animations.
|
* runAnimations() to actually start the animations.
|
||||||
*/
|
*/
|
||||||
void playTransition(ViewGroup sceneRoot) {
|
void playTransition(ViewGroup sceneRoot) {
|
||||||
|
ArrayMap<Animator, AnimationInfo> runningAnimators = getRunningAnimators();
|
||||||
|
int numOldAnims = runningAnimators.size();
|
||||||
|
for (int i = numOldAnims - 1; i >= 0; i--) {
|
||||||
|
Animator anim = runningAnimators.keyAt(i);
|
||||||
|
if (anim != null) {
|
||||||
|
anim.resume();
|
||||||
|
AnimationInfo oldInfo = runningAnimators.get(anim);
|
||||||
|
if (oldInfo != null) {
|
||||||
|
boolean cancel = false;
|
||||||
|
TransitionValues oldValues = oldInfo.values;
|
||||||
|
View oldView = oldInfo.view;
|
||||||
|
TransitionValues newValues = mEndValues.viewValues != null ?
|
||||||
|
mEndValues.viewValues.get(oldView) : null;
|
||||||
|
if (oldValues == null || newValues == null) {
|
||||||
|
if (oldValues != null || newValues != null) {
|
||||||
|
cancel = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (String key : oldValues.values.keySet()) {
|
||||||
|
Object oldValue = oldValues.values.get(key);
|
||||||
|
Object newValue = newValues.values.get(key);
|
||||||
|
if ((oldValue == null && newValue != null) ||
|
||||||
|
(oldValue != null && !oldValue.equals(newValue))) {
|
||||||
|
cancel = true;
|
||||||
|
if (DBG) {
|
||||||
|
Log.d(LOG_TAG, "Transition.play: oldValue != newValue for " +
|
||||||
|
key + ": old, new = " + oldValue + ", " + newValue);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (cancel) {
|
||||||
|
if (anim.isRunning() || anim.isStarted()) {
|
||||||
|
if (DBG) {
|
||||||
|
Log.d(LOG_TAG, "Canceling anim " + anim);
|
||||||
|
}
|
||||||
|
anim.cancel();
|
||||||
|
} else {
|
||||||
|
if (DBG) {
|
||||||
|
Log.d(LOG_TAG, "removing anim from info list: " + anim);
|
||||||
|
}
|
||||||
|
runningAnimators.remove(anim);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// setup() must be called on entire transition hierarchy and set of views
|
// setup() must be called on entire transition hierarchy and set of views
|
||||||
// before calling play() on anything; every transition needs a chance to set up
|
// before calling play() on anything; every transition needs a chance to set up
|
||||||
// target views appropriately before transitions begin running
|
// target views appropriately before transitions begin running
|
||||||
@ -707,7 +898,7 @@ public abstract class Transition implements Cloneable {
|
|||||||
* This is a utility method used by subclasses to handle standard parts of
|
* This is a utility method used by subclasses to handle standard parts of
|
||||||
* setting up and running an Animator: it sets the {@link #getDuration()
|
* setting up and running an Animator: it sets the {@link #getDuration()
|
||||||
* duration} and the {@link #getStartDelay() startDelay}, starts the
|
* duration} and the {@link #getStartDelay() startDelay}, starts the
|
||||||
* animation, and, when the animator ends, calls {@link #endTransition()}.
|
* animation, and, when the animator ends, calls {@link #end()}.
|
||||||
*
|
*
|
||||||
* @param animator The Animator to be run during this transition.
|
* @param animator The Animator to be run during this transition.
|
||||||
*
|
*
|
||||||
@ -716,7 +907,7 @@ public abstract class Transition implements Cloneable {
|
|||||||
protected void animate(Animator animator) {
|
protected void animate(Animator animator) {
|
||||||
// TODO: maybe pass auto-end as a boolean parameter?
|
// TODO: maybe pass auto-end as a boolean parameter?
|
||||||
if (animator == null) {
|
if (animator == null) {
|
||||||
endTransition();
|
end();
|
||||||
} else {
|
} else {
|
||||||
if (getDuration() >= 0) {
|
if (getDuration() >= 0) {
|
||||||
animator.setDuration(getDuration());
|
animator.setDuration(getDuration());
|
||||||
@ -730,7 +921,7 @@ public abstract class Transition implements Cloneable {
|
|||||||
animator.addListener(new AnimatorListenerAdapter() {
|
animator.addListener(new AnimatorListenerAdapter() {
|
||||||
@Override
|
@Override
|
||||||
public void onAnimationEnd(Animator animation) {
|
public void onAnimationEnd(Animator animation) {
|
||||||
endTransition();
|
end();
|
||||||
animation.removeListener(this);
|
animation.removeListener(this);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -738,30 +929,6 @@ public abstract class Transition implements Cloneable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Subclasses may override to receive notice of when the transition starts.
|
|
||||||
* This is equivalent to listening for the
|
|
||||||
* {@link TransitionListener#onTransitionStart(Transition)} callback.
|
|
||||||
*/
|
|
||||||
protected void onTransitionStart() {
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Subclasses may override to receive notice of when the transition is
|
|
||||||
* canceled. This is equivalent to listening for the
|
|
||||||
* {@link TransitionListener#onTransitionCancel(Transition)} callback.
|
|
||||||
*/
|
|
||||||
protected void onTransitionCancel() {
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Subclasses may override to receive notice of when the transition ends.
|
|
||||||
* This is equivalent to listening for the
|
|
||||||
* {@link TransitionListener#onTransitionEnd(Transition)} callback.
|
|
||||||
*/
|
|
||||||
protected void onTransitionEnd() {
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method is called automatically by the transition and
|
* This method is called automatically by the transition and
|
||||||
* TransitionGroup classes prior to a Transition subclass starting;
|
* TransitionGroup classes prior to a Transition subclass starting;
|
||||||
@ -769,9 +936,8 @@ public abstract class Transition implements Cloneable {
|
|||||||
*
|
*
|
||||||
* @hide
|
* @hide
|
||||||
*/
|
*/
|
||||||
protected void startTransition() {
|
protected void start() {
|
||||||
if (mNumInstances == 0) {
|
if (mNumInstances == 0) {
|
||||||
onTransitionStart();
|
|
||||||
if (mListeners != null && mListeners.size() > 0) {
|
if (mListeners != null && mListeners.size() > 0) {
|
||||||
ArrayList<TransitionListener> tmpListeners =
|
ArrayList<TransitionListener> tmpListeners =
|
||||||
(ArrayList<TransitionListener>) mListeners.clone();
|
(ArrayList<TransitionListener>) mListeners.clone();
|
||||||
@ -790,15 +956,14 @@ public abstract class Transition implements Cloneable {
|
|||||||
* a transition did nothing (returned a null Animator from
|
* a transition did nothing (returned a null Animator from
|
||||||
* {@link Transition#play(ViewGroup, TransitionValues,
|
* {@link Transition#play(ViewGroup, TransitionValues,
|
||||||
* TransitionValues)}) or because the transition returned a valid
|
* TransitionValues)}) or because the transition returned a valid
|
||||||
* Animator and endTransition() was called in the onAnimationEnd()
|
* Animator and end() was called in the onAnimationEnd()
|
||||||
* callback of the AnimatorListener.
|
* callback of the AnimatorListener.
|
||||||
*
|
*
|
||||||
* @hide
|
* @hide
|
||||||
*/
|
*/
|
||||||
protected void endTransition() {
|
protected void end() {
|
||||||
--mNumInstances;
|
--mNumInstances;
|
||||||
if (mNumInstances == 0) {
|
if (mNumInstances == 0) {
|
||||||
onTransitionEnd();
|
|
||||||
if (mListeners != null && mListeners.size() > 0) {
|
if (mListeners != null && mListeners.size() > 0) {
|
||||||
ArrayList<TransitionListener> tmpListeners =
|
ArrayList<TransitionListener> tmpListeners =
|
||||||
(ArrayList<TransitionListener>) mListeners.clone();
|
(ArrayList<TransitionListener>) mListeners.clone();
|
||||||
@ -828,7 +993,7 @@ public abstract class Transition implements Cloneable {
|
|||||||
* This method cancels a transition that is currently running.
|
* This method cancels a transition that is currently running.
|
||||||
* Implementation TBD.
|
* Implementation TBD.
|
||||||
*/
|
*/
|
||||||
protected void cancelTransition() {
|
protected void cancel() {
|
||||||
// TODO: how does this work with instances?
|
// TODO: how does this work with instances?
|
||||||
// TODO: this doesn't actually do *anything* yet
|
// TODO: this doesn't actually do *anything* yet
|
||||||
int numAnimators = mCurrentAnimators.size();
|
int numAnimators = mCurrentAnimators.size();
|
||||||
@ -836,7 +1001,6 @@ public abstract class Transition implements Cloneable {
|
|||||||
Animator animator = mCurrentAnimators.get(i);
|
Animator animator = mCurrentAnimators.get(i);
|
||||||
animator.cancel();
|
animator.cancel();
|
||||||
}
|
}
|
||||||
onTransitionCancel();
|
|
||||||
if (mListeners != null && mListeners.size() > 0) {
|
if (mListeners != null && mListeners.size() > 0) {
|
||||||
ArrayList<TransitionListener> tmpListeners =
|
ArrayList<TransitionListener> tmpListeners =
|
||||||
(ArrayList<TransitionListener>) mListeners.clone();
|
(ArrayList<TransitionListener>) mListeners.clone();
|
||||||
@ -901,11 +1065,28 @@ public abstract class Transition implements Cloneable {
|
|||||||
Transition clone = null;
|
Transition clone = null;
|
||||||
try {
|
try {
|
||||||
clone = (Transition) super.clone();
|
clone = (Transition) super.clone();
|
||||||
|
clone.mAnimators = new ArrayList<Animator>();
|
||||||
} catch (CloneNotSupportedException e) {}
|
} catch (CloneNotSupportedException e) {}
|
||||||
|
|
||||||
return clone;
|
return clone;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the name of this Transition. This name is used internally to distinguish
|
||||||
|
* between different transitions to determine when interrupting transitions overlap.
|
||||||
|
* For example, a Move running on the same target view as another Move should determine
|
||||||
|
* whether the old transition is animating to different end values and should be
|
||||||
|
* canceled in favor of the new transition.
|
||||||
|
*
|
||||||
|
* <p>By default, a Transition's name is simply the value of {@link Class#getName()},
|
||||||
|
* but subclasses are free to override and return something different.</p>
|
||||||
|
*
|
||||||
|
* @return The name of this transition.
|
||||||
|
*/
|
||||||
|
public String getName() {
|
||||||
|
return mName;
|
||||||
|
}
|
||||||
|
|
||||||
String toString(String indent) {
|
String toString(String indent) {
|
||||||
String result = indent + getClass().getSimpleName() + "@" +
|
String result = indent + getClass().getSimpleName() + "@" +
|
||||||
Integer.toHexString(hashCode()) + ": ";
|
Integer.toHexString(hashCode()) + ": ";
|
||||||
@ -943,8 +1124,7 @@ public abstract class Transition implements Cloneable {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* A transition listener receives notifications from a transition.
|
* A transition listener receives notifications from a transition.
|
||||||
* Notifications indicate transition lifecycle events: when the transition
|
* Notifications indicate transition lifecycle events.
|
||||||
* begins, ends, or is canceled.
|
|
||||||
*/
|
*/
|
||||||
public static interface TransitionListener {
|
public static interface TransitionListener {
|
||||||
/**
|
/**
|
||||||
@ -957,7 +1137,7 @@ public abstract class Transition implements Cloneable {
|
|||||||
/**
|
/**
|
||||||
* Notification about the end of the transition. Canceled transitions
|
* Notification about the end of the transition. Canceled transitions
|
||||||
* will always notify listeners of both the cancellation and end
|
* will always notify listeners of both the cancellation and end
|
||||||
* events. That is, {@link #onTransitionEnd()} is always called,
|
* events. That is, {@link #onTransitionEnd(Transition)} is always called,
|
||||||
* regardless of whether the transition was canceled or played
|
* regardless of whether the transition was canceled or played
|
||||||
* through to completion.
|
* through to completion.
|
||||||
*
|
*
|
||||||
@ -967,10 +1147,38 @@ public abstract class Transition implements Cloneable {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Notification about the cancellation of the transition.
|
* Notification about the cancellation of the transition.
|
||||||
|
* Note that cancel() may be called by a parent {@link TransitionGroup} on
|
||||||
|
* a child transition which has not yet started. This allows the child
|
||||||
|
* transition to restore state on target objects which was set at
|
||||||
|
* {@link #play(android.view.ViewGroup, TransitionValues, TransitionValues)
|
||||||
|
* play()} time.
|
||||||
*
|
*
|
||||||
* @param transition The transition which was canceled.
|
* @param transition The transition which was canceled.
|
||||||
*/
|
*/
|
||||||
void onTransitionCancel(Transition transition);
|
void onTransitionCancel(Transition transition);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notification when a transition is paused.
|
||||||
|
* Note that play() may be called by a parent {@link TransitionGroup} on
|
||||||
|
* a child transition which has not yet started. This allows the child
|
||||||
|
* transition to restore state on target objects which was set at
|
||||||
|
* {@link #play(android.view.ViewGroup, TransitionValues, TransitionValues)
|
||||||
|
* play()} time.
|
||||||
|
*
|
||||||
|
* @param transition The transition which was paused.
|
||||||
|
*/
|
||||||
|
void onTransitionPause(Transition transition);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notification when a transition is resumed.
|
||||||
|
* Note that resume() may be called by a parent {@link TransitionGroup} on
|
||||||
|
* a child transition which has not yet started. This allows the child
|
||||||
|
* transition to restore state which may have changed in an earlier call
|
||||||
|
* to {@link #onTransitionPause(Transition)}.
|
||||||
|
*
|
||||||
|
* @param transition The transition which was resumed.
|
||||||
|
*/
|
||||||
|
void onTransitionResume(Transition transition);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -991,6 +1199,32 @@ public abstract class Transition implements Cloneable {
|
|||||||
@Override
|
@Override
|
||||||
public void onTransitionCancel(Transition transition) {
|
public void onTransitionCancel(Transition transition) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onTransitionPause(Transition transition) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onTransitionResume(Transition transition) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds information about each animator used when a new transition starts
|
||||||
|
* while other transitions are still running to determine whether a running
|
||||||
|
* animation should be canceled or a new animation noop'd. The structure holds
|
||||||
|
* information about the state that an animation is going to, to be compared to
|
||||||
|
* end state of a new animation.
|
||||||
|
*/
|
||||||
|
private static class AnimationInfo {
|
||||||
|
View view;
|
||||||
|
String name;
|
||||||
|
TransitionValues values;
|
||||||
|
|
||||||
|
AnimationInfo(View view, String name, TransitionValues values) {
|
||||||
|
this.view = view;
|
||||||
|
this.name = name;
|
||||||
|
this.values = values;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -164,7 +164,7 @@ public class TransitionGroup extends Transition {
|
|||||||
@Override
|
@Override
|
||||||
public void onTransitionStart(Transition transition) {
|
public void onTransitionStart(Transition transition) {
|
||||||
if (!mTransitionGroup.mStarted) {
|
if (!mTransitionGroup.mStarted) {
|
||||||
mTransitionGroup.startTransition();
|
mTransitionGroup.start();
|
||||||
mTransitionGroup.mStarted = true;
|
mTransitionGroup.mStarted = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -175,7 +175,7 @@ public class TransitionGroup extends Transition {
|
|||||||
if (mTransitionGroup.mCurrentListeners == 0) {
|
if (mTransitionGroup.mCurrentListeners == 0) {
|
||||||
// All child trans
|
// All child trans
|
||||||
mTransitionGroup.mStarted = false;
|
mTransitionGroup.mStarted = false;
|
||||||
mTransitionGroup.endTransition();
|
mTransitionGroup.end();
|
||||||
}
|
}
|
||||||
transition.removeListener(this);
|
transition.removeListener(this);
|
||||||
}
|
}
|
||||||
@ -233,12 +233,32 @@ public class TransitionGroup extends Transition {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @hide */
|
||||||
@Override
|
@Override
|
||||||
protected void cancelTransition() {
|
public void pause() {
|
||||||
super.cancelTransition();
|
super.pause();
|
||||||
int numTransitions = mTransitions.size();
|
int numTransitions = mTransitions.size();
|
||||||
for (int i = 0; i < numTransitions; ++i) {
|
for (int i = 0; i < numTransitions; ++i) {
|
||||||
mTransitions.get(i).cancelTransition();
|
mTransitions.get(i).pause();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @hide */
|
||||||
|
@Override
|
||||||
|
public void resume() {
|
||||||
|
super.resume();
|
||||||
|
int numTransitions = mTransitions.size();
|
||||||
|
for (int i = 0; i < numTransitions; ++i) {
|
||||||
|
mTransitions.get(i).resume();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void cancel() {
|
||||||
|
super.cancel();
|
||||||
|
int numTransitions = mTransitions.size();
|
||||||
|
for (int i = 0; i < numTransitions; ++i) {
|
||||||
|
mTransitions.get(i).cancel();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,6 +18,7 @@ package android.view.transition;
|
|||||||
|
|
||||||
import android.util.ArrayMap;
|
import android.util.ArrayMap;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.view.ViewTreeObserver;
|
import android.view.ViewTreeObserver;
|
||||||
|
|
||||||
@ -45,8 +46,8 @@ public class TransitionManager {
|
|||||||
ArrayMap<Scene, Transition> mSceneTransitions = new ArrayMap<Scene, Transition>();
|
ArrayMap<Scene, Transition> mSceneTransitions = new ArrayMap<Scene, Transition>();
|
||||||
ArrayMap<Scene, ArrayMap<Scene, Transition>> mScenePairTransitions =
|
ArrayMap<Scene, ArrayMap<Scene, Transition>> mScenePairTransitions =
|
||||||
new ArrayMap<Scene, ArrayMap<Scene, Transition>>();
|
new ArrayMap<Scene, ArrayMap<Scene, Transition>>();
|
||||||
static ArrayMap<ViewGroup, Transition> sRunningTransitions =
|
private static ThreadLocal<ArrayMap<ViewGroup, ArrayList<Transition>>> sRunningTransitions =
|
||||||
new ArrayMap<ViewGroup, Transition>();
|
new ThreadLocal<ArrayMap<ViewGroup, ArrayList<Transition>>>();
|
||||||
private static ArrayList<ViewGroup> sPendingTransitions = new ArrayList<ViewGroup>();
|
private static ArrayList<ViewGroup> sPendingTransitions = new ArrayList<ViewGroup>();
|
||||||
|
|
||||||
|
|
||||||
@ -160,6 +161,16 @@ public class TransitionManager {
|
|||||||
sceneChangeRunTransition(sceneRoot, transitionClone);
|
sceneChangeRunTransition(sceneRoot, transitionClone);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static ArrayMap<ViewGroup, ArrayList<Transition>> getRunningTransitions() {
|
||||||
|
ArrayMap<ViewGroup, ArrayList<Transition>> runningTransitions =
|
||||||
|
sRunningTransitions.get();
|
||||||
|
if (runningTransitions == null) {
|
||||||
|
runningTransitions = new ArrayMap<ViewGroup, ArrayList<Transition>>();
|
||||||
|
sRunningTransitions.set(runningTransitions);
|
||||||
|
}
|
||||||
|
return runningTransitions;
|
||||||
|
}
|
||||||
|
|
||||||
private static void sceneChangeRunTransition(final ViewGroup sceneRoot,
|
private static void sceneChangeRunTransition(final ViewGroup sceneRoot,
|
||||||
final Transition transition) {
|
final Transition transition) {
|
||||||
if (transition != null) {
|
if (transition != null) {
|
||||||
@ -169,16 +180,31 @@ public class TransitionManager {
|
|||||||
sceneRoot.getViewTreeObserver().removeOnPreDrawListener(this);
|
sceneRoot.getViewTreeObserver().removeOnPreDrawListener(this);
|
||||||
sPendingTransitions.remove(sceneRoot);
|
sPendingTransitions.remove(sceneRoot);
|
||||||
// Add to running list, handle end to remove it
|
// Add to running list, handle end to remove it
|
||||||
sRunningTransitions.put(sceneRoot, transition);
|
final ArrayMap<ViewGroup, ArrayList<Transition>> runningTransitions =
|
||||||
|
getRunningTransitions();
|
||||||
|
ArrayList<Transition> currentTransitions = runningTransitions.get(sceneRoot);
|
||||||
|
if (currentTransitions == null) {
|
||||||
|
currentTransitions = new ArrayList<Transition>();
|
||||||
|
runningTransitions.put(sceneRoot, currentTransitions);
|
||||||
|
}
|
||||||
|
currentTransitions.add(transition);
|
||||||
transition.addListener(new Transition.TransitionListenerAdapter() {
|
transition.addListener(new Transition.TransitionListenerAdapter() {
|
||||||
@Override
|
@Override
|
||||||
public void onTransitionEnd(Transition transition) {
|
public void onTransitionEnd(Transition transition) {
|
||||||
sRunningTransitions.remove(sceneRoot);
|
ArrayList<Transition> currentTransitions =
|
||||||
|
runningTransitions.get(sceneRoot);
|
||||||
|
currentTransitions.remove(transition);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
transition.captureValues(sceneRoot, false);
|
transition.captureValues(sceneRoot, false);
|
||||||
transition.playTransition(sceneRoot);
|
transition.playTransition(sceneRoot);
|
||||||
return true;
|
|
||||||
|
// Returning false from onPreDraw() skips the current frame. This is
|
||||||
|
// necessary to avoid artifacts caused by resetting target views
|
||||||
|
// to their proper end states for capturing. Waiting until the next
|
||||||
|
// frame to draw allows these views to have their mid-transition
|
||||||
|
// values set on them again and avoid artifacts.
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -187,16 +213,18 @@ public class TransitionManager {
|
|||||||
private static void sceneChangeSetup(ViewGroup sceneRoot, Transition transition) {
|
private static void sceneChangeSetup(ViewGroup sceneRoot, Transition transition) {
|
||||||
|
|
||||||
// Capture current values
|
// Capture current values
|
||||||
Transition runningTransition = sRunningTransitions.get(sceneRoot);
|
ArrayList<Transition> runningTransitions = getRunningTransitions().get(sceneRoot);
|
||||||
|
|
||||||
|
if (runningTransitions != null && runningTransitions.size() > 0) {
|
||||||
|
for (Transition runningTransition : runningTransitions) {
|
||||||
|
runningTransition.pause();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (transition != null) {
|
if (transition != null) {
|
||||||
transition.captureValues(sceneRoot, true);
|
transition.captureValues(sceneRoot, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (runningTransition != null) {
|
|
||||||
runningTransition.cancelTransition();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Notify previous scene that it is being exited
|
// Notify previous scene that it is being exited
|
||||||
Scene previousScene = sceneRoot.getCurrentScene();
|
Scene previousScene = sceneRoot.getCurrentScene();
|
||||||
if (previousScene != null) {
|
if (previousScene != null) {
|
||||||
|
@ -19,6 +19,7 @@ package android.view.transition;
|
|||||||
import android.animation.Animator;
|
import android.animation.Animator;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
import android.view.ViewOverlay;
|
||||||
import android.view.ViewParent;
|
import android.view.ViewParent;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -38,6 +39,10 @@ public abstract class Visibility extends Transition {
|
|||||||
|
|
||||||
private static final String PROPNAME_VISIBILITY = "android:visibility:visibility";
|
private static final String PROPNAME_VISIBILITY = "android:visibility:visibility";
|
||||||
private static final String PROPNAME_PARENT = "android:visibility:parent";
|
private static final String PROPNAME_PARENT = "android:visibility:parent";
|
||||||
|
private static String[] sTransitionProperties = {
|
||||||
|
PROPNAME_VISIBILITY,
|
||||||
|
PROPNAME_PARENT,
|
||||||
|
};
|
||||||
|
|
||||||
private static class VisibilityInfo {
|
private static class VisibilityInfo {
|
||||||
boolean visibilityChange;
|
boolean visibilityChange;
|
||||||
@ -51,6 +56,11 @@ public abstract class Visibility extends Transition {
|
|||||||
// Temporary structure, used in calculating state in setup() and play()
|
// Temporary structure, used in calculating state in setup() and play()
|
||||||
private VisibilityInfo mTmpVisibilityInfo = new VisibilityInfo();
|
private VisibilityInfo mTmpVisibilityInfo = new VisibilityInfo();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String[] getTransitionProperties() {
|
||||||
|
return sTransitionProperties;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void captureValues(TransitionValues values, boolean start) {
|
protected void captureValues(TransitionValues values, boolean start) {
|
||||||
int visibility = values.view.getVisibility();
|
int visibility = values.view.getVisibility();
|
||||||
@ -58,6 +68,31 @@ public abstract class Visibility extends Transition {
|
|||||||
values.values.put(PROPNAME_PARENT, values.view.getParent());
|
values.values.put(PROPNAME_PARENT, values.view.getParent());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether the view is 'visible' according to the given values
|
||||||
|
* object. This is determined by testing the same properties in the values
|
||||||
|
* object that are used to determine whether the object is appearing or
|
||||||
|
* disappearing in the {@link
|
||||||
|
* #play(android.view.ViewGroup, TransitionValues, TransitionValues)}
|
||||||
|
* method. This method can be called by, for example, subclasses that want
|
||||||
|
* to know whether the object is visible in the same way that Visibility
|
||||||
|
* determines it for the actual animation.
|
||||||
|
*
|
||||||
|
* @param values The TransitionValues object that holds the information by
|
||||||
|
* which visibility is determined.
|
||||||
|
* @return True if the view reference by <code>values</code> is visible,
|
||||||
|
* false otherwise.
|
||||||
|
*/
|
||||||
|
public boolean isVisible(TransitionValues values) {
|
||||||
|
if (values == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
int visibility = (Integer) values.values.get(PROPNAME_VISIBILITY);
|
||||||
|
View parent = (View) values.values.get(PROPNAME_PARENT);
|
||||||
|
|
||||||
|
return visibility == View.VISIBLE && parent != null;
|
||||||
|
}
|
||||||
|
|
||||||
private boolean isHierarchyVisibilityChanging(ViewGroup sceneRoot, ViewGroup view) {
|
private boolean isHierarchyVisibilityChanging(ViewGroup sceneRoot, ViewGroup view) {
|
||||||
|
|
||||||
if (view == sceneRoot) {
|
if (view == sceneRoot) {
|
||||||
@ -197,5 +232,4 @@ public abstract class Visibility extends Transition {
|
|||||||
TransitionValues endValues, int endVisibility) {
|
TransitionValues endValues, int endVisibility) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user