summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--api/current.txt8
-rw-r--r--core/java/android/transition/ChangeBounds.java2
-rw-r--r--core/java/android/transition/ChangeImageTransform.java25
-rw-r--r--core/java/android/transition/ChangeTransform.java437
-rw-r--r--core/java/android/transition/Transition.java42
-rw-r--r--core/java/android/transition/TransitionUtils.java23
-rw-r--r--core/java/android/transition/Visibility.java2
-rw-r--r--core/java/android/view/View.java9
-rw-r--r--core/res/res/values/attrs.xml13
-rw-r--r--core/res/res/values/ids.xml2
-rw-r--r--core/res/res/values/public.xml2
-rw-r--r--core/res/res/values/symbols.xml2
12 files changed, 442 insertions, 125 deletions
diff --git a/api/current.txt b/api/current.txt
index aac7fcd393be..dbd64a1c2b94 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -1028,6 +1028,8 @@ package android {
field public static final int readPermission = 16842759; // 0x1010007
field public static final int recognitionService = 16843932; // 0x101049c
field public static final int relinquishTaskIdentity = 16843894; // 0x1010476
+ field public static final int reparent = 16843965; // 0x10104bd
+ field public static final int reparentWithOverlay = 16843966; // 0x10104be
field public static final int repeatCount = 16843199; // 0x10101bf
field public static final int repeatMode = 16843200; // 0x10101c0
field public static final int reqFiveWayNav = 16843314; // 0x1010232
@@ -31804,7 +31806,7 @@ package android.transition {
ctor public ChangeBounds(android.content.Context, android.util.AttributeSet);
method public void captureEndValues(android.transition.TransitionValues);
method public void captureStartValues(android.transition.TransitionValues);
- method public void setReparent(boolean);
+ method public deprecated void setReparent(boolean);
method public void setResizeClip(boolean);
}
@@ -31827,6 +31829,10 @@ package android.transition {
ctor public ChangeTransform(android.content.Context, android.util.AttributeSet);
method public void captureEndValues(android.transition.TransitionValues);
method public void captureStartValues(android.transition.TransitionValues);
+ method public boolean getReparent();
+ method public boolean getReparentWithOverlay();
+ method public void setReparent(boolean);
+ method public void setReparentWithOverlay(boolean);
}
public class CircularPropagation extends android.transition.VisibilityPropagation {
diff --git a/core/java/android/transition/ChangeBounds.java b/core/java/android/transition/ChangeBounds.java
index 6246cbe003cd..ebb1a5cf5e78 100644
--- a/core/java/android/transition/ChangeBounds.java
+++ b/core/java/android/transition/ChangeBounds.java
@@ -107,6 +107,8 @@ public class ChangeBounds extends Transition {
*
* @param reparent true if the transition should track the parent
* container of target views and animate parent changes.
+ * @deprecated Use {@link android.transition.ChangeTransform} to handle
+ * transitions between different parents.
*/
public void setReparent(boolean reparent) {
mReparent = reparent;
diff --git a/core/java/android/transition/ChangeImageTransform.java b/core/java/android/transition/ChangeImageTransform.java
index 4b230eb0bae8..d7a912057858 100644
--- a/core/java/android/transition/ChangeImageTransform.java
+++ b/core/java/android/transition/ChangeImageTransform.java
@@ -16,7 +16,6 @@
package android.transition;
import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.animation.TypeEvaluator;
import android.content.Context;
@@ -196,28 +195,6 @@ public class ChangeImageTransform extends Transition {
private ObjectAnimator createMatrixAnimator(final ImageView imageView, Matrix startMatrix,
final Matrix endMatrix) {
return ObjectAnimator.ofObject(imageView, ANIMATED_TRANSFORM_PROPERTY,
- new MatrixEvaluator(), startMatrix, endMatrix);
+ new TransitionUtils.MatrixEvaluator(), startMatrix, endMatrix);
}
-
- private static class MatrixEvaluator implements TypeEvaluator<Matrix> {
-
- float[] mTempStartValues = new float[9];
-
- float[] mTempEndValues = new float[9];
-
- Matrix mTempMatrix = new Matrix();
-
- @Override
- public Matrix evaluate(float fraction, Matrix startValue, Matrix endValue) {
- startValue.getValues(mTempStartValues);
- endValue.getValues(mTempEndValues);
- for (int i = 0; i < 9; i++) {
- float diff = mTempEndValues[i] - mTempStartValues[i];
- mTempEndValues[i] = mTempStartValues[i] + (fraction * diff);
- }
- mTempMatrix.setValues(mTempEndValues);
- return mTempMatrix;
- }
- }
-
}
diff --git a/core/java/android/transition/ChangeTransform.java b/core/java/android/transition/ChangeTransform.java
index e9be3b90872c..d579f54d3ca3 100644
--- a/core/java/android/transition/ChangeTransform.java
+++ b/core/java/android/transition/ChangeTransform.java
@@ -16,72 +16,134 @@
package android.transition;
import android.animation.Animator;
-import android.animation.FloatArrayEvaluator;
+import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
+import android.animation.ValueAnimator;
import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Matrix;
import android.util.AttributeSet;
-import android.util.FloatProperty;
import android.util.Property;
+import android.view.GhostView;
import android.view.View;
import android.view.ViewGroup;
+import com.android.internal.R;
/**
* This Transition captures scale and rotation for Views before and after the
* scene change and animates those changes during the transition.
*
- * <p>ChangeTransform does not work when the pivot changes between scenes, so either the
- * pivot must be set to prevent automatic pivot adjustment or the View's size must be unchanged.</p>
+ * A change in parent is handled as well by capturing the transforms from
+ * the parent before and after the scene change and animating those during the
+ * transition.
*/
public class ChangeTransform extends Transition {
private static final String TAG = "ChangeTransform";
- private static final String PROPNAME_SCALE_X = "android:changeTransform:scaleX";
- private static final String PROPNAME_SCALE_Y = "android:changeTransform:scaleY";
- private static final String PROPNAME_ROTATION_X = "android:changeTransform:rotationX";
- private static final String PROPNAME_ROTATION_Y = "android:changeTransform:rotationY";
- private static final String PROPNAME_ROTATION_Z = "android:changeTransform:rotationZ";
- private static final String PROPNAME_PIVOT_X = "android:changeTransform:pivotX";
- private static final String PROPNAME_PIVOT_Y = "android:changeTransform:pivotY";
+ private static final String PROPNAME_MATRIX = "android:changeTransform:matrix";
+ private static final String PROPNAME_TRANSFORMS = "android:changeTransform:transforms";
+ private static final String PROPNAME_PARENT = "android:changeTransform:parent";
+ private static final String PROPNAME_PARENT_MATRIX = "android:changeTransform:parentMatrix";
private static final String[] sTransitionProperties = {
- PROPNAME_SCALE_X,
- PROPNAME_SCALE_Y,
- PROPNAME_ROTATION_X,
- PROPNAME_ROTATION_Y,
- PROPNAME_ROTATION_Z,
+ PROPNAME_MATRIX,
+ PROPNAME_TRANSFORMS,
+ PROPNAME_PARENT_MATRIX,
};
- private static final FloatProperty<View>[] sChangedProperties = new FloatProperty[] {
- (FloatProperty) View.SCALE_X,
- (FloatProperty) View.SCALE_Y,
- (FloatProperty) View.ROTATION_X,
- (FloatProperty) View.ROTATION_Y,
- (FloatProperty) View.ROTATION,
- };
-
- private static Property<View, float[]> TRANSFORMS = new Property<View, float[]>(float[].class,
- "transforms") {
- @Override
- public float[] get(View object) {
- return null;
- }
+ private static final Property<View, Matrix> ANIMATION_MATRIX_PROPERTY =
+ new Property<View, Matrix>(Matrix.class, "animationMatrix") {
+ @Override
+ public Matrix get(View object) {
+ return null;
+ }
- @Override
- public void set(View view, float[] values) {
- for (int i = 0; i < values.length; i++) {
- float value = values[i];
- if (!Float.isNaN(value)) {
- sChangedProperties[i].setValue(view, value);
+ @Override
+ public void set(View object, Matrix value) {
+ object.setAnimationMatrix(value);
}
- }
- }
- };
+ };
+
+ private boolean mUseOverlay = true;
+ private boolean mReparent = true;
+ private Matrix mTempMatrix = new Matrix();
public ChangeTransform() {}
public ChangeTransform(Context context, AttributeSet attrs) {
super(context, attrs);
+ TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ChangeTransform);
+ mUseOverlay = a.getBoolean(R.styleable.ChangeTransform_reparentWithOverlay, true);
+ mReparent = a.getBoolean(R.styleable.ChangeTransform_reparent, true);
+ a.recycle();
+ }
+
+ /**
+ * Returns whether changes to parent should use an overlay or not. When the parent
+ * change doesn't use an overlay, it affects the transforms of the child. The
+ * default value is <code>true</code>.
+ *
+ * <p>Note: when Overlays are not used when a parent changes, a view can be clipped when
+ * it moves outside the bounds of its parent. Setting
+ * {@link android.view.ViewGroup#setClipChildren(boolean)} and
+ * {@link android.view.ViewGroup#setClipToPadding(boolean)} can help. Also, when
+ * Overlays are not used and the parent is animating its location, the position of the
+ * child view will be relative to its parent's final position, so it may appear to "jump"
+ * at the beginning.</p>
+ *
+ * @return <code>true</code> when a changed parent should execute the transition
+ * inside the scene root's overlay or <code>false</code> if a parent change only
+ * affects the transform of the transitioning view.
+ */
+ public boolean getReparentWithOverlay() {
+ return mUseOverlay;
+ }
+
+ /**
+ * Sets whether changes to parent should use an overlay or not. When the parent
+ * change doesn't use an overlay, it affects the transforms of the child. The
+ * default value is <code>true</code>.
+ *
+ * <p>Note: when Overlays are not used when a parent changes, a view can be clipped when
+ * it moves outside the bounds of its parent. Setting
+ * {@link android.view.ViewGroup#setClipChildren(boolean)} and
+ * {@link android.view.ViewGroup#setClipToPadding(boolean)} can help. Also, when
+ * Overlays are not used and the parent is animating its location, the position of the
+ * child view will be relative to its parent's final position, so it may appear to "jump"
+ * at the beginning.</p>
+ *
+ * @return <code>true</code> when a changed parent should execute the transition
+ * inside the scene root's overlay or <code>false</code> if a parent change only
+ * affects the transform of the transitioning view.
+ */
+ public void setReparentWithOverlay(boolean reparentWithOverlay) {
+ mUseOverlay = reparentWithOverlay;
+ }
+
+ /**
+ * Returns whether parent changes will be tracked by the ChangeTransform. If parent
+ * changes are tracked, then the transform will adjust to the transforms of the
+ * different parents. If they aren't tracked, only the transforms of the transitioning
+ * view will be tracked. Default is true.
+ *
+ * @return whether parent changes will be tracked by the ChangeTransform.
+ */
+ public boolean getReparent() {
+ return mReparent;
+ }
+
+ /**
+ * Sets whether parent changes will be tracked by the ChangeTransform. If parent
+ * changes are tracked, then the transform will adjust to the transforms of the
+ * different parents. If they aren't tracked, only the transforms of the transitioning
+ * view will be tracked. Default is true.
+ *
+ * @param reparent Set to true to track parent changes or false to only track changes
+ * of the transitioning view without considering the parent change.
+ */
+ public void setReparent(boolean reparent) {
+ mReparent = reparent;
}
@Override
@@ -89,19 +151,29 @@ public class ChangeTransform extends Transition {
return sTransitionProperties;
}
- private void captureValues(TransitionValues values) {
- View view = values.view;
+ private void captureValues(TransitionValues transitionValues) {
+ View view = transitionValues.view;
if (view.getVisibility() == View.GONE) {
return;
}
-
- values.values.put(PROPNAME_SCALE_X, view.getScaleX());
- values.values.put(PROPNAME_SCALE_Y, view.getScaleY());
- values.values.put(PROPNAME_PIVOT_X, view.getPivotX());
- values.values.put(PROPNAME_PIVOT_Y, view.getPivotY());
- values.values.put(PROPNAME_ROTATION_X, view.getRotationX());
- values.values.put(PROPNAME_ROTATION_Y, view.getRotationY());
- values.values.put(PROPNAME_ROTATION_Z, view.getRotation());
+ transitionValues.values.put(PROPNAME_PARENT, view.getParent());
+ Transforms transforms = new Transforms(view);
+ transitionValues.values.put(PROPNAME_TRANSFORMS, transforms);
+ Matrix matrix = view.getMatrix();
+ if (matrix == null || matrix.isIdentity()) {
+ matrix = null;
+ } else {
+ matrix = new Matrix(matrix);
+ }
+ transitionValues.values.put(PROPNAME_MATRIX, matrix);
+ if (mReparent) {
+ Matrix parentMatrix = new Matrix();
+ ViewGroup parent = (ViewGroup) view.getParent();
+ parent.transformMatrixToGlobal(parentMatrix);
+ parentMatrix.preTranslate(-parent.getScrollX(), -parent.getScrollY());
+ transitionValues.values.put(PROPNAME_PARENT_MATRIX, parentMatrix);
+ }
+ return;
}
@Override
@@ -115,57 +187,252 @@ public class ChangeTransform extends Transition {
}
@Override
- public Animator createAnimator(final ViewGroup sceneRoot, TransitionValues startValues,
+ public Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues,
+ TransitionValues endValues) {
+ if (startValues == null || endValues == null ||
+ !startValues.values.containsKey(PROPNAME_PARENT) ||
+ !endValues.values.containsKey(PROPNAME_PARENT)) {
+ return null;
+ }
+
+ ViewGroup startParent = (ViewGroup) startValues.values.get(PROPNAME_PARENT);
+ ViewGroup endParent = (ViewGroup) endValues.values.get(PROPNAME_PARENT);
+ boolean handleParentChange = mReparent && !parentsMatch(startParent, endParent);
+
+ Matrix startMatrix = (Matrix) startValues.view.getTag(R.id.transitionTransform);
+ if (startMatrix != null) {
+ startValues.values.put(PROPNAME_MATRIX, startMatrix);
+ }
+
+ Matrix startParentMatrix = (Matrix) startValues.view.getTag(R.id.parentMatrix);
+ if (startParentMatrix != null) {
+ startValues.values.put(PROPNAME_PARENT_MATRIX, startParentMatrix);
+ }
+
+ // First handle the parent change:
+ if (handleParentChange) {
+ setMatricesForParent(startValues, endValues);
+ }
+
+ // Next handle the normal matrix transform:
+ ObjectAnimator transformAnimator = createTransformAnimator(startValues, endValues);
+
+ if (handleParentChange && transformAnimator != null && mUseOverlay) {
+ createGhostView(sceneRoot, startValues, endValues);
+ }
+
+ return transformAnimator;
+ }
+
+ private ObjectAnimator createTransformAnimator(TransitionValues startValues,
TransitionValues endValues) {
- if (startValues == null || endValues == null
- || !startValues.values.containsKey(PROPNAME_SCALE_X)
- || !endValues.values.containsKey(PROPNAME_SCALE_X)
- || !isPivotSame(startValues, endValues)
- || !isChanged(startValues, endValues)) {
+ Matrix startMatrix = (Matrix) startValues.values.get(PROPNAME_MATRIX);
+ Matrix endMatrix = (Matrix) endValues.values.get(PROPNAME_MATRIX);
+
+ if (startMatrix == null) {
+ startMatrix = Matrix.IDENTITY_MATRIX;
+ }
+
+ if (endMatrix == null) {
+ endMatrix = Matrix.IDENTITY_MATRIX;
+ }
+
+ if (startMatrix.equals(endMatrix)) {
return null;
}
- float[] start = createValues(startValues);
- float[] end = createValues(endValues);
- for (int i = 0; i < start.length; i++) {
- if (start[i] == end[i]) {
- start[i] = Float.NaN;
- end[i] = Float.NaN;
- } else {
- sChangedProperties[i].setValue(endValues.view, start[i]);
+ final Transforms transforms = (Transforms) endValues.values.get(PROPNAME_TRANSFORMS);
+
+ // clear the transform properties so that we can use the animation matrix instead
+ final View view = endValues.view;
+ setIdentityTransforms(view);
+
+ ObjectAnimator animator = ObjectAnimator.ofObject(view, ANIMATION_MATRIX_PROPERTY,
+ new TransitionUtils.MatrixEvaluator(), startMatrix, endMatrix);
+
+ AnimatorListenerAdapter listener = new AnimatorListenerAdapter() {
+ private boolean mIsCanceled;
+ private Matrix mTempMatrix;
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ mIsCanceled = true;
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ if (!mIsCanceled) {
+ view.setTagInternal(R.id.transitionTransform, null);
+ view.setTagInternal(R.id.parentMatrix, null);
+ }
+ ANIMATION_MATRIX_PROPERTY.set(view, null);
+ transforms.restore(view);
+ }
+
+ @Override
+ public void onAnimationPause(Animator animation) {
+ ValueAnimator animator = (ValueAnimator) animation;
+ Matrix currentMatrix = (Matrix) animator.getAnimatedValue();
+ if (mTempMatrix == null) {
+ mTempMatrix = new Matrix(currentMatrix);
+ } else {
+ mTempMatrix.set(currentMatrix);
+ }
+ view.setTagInternal(R.id.transitionTransform, mTempMatrix);
+ transforms.restore(view);
+ }
+
+ @Override
+ public void onAnimationResume(Animator animation) {
+ setIdentityTransforms(view);
+ }
+ };
+
+ animator.addListener(listener);
+ animator.addPauseListener(listener);
+ return animator;
+ }
+
+ private boolean parentsMatch(ViewGroup startParent, ViewGroup endParent) {
+ boolean parentsMatch = false;
+ if (!isValidTarget(startParent) || !isValidTarget(endParent)) {
+ parentsMatch = startParent == endParent;
+ } else {
+ TransitionValues endValues = getMatchedTransitionValues(startParent, true);
+ if (endValues != null) {
+ parentsMatch = endParent == endValues.view;
}
}
- FloatArrayEvaluator evaluator = new FloatArrayEvaluator(new float[start.length]);
- return ObjectAnimator.ofObject(endValues.view, TRANSFORMS, evaluator, start, end);
+ return parentsMatch;
}
- private static float[] createValues(TransitionValues transitionValues) {
- float[] values = new float[sChangedProperties.length];
- for (int i = 0; i < values.length; i++) {
- values[i] = (Float) transitionValues.values.get(sTransitionProperties[i]);
+ private void createGhostView(final ViewGroup sceneRoot, TransitionValues startValues,
+ TransitionValues endValues) {
+ View view = endValues.view;
+
+ Matrix endMatrix = (Matrix) endValues.values.get(PROPNAME_PARENT_MATRIX);
+ Matrix localEndMatrix = new Matrix(endMatrix);
+ sceneRoot.transformMatrixToLocal(localEndMatrix);
+
+ GhostView ghostView = GhostView.addGhost(view, sceneRoot, localEndMatrix);
+
+ Transition outerTransition = this;
+ while (outerTransition.mParent != null) {
+ outerTransition = outerTransition.mParent;
}
- return values;
+ GhostListener listener = new GhostListener(view, ghostView, endMatrix);
+ outerTransition.addListener(listener);
+
+ if (startValues.view != endValues.view) {
+ startValues.view.setTransitionAlpha(0);
+ }
+ view.setTransitionAlpha(1);
}
- private static boolean isPivotSame(TransitionValues startValues, TransitionValues endValues) {
- float startPivotX = (Float) startValues.values.get(PROPNAME_PIVOT_X);
- float startPivotY = (Float) startValues.values.get(PROPNAME_PIVOT_Y);
- float endPivotX = (Float) endValues.values.get(PROPNAME_PIVOT_X);
- float endPivotY = (Float) endValues.values.get(PROPNAME_PIVOT_Y);
+ private void setMatricesForParent(TransitionValues startValues, TransitionValues endValues) {
+ Matrix endParentMatrix = (Matrix) endValues.values.get(PROPNAME_PARENT_MATRIX);
+ endValues.view.setTagInternal(R.id.parentMatrix, endParentMatrix);
+
+ Matrix toLocal = mTempMatrix;
+ toLocal.reset();
+ endParentMatrix.invert(toLocal);
- // We don't support pivot changes, because they could be automatically set
- // and we can't end the state in an automatic state.
- return startPivotX == endPivotX && startPivotY == endPivotY;
+ Matrix startLocal = (Matrix) startValues.values.get(PROPNAME_MATRIX);
+ if (startLocal == null) {
+ startLocal = new Matrix();
+ startValues.values.put(PROPNAME_MATRIX, startLocal);
+ }
+
+ Matrix startParentMatrix = (Matrix) startValues.values.get(PROPNAME_PARENT_MATRIX);
+ startLocal.postConcat(startParentMatrix);
+ startLocal.postConcat(toLocal);
}
- private static boolean isChanged(TransitionValues startValues, TransitionValues endValues) {
- for (int i = 0; i < sChangedProperties.length; i++) {
- Object start = startValues.values.get(sTransitionProperties[i]);
- Object end = endValues.values.get(sTransitionProperties[i]);
- if (!start.equals(end)) {
- return true;
+ private static void setIdentityTransforms(View view) {
+ setTransforms(view, 0, 0, 0, 1, 1, 0, 0, 0);
+ }
+
+ private static void setTransforms(View view, float translationX, float translationY,
+ float translationZ, float scaleX, float scaleY, float rotationX,
+ float rotationY, float rotationZ) {
+ view.setTranslationX(translationX);
+ view.setTranslationY(translationY);
+ view.setTranslationZ(translationZ);
+ view.setScaleX(scaleX);
+ view.setScaleY(scaleY);
+ view.setRotationX(rotationX);
+ view.setRotationY(rotationY);
+ view.setRotation(rotationZ);
+ }
+
+ private static class Transforms {
+ public final float translationX;
+ public final float translationY;
+ public final float translationZ;
+ public final float scaleX;
+ public final float scaleY;
+ public final float rotationX;
+ public final float rotationY;
+ public final float rotationZ;
+
+ public Transforms(View view) {
+ translationX = view.getTranslationX();
+ translationY = view.getTranslationY();
+ translationZ = view.getTranslationZ();
+ scaleX = view.getScaleX();
+ scaleY = view.getScaleY();
+ rotationX = view.getRotationX();
+ rotationY = view.getRotationY();
+ rotationZ = view.getRotation();
+ }
+
+ public void restore(View view) {
+ setTransforms(view, translationX, translationY, translationZ, scaleX, scaleY,
+ rotationX, rotationY, rotationZ);
+ }
+
+ @Override
+ public boolean equals(Object that) {
+ if (!(that instanceof Transforms)) {
+ return false;
}
+ Transforms thatTransform = (Transforms) that;
+ return thatTransform.translationX == translationX &&
+ thatTransform.translationY == translationY &&
+ thatTransform.translationZ == translationZ &&
+ thatTransform.scaleX == scaleX &&
+ thatTransform.scaleY == scaleY &&
+ thatTransform.rotationX == rotationX &&
+ thatTransform.rotationY == rotationY &&
+ thatTransform.rotationZ == rotationZ;
+ }
+ }
+
+ private static class GhostListener extends Transition.TransitionListenerAdapter {
+ private View mView;
+ private GhostView mGhostView;
+ private Matrix mEndMatrix;
+
+ public GhostListener(View view, GhostView ghostView, Matrix endMatrix) {
+ mView = view;
+ mGhostView = ghostView;
+ mEndMatrix = endMatrix;
+ }
+
+ @Override
+ public void onTransitionEnd(Transition transition) {
+ transition.removeListener(this);
+ GhostView.removeGhost(mView);
+ }
+
+ @Override
+ public void onTransitionPause(Transition transition) {
+ mGhostView.setVisibility(View.INVISIBLE);
+ }
+
+ @Override
+ public void onTransitionResume(Transition transition) {
+ mGhostView.setVisibility(View.VISIBLE);
}
- return false;
}
}
diff --git a/core/java/android/transition/Transition.java b/core/java/android/transition/Transition.java
index 0d32d4038e29..59ba71fa8208 100644
--- a/core/java/android/transition/Transition.java
+++ b/core/java/android/transition/Transition.java
@@ -112,8 +112,8 @@ import com.android.internal.R;
*
* Further information on XML resource descriptions for transitions can be found for
* {@link android.R.styleable#Transition}, {@link android.R.styleable#TransitionSet},
- * {@link android.R.styleable#TransitionTarget}, {@link android.R.styleable#Fade}, and
- * {@link android.R.styleable#Slide}.
+ * {@link android.R.styleable#TransitionTarget}, {@link android.R.styleable#Fade},
+ * {@link android.R.styleable#Slide}, and {@link android.R.styleable#ChangeTransform}.
*
*/
public abstract class Transition implements Cloneable {
@@ -1755,24 +1755,40 @@ public abstract class Transition implements Cloneable {
// if oldValues null, then transition didn't care to stash values,
// and won't get canceled
if (oldValues != null && newValues != null) {
- for (String key : oldValues.values.keySet()) {
- Object oldValue = oldValues.values.get(key);
- Object newValue = newValues.values.get(key);
- if (oldValue != null && newValue != null &&
- !oldValue.equals(newValue)) {
- valuesChanged = true;
- if (DBG) {
- Log.d(LOG_TAG, "Transition.playTransition: " +
- "oldValue != newValue for " + key +
- ": old, new = " + oldValue + ", " + newValue);
+ String[] properties = getTransitionProperties();
+ if (properties != null) {
+ int count = properties.length;
+ for (int i = 0; i < count; i++) {
+ if (isValueChanged(oldValues, newValues, properties[i])) {
+ valuesChanged = true;
+ break;
+ }
+ }
+ } else {
+ for (String key : oldValues.values.keySet()) {
+ if (isValueChanged(oldValues, newValues, key)) {
+ valuesChanged = true;
+ break;
}
- break;
}
}
}
return valuesChanged;
}
+ private static boolean isValueChanged(TransitionValues oldValues, TransitionValues newValues,
+ String key) {
+ Object oldValue = oldValues.values.get(key);
+ Object newValue = newValues.values.get(key);
+ boolean changed = (oldValue != null && newValue != null && !oldValue.equals(newValue));
+ if (DBG && changed) {
+ Log.d(LOG_TAG, "Transition.playTransition: " +
+ "oldValue != newValue for " + key +
+ ": old, new = " + oldValue + ", " + newValue);
+ }
+ return changed;
+ }
+
/**
* This is a utility method used by subclasses to handle standard parts of
* setting up and running an Animator: it sets the {@link #getDuration()
diff --git a/core/java/android/transition/TransitionUtils.java b/core/java/android/transition/TransitionUtils.java
index 931d46a53187..b0c9e9adec22 100644
--- a/core/java/android/transition/TransitionUtils.java
+++ b/core/java/android/transition/TransitionUtils.java
@@ -18,6 +18,8 @@ package android.transition;
import android.animation.Animator;
import android.animation.AnimatorSet;
+import android.animation.TypeEvaluator;
+import android.graphics.Matrix;
/**
* Static utility methods for Transitions.
@@ -37,4 +39,25 @@ public class TransitionUtils {
return animatorSet;
}
}
+
+ public static class MatrixEvaluator implements TypeEvaluator<Matrix> {
+
+ float[] mTempStartValues = new float[9];
+
+ float[] mTempEndValues = new float[9];
+
+ Matrix mTempMatrix = new Matrix();
+
+ @Override
+ public Matrix evaluate(float fraction, Matrix startValue, Matrix endValue) {
+ startValue.getValues(mTempStartValues);
+ endValue.getValues(mTempEndValues);
+ for (int i = 0; i < 9; i++) {
+ float diff = mTempEndValues[i] - mTempStartValues[i];
+ mTempEndValues[i] = mTempStartValues[i] + (fraction * diff);
+ }
+ mTempMatrix.setValues(mTempEndValues);
+ return mTempMatrix;
+ }
+ }
}
diff --git a/core/java/android/transition/Visibility.java b/core/java/android/transition/Visibility.java
index 79dbb49f5245..81c69d18e82d 100644
--- a/core/java/android/transition/Visibility.java
+++ b/core/java/android/transition/Visibility.java
@@ -63,8 +63,6 @@ public abstract class Visibility extends Transition {
private static final String[] sTransitionProperties = {
PROPNAME_VISIBILITY,
- PROPNAME_PARENT,
- PROPNAME_SCREEN_LOCATION,
};
private static class VisibilityInfo {
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 243d7d797062..64d008347e1e 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -10793,6 +10793,15 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
}
}
+ /** @hide */
+ public void setAnimationMatrix(Matrix matrix) {
+ invalidateViewProperty(true, false);
+ mRenderNode.setAnimationMatrix(matrix);
+ invalidateViewProperty(false, true);
+
+ invalidateParentIfNeededAndWasQuickRejected();
+ }
+
/**
* Returns the current StateListAnimator if exists.
*
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 27b770141da9..23edc48b34d8 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -5597,6 +5597,19 @@
</attr>
</declare-styleable>
+ <!-- Use <code>changeTransform</code> as the root tag of the XML resource that
+ describes a {@link android.transition.ChangeTransform} transition. -->
+ <declare-styleable name="ChangeTransform">
+ <!-- A parent change should use an overlay or affect the transform of the
+ transitionining View. Default is true. Corresponds to
+ {@link android.transition.ChangeTransform#setReparentWithOverlay(boolean)}. -->
+ <attr name="reparentWithOverlay" format="boolean"/>
+
+ <!-- Tells ChangeTransform to track parent changes. Default is true. Corresponds to
+ {@link android.transition.ChangeTransform#setReparent(boolean)}. -->
+ <attr name="reparent" format="boolean"/>
+ </declare-styleable>
+
<!-- Use <code>transitionManager</code> as the root tag of the XML resource that
describes a {@link android.transition.TransitionManager
TransitionManager}. -->
diff --git a/core/res/res/values/ids.xml b/core/res/res/values/ids.xml
index c64e910e955d..4c59f73ff53e 100644
--- a/core/res/res/values/ids.xml
+++ b/core/res/res/values/ids.xml
@@ -85,4 +85,6 @@
<item type="id" name="scene_layoutid_cache" />
<item type="id" name="mask" />
<item type="id" name="transitionPosition" />
+ <item type="id" name="transitionTransform" />
+ <item type="id" name="parentMatrix" />
</resources>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index b3b5ca955136..f2466f78c326 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2276,6 +2276,8 @@
<public type="attr" name="ageHint" />
<public type="attr" name="country" />
<public type="attr" name="windowSharedElementsUseOverlay" />
+ <public type="attr" name="reparent" />
+ <public type="attr" name="reparentWithOverlay" />
<public-padding type="dimen" name="l_resource_pad" end="0x01050010" />
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 9b3c05efe373..9e18a46d0f8c 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1986,4 +1986,6 @@
<java-symbol type="color" name="battery_saver_mode_color" />
<java-symbol type="color" name="system_notification_accent_color" />
<java-symbol type="dimen" name="text_handle_min_size" />
+ <java-symbol type="id" name="transitionTransform" />
+ <java-symbol type="id" name="parentMatrix" />
</resources>