ChangeImageTransform.java revision 990205eada00ad3e575761d19607bb03e12f9aa3
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 */
16package android.transition;
17
18import android.animation.Animator;
19import android.animation.AnimatorListenerAdapter;
20import android.animation.ObjectAnimator;
21import android.animation.TypeEvaluator;
22import android.graphics.Matrix;
23import android.graphics.Rect;
24import android.graphics.drawable.Drawable;
25import android.util.Property;
26import android.view.View;
27import android.view.ViewGroup;
28import android.widget.ImageView;
29
30import java.util.Map;
31
32/**
33 * Transitions changes in ImageView {@link ImageView#setScaleType(ImageView.ScaleType)} as
34 * well as image scaling due to ImageView size changes. When combined with
35 * {@link android.transition.ChangeBounds}, an ImageView that changes size will
36 * scale smoothly.
37 */
38public class ChangeImageTransform extends Transition {
39
40    private static final String TAG = "ChangeScaleType";
41
42    private static final String PROPNAME_MATRIX = "android:changeScaleType:matrix";
43    private static final String PROPNAME_BOUNDS = "android:changeScaleType:bounds";
44
45    private static final String[] sTransitionProperties = {
46            PROPNAME_MATRIX,
47            PROPNAME_BOUNDS,
48    };
49
50    private static TypeEvaluator<Matrix> NULL_MATRIX_EVALUATOR = new TypeEvaluator<Matrix>() {
51        @Override
52        public Matrix evaluate(float fraction, Matrix startValue, Matrix endValue) {
53            return null;
54        }
55    };
56
57    private static Property<ImageView, Matrix> ANIMATED_TRANSFORM_PROPERTY
58            = new Property<ImageView, Matrix>(Matrix.class, "animatedTransform") {
59        @Override
60        public void set(ImageView object, Matrix value) {
61            object.animateTransform(value);
62        }
63
64        @Override
65        public Matrix get(ImageView object) {
66            return null;
67        }
68    };
69
70    private void captureValues(TransitionValues transitionValues) {
71        View view = transitionValues.view;
72        if (!(view instanceof ImageView) || view.getVisibility() != View.VISIBLE) {
73            return;
74        }
75        ImageView imageView = (ImageView) view;
76        Drawable drawable = imageView.getDrawable();
77        if (drawable == null) {
78            return;
79        }
80        Map<String, Object> values = transitionValues.values;
81
82        int left = view.getLeft();
83        int top = view.getTop();
84        int right = view.getRight();
85        int bottom = view.getBottom();
86
87        Rect bounds = new Rect(left, top, right, bottom);
88        values.put(PROPNAME_BOUNDS, bounds);
89        Matrix matrix;
90        ImageView.ScaleType scaleType = imageView.getScaleType();
91        if (scaleType == ImageView.ScaleType.FIT_XY) {
92            matrix = imageView.getImageMatrix();
93            if (!matrix.isIdentity()) {
94                matrix = new Matrix(matrix);
95            } else {
96                int drawableWidth = drawable.getIntrinsicWidth();
97                int drawableHeight = drawable.getIntrinsicHeight();
98                if (drawableWidth > 0 && drawableHeight > 0) {
99                    float scaleX = ((float) bounds.width()) / drawableWidth;
100                    float scaleY = ((float) bounds.height()) / drawableHeight;
101                    matrix = new Matrix();
102                    matrix.setScale(scaleX, scaleY);
103                } else {
104                    matrix = null;
105                }
106            }
107        } else {
108            matrix = new Matrix(imageView.getImageMatrix());
109        }
110        values.put(PROPNAME_MATRIX, matrix);
111    }
112
113    @Override
114    public void captureStartValues(TransitionValues transitionValues) {
115        captureValues(transitionValues);
116    }
117
118    @Override
119    public void captureEndValues(TransitionValues transitionValues) {
120        captureValues(transitionValues);
121    }
122
123    @Override
124    public String[] getTransitionProperties() {
125        return sTransitionProperties;
126    }
127
128    /**
129     * Creates an Animator for ImageViews moving, changing dimensions, and/or changing
130     * {@link android.widget.ImageView.ScaleType}.
131     *
132     * @param sceneRoot   The root of the transition hierarchy.
133     * @param startValues The values for a specific target in the start scene.
134     * @param endValues   The values for the target in the end scene.
135     * @return An Animator to move an ImageView or null if the View is not an ImageView,
136     * the Drawable changed, the View is not VISIBLE, or there was no change.
137     */
138    @Override
139    public Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues,
140            TransitionValues endValues) {
141        if (startValues == null || endValues == null) {
142            return null;
143        }
144        Rect startBounds = (Rect) startValues.values.get(PROPNAME_BOUNDS);
145        Rect endBounds = (Rect) endValues.values.get(PROPNAME_BOUNDS);
146        if (startBounds == null || endBounds == null) {
147            return null;
148        }
149
150        Matrix startMatrix = (Matrix) startValues.values.get(PROPNAME_MATRIX);
151        Matrix endMatrix = (Matrix) endValues.values.get(PROPNAME_MATRIX);
152
153        boolean matricesEqual = (startMatrix == null && endMatrix == null) ||
154                (startMatrix != null && startMatrix.equals(endMatrix));
155
156        if (startBounds.equals(endBounds) && matricesEqual) {
157            return null;
158        }
159
160        ImageView imageView = (ImageView) endValues.view;
161        Drawable drawable = imageView.getDrawable();
162        int drawableWidth = drawable.getIntrinsicWidth();
163        int drawableHeight = drawable.getIntrinsicHeight();
164
165        ObjectAnimator animator;
166        if (drawableWidth == 0 || drawableHeight == 0) {
167            animator = createNullAnimator(imageView);
168        } else {
169            if (startMatrix == null) {
170                startMatrix = Matrix.IDENTITY_MATRIX;
171            }
172            if (endMatrix == null) {
173                endMatrix = Matrix.IDENTITY_MATRIX;
174            }
175            animator = createMatrixAnimator(imageView, startMatrix, endMatrix);
176        }
177        return animator;
178    }
179
180    private ObjectAnimator createNullAnimator(ImageView imageView) {
181        return ObjectAnimator.ofObject(imageView, ANIMATED_TRANSFORM_PROPERTY,
182                NULL_MATRIX_EVALUATOR, null, null);
183    }
184
185    private ObjectAnimator createMatrixAnimator(final ImageView imageView, Matrix startMatrix,
186            final Matrix endMatrix) {
187        ObjectAnimator animator = ObjectAnimator.ofObject(imageView, ANIMATED_TRANSFORM_PROPERTY,
188                new MatrixEvaluator(), startMatrix, endMatrix);
189        /*
190        AnimatorListenerAdapter listener = new AnimatorListenerAdapter() {
191            private Matrix mPausedMatrix;
192
193            @Override
194            public void onAnimationPause(Animator animation) {
195                if (mPausedMatrix == null) {
196                    mPausedMatrix = new Matrix();
197                }
198                Matrix imageMatrix = imageView.getImageMatrix();
199                mPausedMatrix.set(imageMatrix);
200                imageView.animateTransform(endMatrix);
201            }
202
203            @Override
204            public void onAnimationResume(Animator animation) {
205                imageView.animateTransform(mPausedMatrix);
206            }
207        };
208        animator.addPauseListener(listener);
209        */
210        return animator;
211    }
212
213    private static class MatrixEvaluator implements TypeEvaluator<Matrix> {
214
215        float[] mTempStartValues = new float[9];
216
217        float[] mTempEndValues = new float[9];
218
219        Matrix mTempMatrix = new Matrix();
220
221        @Override
222        public Matrix evaluate(float fraction, Matrix startValue, Matrix endValue) {
223            startValue.getValues(mTempStartValues);
224            endValue.getValues(mTempEndValues);
225            for (int i = 0; i < 9; i++) {
226                float diff = mTempEndValues[i] - mTempStartValues[i];
227                mTempEndValues[i] = mTempStartValues[i] + (fraction * diff);
228            }
229            mTempMatrix.setValues(mTempEndValues);
230            return mTempMatrix;
231        }
232    }
233
234}
235