FileDocCategorySizeDatePackage
ChangeTransform.javaAPI DocAndroid 5.1 API21433Thu Mar 12 22:22:10 GMT 2015android.transition

ChangeTransform

public 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_PROPERTY
This property sets the animation matrix properties that are not translations.
private static final android.util.Property
TRANSLATIONS_PROPERTY
This 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 voidcaptureEndValues(TransitionValues transitionValues)

        captureValues(transitionValues);
    
public voidcaptureStartValues(TransitionValues transitionValues)

        captureValues(transitionValues);
    
private voidcaptureValues(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.AnimatorcreateAnimator(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 voidcreateGhostView(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.ObjectAnimatorcreateTransformAnimator(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 booleangetReparent()
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.
attr
ref android.R.styleable#ChangeTransform_reparent

        return mReparent;
    
public booleangetReparentWithOverlay()
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
true when a changed parent should execute the transition inside the scene root's overlay or false if a parent change only affects the transform of the transitioning view.
attr
ref android.R.styleable#ChangeTransform_reparentWithOverlay

        return mUseOverlay;
    
public java.lang.String[]getTransitionProperties()

        return sTransitionProperties;
    
private booleanparentsMatch(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 voidsetIdentityTransforms(android.view.View view)

        setTransforms(view, 0, 0, 0, 1, 1, 0, 0, 0);
    
private voidsetMatricesForParent(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 voidsetReparent(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.

param
reparent Set to true to track parent changes or false to only track changes of the transitioning view without considering the parent change.
attr
ref android.R.styleable#ChangeTransform_reparent

        mReparent = reparent;
    
public voidsetReparentWithOverlay(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.

return
true when a changed parent should execute the transition inside the scene root's overlay or false if a parent change only affects the transform of the transitioning view.
attr
ref android.R.styleable#ChangeTransform_reparentWithOverlay

        mUseOverlay = reparentWithOverlay;
    
private static voidsetTransforms(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);