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