ChangeTransformpublic class ChangeTransform extends Transition This Transition captures scale and rotation for Views before and after the
scene change and animates those changes during the transition.
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. |
Fields Summary |
---|
private static final String | TAG | private static final String | PROPNAME_MATRIX | private static final String | PROPNAME_TRANSFORMS | private static final String | PROPNAME_PARENT | private static final String | PROPNAME_PARENT_MATRIX | private static final String | PROPNAME_INTERMEDIATE_PARENT_MATRIX | private static final String | PROPNAME_INTERMEDIATE_MATRIX | private static final String[] | sTransitionProperties | private static final android.util.Property | NON_TRANSLATIONS_PROPERTYThis property sets the animation matrix properties that are not translations. | private static final android.util.Property | TRANSLATIONS_PROPERTYThis property sets the translation animation matrix properties. | private boolean | mUseOverlay | private boolean | mReparent | private android.graphics.Matrix | mTempMatrix |
Constructors Summary |
---|
public ChangeTransform()
| public ChangeTransform(android.content.Context context, android.util.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();
|
Methods Summary |
---|
public void | captureEndValues(TransitionValues transitionValues)
captureValues(transitionValues);
| public void | captureStartValues(TransitionValues transitionValues)
captureValues(transitionValues);
| private void | captureValues(TransitionValues transitionValues)
View view = transitionValues.view;
if (view.getVisibility() == View.GONE) {
return;
}
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);
transitionValues.values.put(PROPNAME_INTERMEDIATE_MATRIX,
view.getTag(R.id.transitionTransform));
transitionValues.values.put(PROPNAME_INTERMEDIATE_PARENT_MATRIX,
view.getTag(R.id.parentMatrix));
}
return;
| public android.animation.Animator | createAnimator(android.view.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.values.get(PROPNAME_INTERMEDIATE_MATRIX);
if (startMatrix != null) {
startValues.values.put(PROPNAME_MATRIX, startMatrix);
}
Matrix startParentMatrix = (Matrix)
startValues.values.get(PROPNAME_INTERMEDIATE_PARENT_MATRIX);
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,
handleParentChange);
if (handleParentChange && transformAnimator != null && mUseOverlay) {
createGhostView(sceneRoot, startValues, endValues);
}
return transformAnimator;
| private void | createGhostView(android.view.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;
}
GhostListener listener = new GhostListener(view, startValues.view, ghostView);
outerTransition.addListener(listener);
if (startValues.view != endValues.view) {
startValues.view.setTransitionAlpha(0);
}
view.setTransitionAlpha(1);
| private android.animation.ObjectAnimator | createTransformAnimator(TransitionValues startValues, TransitionValues endValues, boolean handleParentChange)
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;
}
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);
final float[] startMatrixValues = new float[9];
startMatrix.getValues(startMatrixValues);
final float[] endMatrixValues = new float[9];
endMatrix.getValues(endMatrixValues);
final PathAnimatorMatrix pathAnimatorMatrix =
new PathAnimatorMatrix(view, startMatrixValues);
PropertyValuesHolder valuesProperty = PropertyValuesHolder.ofObject(
NON_TRANSLATIONS_PROPERTY, new FloatArrayEvaluator(new float[9]),
startMatrixValues, endMatrixValues);
Path path = getPathMotion().getPath(startMatrixValues[Matrix.MTRANS_X],
startMatrixValues[Matrix.MTRANS_Y], endMatrixValues[Matrix.MTRANS_X],
endMatrixValues[Matrix.MTRANS_Y]);
PropertyValuesHolder translationProperty = PropertyValuesHolder.ofObject(
TRANSLATIONS_PROPERTY, null, path);
ObjectAnimator animator = ObjectAnimator.ofPropertyValuesHolder(pathAnimatorMatrix,
valuesProperty, translationProperty);
final Matrix finalEndMatrix = endMatrix;
AnimatorListenerAdapter listener = new AnimatorListenerAdapter() {
private boolean mIsCanceled;
private Matrix mTempMatrix = new Matrix();
@Override
public void onAnimationCancel(Animator animation) {
mIsCanceled = true;
}
@Override
public void onAnimationEnd(Animator animation) {
if (!mIsCanceled) {
if (handleParentChange && mUseOverlay) {
setCurrentMatrix(finalEndMatrix);
} else {
view.setTagInternal(R.id.transitionTransform, null);
view.setTagInternal(R.id.parentMatrix, null);
}
}
view.setAnimationMatrix(null);
transforms.restore(view);
}
@Override
public void onAnimationPause(Animator animation) {
Matrix currentMatrix = pathAnimatorMatrix.getMatrix();
setCurrentMatrix(currentMatrix);
}
@Override
public void onAnimationResume(Animator animation) {
setIdentityTransforms(view);
}
private void setCurrentMatrix(Matrix currentMatrix) {
mTempMatrix.set(currentMatrix);
view.setTagInternal(R.id.transitionTransform, mTempMatrix);
transforms.restore(view);
}
};
animator.addListener(listener);
animator.addPauseListener(listener);
return animator;
| public boolean | getReparent()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 mReparent;
| public boolean | getReparentWithOverlay()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 true .
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.
return mUseOverlay;
| public java.lang.String[] | getTransitionProperties()
return sTransitionProperties;
| private boolean | parentsMatch(android.view.ViewGroup startParent, android.view.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;
}
}
return parentsMatch;
| private static void | setIdentityTransforms(android.view.View view)
setTransforms(view, 0, 0, 0, 1, 1, 0, 0, 0);
| 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);
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);
| public void | setReparent(boolean reparent)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.
mReparent = reparent;
| public void | setReparentWithOverlay(boolean reparentWithOverlay)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 true .
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.
mUseOverlay = reparentWithOverlay;
| private static void | setTransforms(android.view.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);
|
|