1990205eada00ad3e575761d19607bb03e12f9aa3George Mount/* 2990205eada00ad3e575761d19607bb03e12f9aa3George Mount * Copyright (C) 2014 The Android Open Source Project 3990205eada00ad3e575761d19607bb03e12f9aa3George Mount * 4990205eada00ad3e575761d19607bb03e12f9aa3George Mount * Licensed under the Apache License, Version 2.0 (the "License"); 5990205eada00ad3e575761d19607bb03e12f9aa3George Mount * you may not use this file except in compliance with the License. 6990205eada00ad3e575761d19607bb03e12f9aa3George Mount * You may obtain a copy of the License at 7990205eada00ad3e575761d19607bb03e12f9aa3George Mount * 8990205eada00ad3e575761d19607bb03e12f9aa3George Mount * http://www.apache.org/licenses/LICENSE-2.0 9990205eada00ad3e575761d19607bb03e12f9aa3George Mount * 10990205eada00ad3e575761d19607bb03e12f9aa3George Mount * Unless required by applicable law or agreed to in writing, software 11990205eada00ad3e575761d19607bb03e12f9aa3George Mount * distributed under the License is distributed on an "AS IS" BASIS, 12990205eada00ad3e575761d19607bb03e12f9aa3George Mount * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13990205eada00ad3e575761d19607bb03e12f9aa3George Mount * See the License for the specific language governing permissions and 14990205eada00ad3e575761d19607bb03e12f9aa3George Mount * limitations under the License. 15990205eada00ad3e575761d19607bb03e12f9aa3George Mount */ 16990205eada00ad3e575761d19607bb03e12f9aa3George Mountpackage android.transition; 17990205eada00ad3e575761d19607bb03e12f9aa3George Mount 18990205eada00ad3e575761d19607bb03e12f9aa3George Mountimport android.animation.Animator; 19990205eada00ad3e575761d19607bb03e12f9aa3George Mountimport android.animation.ObjectAnimator; 20990205eada00ad3e575761d19607bb03e12f9aa3George Mountimport android.animation.TypeEvaluator; 21ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mountimport android.content.Context; 22990205eada00ad3e575761d19607bb03e12f9aa3George Mountimport android.graphics.Matrix; 23990205eada00ad3e575761d19607bb03e12f9aa3George Mountimport android.graphics.Rect; 24990205eada00ad3e575761d19607bb03e12f9aa3George Mountimport android.graphics.drawable.Drawable; 25ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mountimport android.util.AttributeSet; 26990205eada00ad3e575761d19607bb03e12f9aa3George Mountimport android.util.Property; 27990205eada00ad3e575761d19607bb03e12f9aa3George Mountimport android.view.View; 28990205eada00ad3e575761d19607bb03e12f9aa3George Mountimport android.view.ViewGroup; 29990205eada00ad3e575761d19607bb03e12f9aa3George Mountimport android.widget.ImageView; 30990205eada00ad3e575761d19607bb03e12f9aa3George Mount 31990205eada00ad3e575761d19607bb03e12f9aa3George Mountimport java.util.Map; 32990205eada00ad3e575761d19607bb03e12f9aa3George Mount 33990205eada00ad3e575761d19607bb03e12f9aa3George Mount/** 34ad88e1b1b2c57fa56bde68764b22240d145fa0efGeorge Mount * This Transition captures an ImageView's matrix before and after the 35ad88e1b1b2c57fa56bde68764b22240d145fa0efGeorge Mount * scene change and animates it during the transition. 36ad88e1b1b2c57fa56bde68764b22240d145fa0efGeorge Mount * 37ad88e1b1b2c57fa56bde68764b22240d145fa0efGeorge Mount * <p>In combination with ChangeBounds, ChangeImageTransform allows ImageViews 38ad88e1b1b2c57fa56bde68764b22240d145fa0efGeorge Mount * that change size, shape, or {@link android.widget.ImageView.ScaleType} to animate contents 39ad88e1b1b2c57fa56bde68764b22240d145fa0efGeorge Mount * smoothly.</p> 40990205eada00ad3e575761d19607bb03e12f9aa3George Mount */ 41990205eada00ad3e575761d19607bb03e12f9aa3George Mountpublic class ChangeImageTransform extends Transition { 42990205eada00ad3e575761d19607bb03e12f9aa3George Mount 43ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount private static final String TAG = "ChangeImageTransform"; 44990205eada00ad3e575761d19607bb03e12f9aa3George Mount 45ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount private static final String PROPNAME_MATRIX = "android:changeImageTransform:matrix"; 46ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount private static final String PROPNAME_BOUNDS = "android:changeImageTransform:bounds"; 47990205eada00ad3e575761d19607bb03e12f9aa3George Mount 48990205eada00ad3e575761d19607bb03e12f9aa3George Mount private static final String[] sTransitionProperties = { 49990205eada00ad3e575761d19607bb03e12f9aa3George Mount PROPNAME_MATRIX, 50990205eada00ad3e575761d19607bb03e12f9aa3George Mount PROPNAME_BOUNDS, 51990205eada00ad3e575761d19607bb03e12f9aa3George Mount }; 52990205eada00ad3e575761d19607bb03e12f9aa3George Mount 53990205eada00ad3e575761d19607bb03e12f9aa3George Mount private static TypeEvaluator<Matrix> NULL_MATRIX_EVALUATOR = new TypeEvaluator<Matrix>() { 54990205eada00ad3e575761d19607bb03e12f9aa3George Mount @Override 55990205eada00ad3e575761d19607bb03e12f9aa3George Mount public Matrix evaluate(float fraction, Matrix startValue, Matrix endValue) { 56990205eada00ad3e575761d19607bb03e12f9aa3George Mount return null; 57990205eada00ad3e575761d19607bb03e12f9aa3George Mount } 58990205eada00ad3e575761d19607bb03e12f9aa3George Mount }; 59990205eada00ad3e575761d19607bb03e12f9aa3George Mount 60990205eada00ad3e575761d19607bb03e12f9aa3George Mount private static Property<ImageView, Matrix> ANIMATED_TRANSFORM_PROPERTY 61990205eada00ad3e575761d19607bb03e12f9aa3George Mount = new Property<ImageView, Matrix>(Matrix.class, "animatedTransform") { 62990205eada00ad3e575761d19607bb03e12f9aa3George Mount @Override 63990205eada00ad3e575761d19607bb03e12f9aa3George Mount public void set(ImageView object, Matrix value) { 64990205eada00ad3e575761d19607bb03e12f9aa3George Mount object.animateTransform(value); 65990205eada00ad3e575761d19607bb03e12f9aa3George Mount } 66990205eada00ad3e575761d19607bb03e12f9aa3George Mount 67990205eada00ad3e575761d19607bb03e12f9aa3George Mount @Override 68990205eada00ad3e575761d19607bb03e12f9aa3George Mount public Matrix get(ImageView object) { 69990205eada00ad3e575761d19607bb03e12f9aa3George Mount return null; 70990205eada00ad3e575761d19607bb03e12f9aa3George Mount } 71990205eada00ad3e575761d19607bb03e12f9aa3George Mount }; 72990205eada00ad3e575761d19607bb03e12f9aa3George Mount 73ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount public ChangeImageTransform() {} 74ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount 75ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount public ChangeImageTransform(Context context, AttributeSet attrs) { 76ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount super(context, attrs); 77ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount } 78ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount 79990205eada00ad3e575761d19607bb03e12f9aa3George Mount private void captureValues(TransitionValues transitionValues) { 80990205eada00ad3e575761d19607bb03e12f9aa3George Mount View view = transitionValues.view; 81990205eada00ad3e575761d19607bb03e12f9aa3George Mount if (!(view instanceof ImageView) || view.getVisibility() != View.VISIBLE) { 82990205eada00ad3e575761d19607bb03e12f9aa3George Mount return; 83990205eada00ad3e575761d19607bb03e12f9aa3George Mount } 84990205eada00ad3e575761d19607bb03e12f9aa3George Mount ImageView imageView = (ImageView) view; 85990205eada00ad3e575761d19607bb03e12f9aa3George Mount Drawable drawable = imageView.getDrawable(); 86990205eada00ad3e575761d19607bb03e12f9aa3George Mount if (drawable == null) { 87990205eada00ad3e575761d19607bb03e12f9aa3George Mount return; 88990205eada00ad3e575761d19607bb03e12f9aa3George Mount } 89990205eada00ad3e575761d19607bb03e12f9aa3George Mount Map<String, Object> values = transitionValues.values; 90990205eada00ad3e575761d19607bb03e12f9aa3George Mount 91990205eada00ad3e575761d19607bb03e12f9aa3George Mount int left = view.getLeft(); 92990205eada00ad3e575761d19607bb03e12f9aa3George Mount int top = view.getTop(); 93990205eada00ad3e575761d19607bb03e12f9aa3George Mount int right = view.getRight(); 94990205eada00ad3e575761d19607bb03e12f9aa3George Mount int bottom = view.getBottom(); 95990205eada00ad3e575761d19607bb03e12f9aa3George Mount 96990205eada00ad3e575761d19607bb03e12f9aa3George Mount Rect bounds = new Rect(left, top, right, bottom); 97990205eada00ad3e575761d19607bb03e12f9aa3George Mount values.put(PROPNAME_BOUNDS, bounds); 98990205eada00ad3e575761d19607bb03e12f9aa3George Mount Matrix matrix; 99990205eada00ad3e575761d19607bb03e12f9aa3George Mount ImageView.ScaleType scaleType = imageView.getScaleType(); 100990205eada00ad3e575761d19607bb03e12f9aa3George Mount if (scaleType == ImageView.ScaleType.FIT_XY) { 101990205eada00ad3e575761d19607bb03e12f9aa3George Mount matrix = imageView.getImageMatrix(); 102990205eada00ad3e575761d19607bb03e12f9aa3George Mount if (!matrix.isIdentity()) { 103990205eada00ad3e575761d19607bb03e12f9aa3George Mount matrix = new Matrix(matrix); 104990205eada00ad3e575761d19607bb03e12f9aa3George Mount } else { 105990205eada00ad3e575761d19607bb03e12f9aa3George Mount int drawableWidth = drawable.getIntrinsicWidth(); 106990205eada00ad3e575761d19607bb03e12f9aa3George Mount int drawableHeight = drawable.getIntrinsicHeight(); 107990205eada00ad3e575761d19607bb03e12f9aa3George Mount if (drawableWidth > 0 && drawableHeight > 0) { 108990205eada00ad3e575761d19607bb03e12f9aa3George Mount float scaleX = ((float) bounds.width()) / drawableWidth; 109990205eada00ad3e575761d19607bb03e12f9aa3George Mount float scaleY = ((float) bounds.height()) / drawableHeight; 110990205eada00ad3e575761d19607bb03e12f9aa3George Mount matrix = new Matrix(); 111990205eada00ad3e575761d19607bb03e12f9aa3George Mount matrix.setScale(scaleX, scaleY); 112990205eada00ad3e575761d19607bb03e12f9aa3George Mount } else { 113990205eada00ad3e575761d19607bb03e12f9aa3George Mount matrix = null; 114990205eada00ad3e575761d19607bb03e12f9aa3George Mount } 115990205eada00ad3e575761d19607bb03e12f9aa3George Mount } 116990205eada00ad3e575761d19607bb03e12f9aa3George Mount } else { 117990205eada00ad3e575761d19607bb03e12f9aa3George Mount matrix = new Matrix(imageView.getImageMatrix()); 118990205eada00ad3e575761d19607bb03e12f9aa3George Mount } 119990205eada00ad3e575761d19607bb03e12f9aa3George Mount values.put(PROPNAME_MATRIX, matrix); 120990205eada00ad3e575761d19607bb03e12f9aa3George Mount } 121990205eada00ad3e575761d19607bb03e12f9aa3George Mount 122990205eada00ad3e575761d19607bb03e12f9aa3George Mount @Override 123990205eada00ad3e575761d19607bb03e12f9aa3George Mount public void captureStartValues(TransitionValues transitionValues) { 124990205eada00ad3e575761d19607bb03e12f9aa3George Mount captureValues(transitionValues); 125990205eada00ad3e575761d19607bb03e12f9aa3George Mount } 126990205eada00ad3e575761d19607bb03e12f9aa3George Mount 127990205eada00ad3e575761d19607bb03e12f9aa3George Mount @Override 128990205eada00ad3e575761d19607bb03e12f9aa3George Mount public void captureEndValues(TransitionValues transitionValues) { 129990205eada00ad3e575761d19607bb03e12f9aa3George Mount captureValues(transitionValues); 130990205eada00ad3e575761d19607bb03e12f9aa3George Mount } 131990205eada00ad3e575761d19607bb03e12f9aa3George Mount 132990205eada00ad3e575761d19607bb03e12f9aa3George Mount @Override 133990205eada00ad3e575761d19607bb03e12f9aa3George Mount public String[] getTransitionProperties() { 134990205eada00ad3e575761d19607bb03e12f9aa3George Mount return sTransitionProperties; 135990205eada00ad3e575761d19607bb03e12f9aa3George Mount } 136990205eada00ad3e575761d19607bb03e12f9aa3George Mount 137990205eada00ad3e575761d19607bb03e12f9aa3George Mount /** 138990205eada00ad3e575761d19607bb03e12f9aa3George Mount * Creates an Animator for ImageViews moving, changing dimensions, and/or changing 139990205eada00ad3e575761d19607bb03e12f9aa3George Mount * {@link android.widget.ImageView.ScaleType}. 140990205eada00ad3e575761d19607bb03e12f9aa3George Mount * 141990205eada00ad3e575761d19607bb03e12f9aa3George Mount * @param sceneRoot The root of the transition hierarchy. 142990205eada00ad3e575761d19607bb03e12f9aa3George Mount * @param startValues The values for a specific target in the start scene. 143990205eada00ad3e575761d19607bb03e12f9aa3George Mount * @param endValues The values for the target in the end scene. 144990205eada00ad3e575761d19607bb03e12f9aa3George Mount * @return An Animator to move an ImageView or null if the View is not an ImageView, 145990205eada00ad3e575761d19607bb03e12f9aa3George Mount * the Drawable changed, the View is not VISIBLE, or there was no change. 146990205eada00ad3e575761d19607bb03e12f9aa3George Mount */ 147990205eada00ad3e575761d19607bb03e12f9aa3George Mount @Override 148990205eada00ad3e575761d19607bb03e12f9aa3George Mount public Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues, 149990205eada00ad3e575761d19607bb03e12f9aa3George Mount TransitionValues endValues) { 150990205eada00ad3e575761d19607bb03e12f9aa3George Mount if (startValues == null || endValues == null) { 151990205eada00ad3e575761d19607bb03e12f9aa3George Mount return null; 152990205eada00ad3e575761d19607bb03e12f9aa3George Mount } 153990205eada00ad3e575761d19607bb03e12f9aa3George Mount Rect startBounds = (Rect) startValues.values.get(PROPNAME_BOUNDS); 154990205eada00ad3e575761d19607bb03e12f9aa3George Mount Rect endBounds = (Rect) endValues.values.get(PROPNAME_BOUNDS); 155990205eada00ad3e575761d19607bb03e12f9aa3George Mount if (startBounds == null || endBounds == null) { 156990205eada00ad3e575761d19607bb03e12f9aa3George Mount return null; 157990205eada00ad3e575761d19607bb03e12f9aa3George Mount } 158990205eada00ad3e575761d19607bb03e12f9aa3George Mount 159990205eada00ad3e575761d19607bb03e12f9aa3George Mount Matrix startMatrix = (Matrix) startValues.values.get(PROPNAME_MATRIX); 160990205eada00ad3e575761d19607bb03e12f9aa3George Mount Matrix endMatrix = (Matrix) endValues.values.get(PROPNAME_MATRIX); 161990205eada00ad3e575761d19607bb03e12f9aa3George Mount 162990205eada00ad3e575761d19607bb03e12f9aa3George Mount boolean matricesEqual = (startMatrix == null && endMatrix == null) || 163990205eada00ad3e575761d19607bb03e12f9aa3George Mount (startMatrix != null && startMatrix.equals(endMatrix)); 164990205eada00ad3e575761d19607bb03e12f9aa3George Mount 165990205eada00ad3e575761d19607bb03e12f9aa3George Mount if (startBounds.equals(endBounds) && matricesEqual) { 166990205eada00ad3e575761d19607bb03e12f9aa3George Mount return null; 167990205eada00ad3e575761d19607bb03e12f9aa3George Mount } 168990205eada00ad3e575761d19607bb03e12f9aa3George Mount 169990205eada00ad3e575761d19607bb03e12f9aa3George Mount ImageView imageView = (ImageView) endValues.view; 170990205eada00ad3e575761d19607bb03e12f9aa3George Mount Drawable drawable = imageView.getDrawable(); 171990205eada00ad3e575761d19607bb03e12f9aa3George Mount int drawableWidth = drawable.getIntrinsicWidth(); 172990205eada00ad3e575761d19607bb03e12f9aa3George Mount int drawableHeight = drawable.getIntrinsicHeight(); 173990205eada00ad3e575761d19607bb03e12f9aa3George Mount 174990205eada00ad3e575761d19607bb03e12f9aa3George Mount ObjectAnimator animator; 175990205eada00ad3e575761d19607bb03e12f9aa3George Mount if (drawableWidth == 0 || drawableHeight == 0) { 176990205eada00ad3e575761d19607bb03e12f9aa3George Mount animator = createNullAnimator(imageView); 177990205eada00ad3e575761d19607bb03e12f9aa3George Mount } else { 178990205eada00ad3e575761d19607bb03e12f9aa3George Mount if (startMatrix == null) { 179990205eada00ad3e575761d19607bb03e12f9aa3George Mount startMatrix = Matrix.IDENTITY_MATRIX; 180990205eada00ad3e575761d19607bb03e12f9aa3George Mount } 181990205eada00ad3e575761d19607bb03e12f9aa3George Mount if (endMatrix == null) { 182990205eada00ad3e575761d19607bb03e12f9aa3George Mount endMatrix = Matrix.IDENTITY_MATRIX; 183990205eada00ad3e575761d19607bb03e12f9aa3George Mount } 184dbe3655c0b93b4bf80c751b4148fcbefbfe96d5bGeorge Mount ANIMATED_TRANSFORM_PROPERTY.set(imageView, startMatrix); 185990205eada00ad3e575761d19607bb03e12f9aa3George Mount animator = createMatrixAnimator(imageView, startMatrix, endMatrix); 186990205eada00ad3e575761d19607bb03e12f9aa3George Mount } 187990205eada00ad3e575761d19607bb03e12f9aa3George Mount return animator; 188990205eada00ad3e575761d19607bb03e12f9aa3George Mount } 189990205eada00ad3e575761d19607bb03e12f9aa3George Mount 190990205eada00ad3e575761d19607bb03e12f9aa3George Mount private ObjectAnimator createNullAnimator(ImageView imageView) { 191990205eada00ad3e575761d19607bb03e12f9aa3George Mount return ObjectAnimator.ofObject(imageView, ANIMATED_TRANSFORM_PROPERTY, 192990205eada00ad3e575761d19607bb03e12f9aa3George Mount NULL_MATRIX_EVALUATOR, null, null); 193990205eada00ad3e575761d19607bb03e12f9aa3George Mount } 194990205eada00ad3e575761d19607bb03e12f9aa3George Mount 195990205eada00ad3e575761d19607bb03e12f9aa3George Mount private ObjectAnimator createMatrixAnimator(final ImageView imageView, Matrix startMatrix, 196990205eada00ad3e575761d19607bb03e12f9aa3George Mount final Matrix endMatrix) { 197ad88e1b1b2c57fa56bde68764b22240d145fa0efGeorge Mount return ObjectAnimator.ofObject(imageView, ANIMATED_TRANSFORM_PROPERTY, 198c94e2b393f6eba684ee2c84eaa50746fc1459d0fDake Gu new TransitionUtils.MatrixEvaluator(), startMatrix, endMatrix); 199990205eada00ad3e575761d19607bb03e12f9aa3George Mount } 200990205eada00ad3e575761d19607bb03e12f9aa3George Mount} 201