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.annotation.NonNull; 22 import android.annotation.Nullable; 23 import android.content.Context; 24 import android.graphics.Matrix; 25 import android.graphics.Rect; 26 import android.graphics.drawable.Drawable; 27 import android.util.AttributeSet; 28 import android.util.Property; 29 import android.view.View; 30 import android.view.ViewGroup; 31 import android.widget.ImageView; 32 33 import java.util.Map; 34 35 /** 36 * This Transition captures an ImageView's matrix before and after the 37 * scene change and animates it during the transition. 38 * 39 * <p>In combination with ChangeBounds, ChangeImageTransform allows ImageViews 40 * that change size, shape, or {@link android.widget.ImageView.ScaleType} to animate contents 41 * smoothly.</p> 42 */ 43 public class ChangeImageTransform extends Transition { 44 45 private static final String TAG = "ChangeImageTransform"; 46 47 private static final String PROPNAME_MATRIX = "android:changeImageTransform:matrix"; 48 private static final String PROPNAME_BOUNDS = "android:changeImageTransform:bounds"; 49 50 private static final String[] sTransitionProperties = { 51 PROPNAME_MATRIX, 52 PROPNAME_BOUNDS, 53 }; 54 55 private static TypeEvaluator<Matrix> NULL_MATRIX_EVALUATOR = new TypeEvaluator<Matrix>() { 56 @Override 57 public Matrix evaluate(float fraction, Matrix startValue, Matrix endValue) { 58 return null; 59 } 60 }; 61 62 private static Property<ImageView, Matrix> ANIMATED_TRANSFORM_PROPERTY 63 = new Property<ImageView, Matrix>(Matrix.class, "animatedTransform") { 64 @Override 65 public void set(ImageView object, Matrix value) { 66 object.animateTransform(value); 67 } 68 69 @Override 70 public Matrix get(ImageView object) { 71 return null; 72 } 73 }; 74 ChangeImageTransform()75 public ChangeImageTransform() {} 76 ChangeImageTransform(Context context, AttributeSet attrs)77 public ChangeImageTransform(Context context, AttributeSet attrs) { 78 super(context, attrs); 79 } 80 captureValues(TransitionValues transitionValues)81 private void captureValues(TransitionValues transitionValues) { 82 View view = transitionValues.view; 83 if (!(view instanceof ImageView) || view.getVisibility() != View.VISIBLE) { 84 return; 85 } 86 ImageView imageView = (ImageView) view; 87 Drawable drawable = imageView.getDrawable(); 88 if (drawable == null) { 89 return; 90 } 91 Map<String, Object> values = transitionValues.values; 92 93 int left = view.getLeft(); 94 int top = view.getTop(); 95 int right = view.getRight(); 96 int bottom = view.getBottom(); 97 98 Rect bounds = new Rect(left, top, right, bottom); 99 values.put(PROPNAME_BOUNDS, bounds); 100 Matrix matrix; 101 ImageView.ScaleType scaleType = imageView.getScaleType(); 102 int drawableWidth = drawable.getIntrinsicWidth(); 103 int drawableHeight = drawable.getIntrinsicHeight(); 104 if (scaleType == ImageView.ScaleType.FIT_XY && drawableWidth > 0 && drawableHeight > 0) { 105 float scaleX = ((float) bounds.width()) / drawableWidth; 106 float scaleY = ((float) bounds.height()) / drawableHeight; 107 matrix = new Matrix(); 108 matrix.setScale(scaleX, scaleY); 109 } else { 110 matrix = new Matrix(imageView.getImageMatrix()); 111 } 112 values.put(PROPNAME_MATRIX, matrix); 113 } 114 115 @Override captureStartValues(TransitionValues transitionValues)116 public void captureStartValues(TransitionValues transitionValues) { 117 captureValues(transitionValues); 118 } 119 120 @Override captureEndValues(TransitionValues transitionValues)121 public void captureEndValues(TransitionValues transitionValues) { 122 captureValues(transitionValues); 123 } 124 125 @Override getTransitionProperties()126 public String[] getTransitionProperties() { 127 return sTransitionProperties; 128 } 129 130 /** 131 * Creates an Animator for ImageViews moving, changing dimensions, and/or changing 132 * {@link android.widget.ImageView.ScaleType}. 133 * 134 * @param sceneRoot The root of the transition hierarchy. 135 * @param startValues The values for a specific target in the start scene. 136 * @param endValues The values for the target in the end scene. 137 * @return An Animator to move an ImageView or null if the View is not an ImageView, 138 * the Drawable changed, the View is not VISIBLE, or there was no change. 139 */ 140 @Nullable 141 @Override createAnimator(@onNull ViewGroup sceneRoot, @Nullable TransitionValues startValues, @Nullable TransitionValues endValues)142 public Animator createAnimator(@NonNull ViewGroup sceneRoot, 143 @Nullable TransitionValues startValues, 144 @Nullable TransitionValues endValues) { 145 if (startValues == null || endValues == null) { 146 return null; 147 } 148 Rect startBounds = (Rect) startValues.values.get(PROPNAME_BOUNDS); 149 Rect endBounds = (Rect) endValues.values.get(PROPNAME_BOUNDS); 150 Matrix startMatrix = (Matrix) startValues.values.get(PROPNAME_MATRIX); 151 Matrix endMatrix = (Matrix) endValues.values.get(PROPNAME_MATRIX); 152 if (startBounds == null || endBounds == null || startMatrix == null || endMatrix == null) { 153 return null; 154 } 155 156 if (startBounds.equals(endBounds) && startMatrix.equals(endMatrix)) { 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 ANIMATED_TRANSFORM_PROPERTY.set(imageView, startMatrix); 170 animator = createMatrixAnimator(imageView, startMatrix, endMatrix); 171 } 172 return animator; 173 } 174 createNullAnimator(ImageView imageView)175 private ObjectAnimator createNullAnimator(ImageView imageView) { 176 return ObjectAnimator.ofObject(imageView, ANIMATED_TRANSFORM_PROPERTY, 177 NULL_MATRIX_EVALUATOR, Matrix.IDENTITY_MATRIX, Matrix.IDENTITY_MATRIX); 178 } 179 createMatrixAnimator(final ImageView imageView, Matrix startMatrix, final Matrix endMatrix)180 private ObjectAnimator createMatrixAnimator(final ImageView imageView, Matrix startMatrix, 181 final Matrix endMatrix) { 182 return ObjectAnimator.ofObject(imageView, ANIMATED_TRANSFORM_PROPERTY, 183 new TransitionUtils.MatrixEvaluator(), startMatrix, endMatrix); 184 } 185 } 186