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