Home | History | Annotate | Download | only in transition
      1 /*
      2  * Copyright (C) 2014 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 package android.transition;
     17 
     18 import android.animation.Animator;
     19 import android.animation.ObjectAnimator;
     20 import android.animation.TypeEvaluator;
     21 import android.content.Context;
     22 import android.graphics.Matrix;
     23 import android.graphics.Rect;
     24 import android.graphics.drawable.Drawable;
     25 import android.util.AttributeSet;
     26 import android.util.Property;
     27 import android.view.View;
     28 import android.view.ViewGroup;
     29 import android.widget.ImageView;
     30 
     31 import java.util.Map;
     32 
     33 /**
     34  * This Transition captures an ImageView's matrix before and after the
     35  * scene change and animates it during the transition.
     36  *
     37  * <p>In combination with ChangeBounds, ChangeImageTransform allows ImageViews
     38  * that change size, shape, or {@link android.widget.ImageView.ScaleType} to animate contents
     39  * smoothly.</p>
     40  */
     41 public class ChangeImageTransform extends Transition {
     42 
     43     private static final String TAG = "ChangeImageTransform";
     44 
     45     private static final String PROPNAME_MATRIX = "android:changeImageTransform:matrix";
     46     private static final String PROPNAME_BOUNDS = "android:changeImageTransform:bounds";
     47 
     48     private static final String[] sTransitionProperties = {
     49             PROPNAME_MATRIX,
     50             PROPNAME_BOUNDS,
     51     };
     52 
     53     private static TypeEvaluator<Matrix> NULL_MATRIX_EVALUATOR = new TypeEvaluator<Matrix>() {
     54         @Override
     55         public Matrix evaluate(float fraction, Matrix startValue, Matrix endValue) {
     56             return null;
     57         }
     58     };
     59 
     60     private static Property<ImageView, Matrix> ANIMATED_TRANSFORM_PROPERTY
     61             = new Property<ImageView, Matrix>(Matrix.class, "animatedTransform") {
     62         @Override
     63         public void set(ImageView object, Matrix value) {
     64             object.animateTransform(value);
     65         }
     66 
     67         @Override
     68         public Matrix get(ImageView object) {
     69             return null;
     70         }
     71     };
     72 
     73     public ChangeImageTransform() {}
     74 
     75     public ChangeImageTransform(Context context, AttributeSet attrs) {
     76         super(context, attrs);
     77     }
     78 
     79     private void captureValues(TransitionValues transitionValues) {
     80         View view = transitionValues.view;
     81         if (!(view instanceof ImageView) || view.getVisibility() != View.VISIBLE) {
     82             return;
     83         }
     84         ImageView imageView = (ImageView) view;
     85         Drawable drawable = imageView.getDrawable();
     86         if (drawable == null) {
     87             return;
     88         }
     89         Map<String, Object> values = transitionValues.values;
     90 
     91         int left = view.getLeft();
     92         int top = view.getTop();
     93         int right = view.getRight();
     94         int bottom = view.getBottom();
     95 
     96         Rect bounds = new Rect(left, top, right, bottom);
     97         values.put(PROPNAME_BOUNDS, bounds);
     98         Matrix matrix;
     99         ImageView.ScaleType scaleType = imageView.getScaleType();
    100         if (scaleType == ImageView.ScaleType.FIT_XY) {
    101             matrix = imageView.getImageMatrix();
    102             if (!matrix.isIdentity()) {
    103                 matrix = new Matrix(matrix);
    104             } else {
    105                 int drawableWidth = drawable.getIntrinsicWidth();
    106                 int drawableHeight = drawable.getIntrinsicHeight();
    107                 if (drawableWidth > 0 && drawableHeight > 0) {
    108                     float scaleX = ((float) bounds.width()) / drawableWidth;
    109                     float scaleY = ((float) bounds.height()) / drawableHeight;
    110                     matrix = new Matrix();
    111                     matrix.setScale(scaleX, scaleY);
    112                 } else {
    113                     matrix = null;
    114                 }
    115             }
    116         } else {
    117             matrix = new Matrix(imageView.getImageMatrix());
    118         }
    119         values.put(PROPNAME_MATRIX, matrix);
    120     }
    121 
    122     @Override
    123     public void captureStartValues(TransitionValues transitionValues) {
    124         captureValues(transitionValues);
    125     }
    126 
    127     @Override
    128     public void captureEndValues(TransitionValues transitionValues) {
    129         captureValues(transitionValues);
    130     }
    131 
    132     @Override
    133     public String[] getTransitionProperties() {
    134         return sTransitionProperties;
    135     }
    136 
    137     /**
    138      * Creates an Animator for ImageViews moving, changing dimensions, and/or changing
    139      * {@link android.widget.ImageView.ScaleType}.
    140      *
    141      * @param sceneRoot   The root of the transition hierarchy.
    142      * @param startValues The values for a specific target in the start scene.
    143      * @param endValues   The values for the target in the end scene.
    144      * @return An Animator to move an ImageView or null if the View is not an ImageView,
    145      * the Drawable changed, the View is not VISIBLE, or there was no change.
    146      */
    147     @Override
    148     public Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues,
    149             TransitionValues endValues) {
    150         if (startValues == null || endValues == null) {
    151             return null;
    152         }
    153         Rect startBounds = (Rect) startValues.values.get(PROPNAME_BOUNDS);
    154         Rect endBounds = (Rect) endValues.values.get(PROPNAME_BOUNDS);
    155         if (startBounds == null || endBounds == null) {
    156             return null;
    157         }
    158 
    159         Matrix startMatrix = (Matrix) startValues.values.get(PROPNAME_MATRIX);
    160         Matrix endMatrix = (Matrix) endValues.values.get(PROPNAME_MATRIX);
    161 
    162         boolean matricesEqual = (startMatrix == null && endMatrix == null) ||
    163                 (startMatrix != null && startMatrix.equals(endMatrix));
    164 
    165         if (startBounds.equals(endBounds) && matricesEqual) {
    166             return null;
    167         }
    168 
    169         ImageView imageView = (ImageView) endValues.view;
    170         Drawable drawable = imageView.getDrawable();
    171         int drawableWidth = drawable.getIntrinsicWidth();
    172         int drawableHeight = drawable.getIntrinsicHeight();
    173 
    174         ObjectAnimator animator;
    175         if (drawableWidth == 0 || drawableHeight == 0) {
    176             animator = createNullAnimator(imageView);
    177         } else {
    178             if (startMatrix == null) {
    179                 startMatrix = Matrix.IDENTITY_MATRIX;
    180             }
    181             if (endMatrix == null) {
    182                 endMatrix = Matrix.IDENTITY_MATRIX;
    183             }
    184             ANIMATED_TRANSFORM_PROPERTY.set(imageView, startMatrix);
    185             animator = createMatrixAnimator(imageView, startMatrix, endMatrix);
    186         }
    187         return animator;
    188     }
    189 
    190     private ObjectAnimator createNullAnimator(ImageView imageView) {
    191         return ObjectAnimator.ofObject(imageView, ANIMATED_TRANSFORM_PROPERTY,
    192                 NULL_MATRIX_EVALUATOR, null, null);
    193     }
    194 
    195     private ObjectAnimator createMatrixAnimator(final ImageView imageView, Matrix startMatrix,
    196             final Matrix endMatrix) {
    197         return ObjectAnimator.ofObject(imageView, ANIMATED_TRANSFORM_PROPERTY,
    198                 new TransitionUtils.MatrixEvaluator(), startMatrix, endMatrix);
    199     }
    200 }
    201