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 ChangeImageTransform()73 public ChangeImageTransform() {} 74 ChangeImageTransform(Context context, AttributeSet attrs)75 public ChangeImageTransform(Context context, AttributeSet attrs) { 76 super(context, attrs); 77 } 78 captureValues(TransitionValues transitionValues)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 captureStartValues(TransitionValues transitionValues)123 public void captureStartValues(TransitionValues transitionValues) { 124 captureValues(transitionValues); 125 } 126 127 @Override captureEndValues(TransitionValues transitionValues)128 public void captureEndValues(TransitionValues transitionValues) { 129 captureValues(transitionValues); 130 } 131 132 @Override getTransitionProperties()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 createAnimator(ViewGroup sceneRoot, TransitionValues startValues, TransitionValues endValues)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 createNullAnimator(ImageView imageView)190 private ObjectAnimator createNullAnimator(ImageView imageView) { 191 return ObjectAnimator.ofObject(imageView, ANIMATED_TRANSFORM_PROPERTY, 192 NULL_MATRIX_EVALUATOR, null, null); 193 } 194 createMatrixAnimator(final ImageView imageView, Matrix startMatrix, final Matrix endMatrix)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