Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 608b87d9 authored by George Mount's avatar George Mount
Browse files

Add Transform and ClipBounds Transitions.

Made MoveImage use an overlay view. Drawables
cover all Views in the overlay and there may
be a desire to order the overlays.

Change-Id: Ic7b81f0d26d8cce3f475c2eebbce01538bc55d46
parent f976c3d4
Loading
Loading
Loading
Loading
+12 −0
Original line number Diff line number Diff line
@@ -27952,6 +27952,18 @@ package android.transition {
    method public void setResizeClip(boolean);
  }
  public class ChangeClipBounds extends android.transition.Transition {
    ctor public ChangeClipBounds();
    method public void captureEndValues(android.transition.TransitionValues);
    method public void captureStartValues(android.transition.TransitionValues);
  }
  public class ChangeTransform extends android.transition.Transition {
    ctor public ChangeTransform();
    method public void captureEndValues(android.transition.TransitionValues);
    method public void captureStartValues(android.transition.TransitionValues);
  }
  public class CircularPropagation extends android.transition.VisibilityPropagation {
    ctor public CircularPropagation();
    method public long getStartDelay(android.view.ViewGroup, android.transition.Transition, android.transition.TransitionValues, android.transition.TransitionValues);
+96 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2014 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package android.transition;

import android.animation.Animator;
import android.animation.ObjectAnimator;
import android.animation.RectEvaluator;
import android.graphics.Rect;
import android.view.View;
import android.view.ViewGroup;

/**
 * ChangeClipBounds captures the {@link android.view.View#getClipBounds()} before and after the
 * scene change and animates those changes during the transition.
 */
public class ChangeClipBounds extends Transition {

    private static final String TAG = "ChangeTransform";

    private static final String PROPNAME_CLIP = "android:clipBounds:clip";
    private static final String PROPNAME_BOUNDS = "android:clipBounds:bounds";

    private static final String[] sTransitionProperties = {
            PROPNAME_CLIP,
    };

    @Override
    public String[] getTransitionProperties() {
        return sTransitionProperties;
    }

    private void captureValues(TransitionValues values) {
        View view = values.view;
        if (view.getVisibility() == View.GONE) {
            return;
        }

        Rect clip = view.getClipBounds();
        values.values.put(PROPNAME_CLIP, clip);
        if (clip == null) {
            Rect bounds = new Rect(0, 0, view.getWidth(), view.getHeight());
            values.values.put(PROPNAME_BOUNDS, bounds);
        }
    }

    @Override
    public void captureStartValues(TransitionValues transitionValues) {
        captureValues(transitionValues);
    }

    @Override
    public void captureEndValues(TransitionValues transitionValues) {
        captureValues(transitionValues);
    }

    @Override
    public Animator createAnimator(final ViewGroup sceneRoot, TransitionValues startValues,
            TransitionValues endValues) {
        if (startValues == null || endValues == null
                || !startValues.values.containsKey(PROPNAME_CLIP)
                || !endValues.values.containsKey(PROPNAME_CLIP)) {
            return null;
        }
        Rect start = (Rect) startValues.values.get(PROPNAME_CLIP);
        Rect end = (Rect) endValues.values.get(PROPNAME_CLIP);
        if (start == null && end == null) {
            return null; // No animation required since there is no clip.
        }

        if (start == null) {
            start = (Rect) startValues.values.get(PROPNAME_BOUNDS);
        } else if (end == null) {
            end = (Rect) endValues.values.get(PROPNAME_BOUNDS);
        }
        if (start.equals(end)) {
            return null;
        }

        endValues.view.setClipBounds(start);
        RectEvaluator evaluator = new RectEvaluator(new Rect());
        return ObjectAnimator.ofObject(endValues.view, "clipBounds", evaluator, start, end);
    }
}
+163 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2014 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package android.transition;

import android.animation.Animator;
import android.animation.FloatArrayEvaluator;
import android.animation.ObjectAnimator;
import android.util.FloatProperty;
import android.util.Property;
import android.view.View;
import android.view.ViewGroup;

/**
 * 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>
 */
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[] sTransitionProperties = {
            PROPNAME_SCALE_X,
            PROPNAME_SCALE_Y,
            PROPNAME_ROTATION_X,
            PROPNAME_ROTATION_Y,
            PROPNAME_ROTATION_Z,
    };

    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;
        }

        @Override
        public void set(View view, float[] values) {
            for (int i = 0; i < values.length; i++) {
                float value = values[i];
                if (value != Float.NaN) {
                    sChangedProperties[i].setValue(view, value);
                }
            }
        }
    };

    @Override
    public String[] getTransitionProperties() {
        return sTransitionProperties;
    }

    private void captureValues(TransitionValues values) {
        View view = values.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());
    }

    @Override
    public void captureStartValues(TransitionValues transitionValues) {
        captureValues(transitionValues);
    }

    @Override
    public void captureEndValues(TransitionValues transitionValues) {
        captureValues(transitionValues);
    }

    @Override
    public Animator createAnimator(final ViewGroup sceneRoot, 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)) {
            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]);
            }
        }
        FloatArrayEvaluator evaluator = new FloatArrayEvaluator(new float[start.length]);
        return ObjectAnimator.ofObject(endValues.view, TRANSFORMS, evaluator, start, end);
    }

    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]);
        }
        return values;
    }

    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);

        // 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;
    }

    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;
            }
        }
        return false;
    }
}
+17 −5
Original line number Diff line number Diff line
@@ -170,13 +170,20 @@ public class MoveImage extends Transition {

        drawable = drawable.getConstantState().newDrawable();
        final MatrixClippedDrawable matrixClippedDrawable = new MatrixClippedDrawable(drawable);
        final ImageView overlayImage = new ImageView(imageView.getContext());
        final ViewGroupOverlay overlay = sceneRoot.getOverlay();
        overlay.add(overlayImage);
        overlayImage.setLeft(0);
        overlayImage.setTop(0);
        overlayImage.setRight(sceneRoot.getWidth());
        overlayImage.setBottom(sceneRoot.getBottom());
        overlayImage.setScaleType(ImageView.ScaleType.MATRIX);
        overlayImage.setImageDrawable(matrixClippedDrawable);
        matrixClippedDrawable.setMatrix(startMatrix);
        matrixClippedDrawable.setBounds(startBounds);
        matrixClippedDrawable.setClipRect(startClip);

        imageView.setVisibility(View.INVISIBLE);
        final ViewGroupOverlay overlay = sceneRoot.getOverlay();
        overlay.add(matrixClippedDrawable);
        ObjectAnimator animator = ObjectAnimator.ofPropertyValuesHolder(matrixClippedDrawable,
                changes.toArray(new PropertyValuesHolder[changes.size()]));

@@ -184,19 +191,24 @@ public class MoveImage extends Transition {
            @Override
            public void onAnimationEnd(Animator animation) {
                imageView.setVisibility(View.VISIBLE);
                overlay.remove(matrixClippedDrawable);
                overlay.remove(overlayImage);
            }

            @Override
            public void onAnimationPause(Animator animation) {
                imageView.setVisibility(View.VISIBLE);
                overlay.remove(matrixClippedDrawable);
                overlayImage.setVisibility(View.INVISIBLE);
            }

            @Override
            public void onAnimationResume(Animator animation) {
                imageView.setVisibility(View.INVISIBLE);
                overlay.add(matrixClippedDrawable);
                overlayImage.setVisibility(View.VISIBLE);
            }

            @Override
            public void onAnimationCancel(Animator animation) {
                onAnimationEnd(animation);
            }
        };

+5 −6
Original line number Diff line number Diff line
@@ -66,13 +66,12 @@ import java.util.List;
 *
 * {@sample development/samples/ApiDemos/res/transition/changebounds.xml ChangeBounds}
 *
 * <p>{@link android.transition.Explode} transition:</p>
 * <p>This TransitionSet contains {@link android.transition.Explode} for visibility,
 * {@link android.transition.ChangeBounds}, {@link android.transition.ChangeTransform},
 * and {@link android.transition.ChangeClipBounds} for non-<code>ImageView</code>s and
 * {@link android.transition.MoveImage} for <code>ImageView</code>s:</p>
 *
 * {@sample development/samples/ApiDemos/res/transition/explode.xml Explode}
 *
 * <p>{@link android.transition.MoveImage} transition:</p>
 *
 * {@sample development/samples/ApiDemos/res/transition/move_image.xml MoveImage}
 * {@sample development/samples/ApiDemos/res/transition/explode_move_together.xml MultipleTransform}
 *
 * <p>Note that attributes for the transition are not required, just as they are
 * optional when declared in code; Transitions created from XML resources will use
Loading