ChangeBounds.java revision 4d1ecf5daf6ad0b5420f3f3657e2cbaeea630240
1faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase/* 2faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase * Copyright (C) 2013 The Android Open Source Project 3faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase * 4faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase * Licensed under the Apache License, Version 2.0 (the "License"); 5faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase * you may not use this file except in compliance with the License. 6faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase * You may obtain a copy of the License at 7faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase * 8faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase * http://www.apache.org/licenses/LICENSE-2.0 9faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase * 10faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase * Unless required by applicable law or agreed to in writing, software 11faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase * distributed under the License is distributed on an "AS IS" BASIS, 12faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase * See the License for the specific language governing permissions and 14faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase * limitations under the License. 15faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase */ 166ebe3de331efd00ba23bc4191d4a82cfa4c39160Chet Haase 17d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haasepackage android.transition; 18faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase 19ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mountimport android.content.Context; 20ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mountimport android.graphics.PointF; 21ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount 22faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haaseimport android.animation.Animator; 23faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haaseimport android.animation.AnimatorListenerAdapter; 24faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haaseimport android.animation.ObjectAnimator; 25faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haaseimport android.animation.PropertyValuesHolder; 26faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haaseimport android.animation.RectEvaluator; 27faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haaseimport android.graphics.Bitmap; 28faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haaseimport android.graphics.Canvas; 29ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mountimport android.graphics.Path; 30faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haaseimport android.graphics.Rect; 31faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haaseimport android.graphics.drawable.BitmapDrawable; 32ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mountimport android.graphics.drawable.Drawable; 33ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mountimport android.util.AttributeSet; 34ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mountimport android.util.Property; 35faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haaseimport android.view.View; 36faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haaseimport android.view.ViewGroup; 37faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase 3808735185f8105710e18ad02297461bec9268e514Chet Haaseimport java.util.Map; 39faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase 40faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase/** 41faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase * This transition captures the layout bounds of target views before and after 42faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase * the scene change and animates those changes during the transition. 43d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase * 44d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase * <p>A ChangeBounds transition can be described in a resource file by using the 45d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase * tag <code>changeBounds</code>, along with the other standard 46d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase * attributes of {@link android.R.styleable#Transition}.</p> 47faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase */ 48d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haasepublic class ChangeBounds extends Transition { 49faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase 50d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase private static final String PROPNAME_BOUNDS = "android:changeBounds:bounds"; 51d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase private static final String PROPNAME_PARENT = "android:changeBounds:parent"; 52d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase private static final String PROPNAME_WINDOW_X = "android:changeBounds:windowX"; 53d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase private static final String PROPNAME_WINDOW_Y = "android:changeBounds:windowY"; 54af78bdd615ecd5ce9d41a6160ce9f53fa269b119Chet Haase private static final String[] sTransitionProperties = { 55199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase PROPNAME_BOUNDS, 56199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase PROPNAME_PARENT, 57199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase PROPNAME_WINDOW_X, 58199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase PROPNAME_WINDOW_Y 59199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase }; 60199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase 61ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount private static final Property<Drawable, PointF> DRAWABLE_ORIGIN_PROPERTY = 62ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount new Property<Drawable, PointF>(PointF.class, "boundsOrigin") { 63ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount private Rect mBounds = new Rect(); 64ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount 65ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount @Override 66ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount public void set(Drawable object, PointF value) { 67ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount object.copyBounds(mBounds); 68ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount mBounds.offsetTo(Math.round(value.x), Math.round(value.y)); 69ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount object.setBounds(mBounds); 70ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount } 71ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount 72ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount @Override 73ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount public PointF get(Drawable object) { 74ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount object.copyBounds(mBounds); 75ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount return new PointF(mBounds.left, mBounds.top); 76ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount } 77ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount }; 78ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount 79faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase int[] tempLocation = new int[2]; 80faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase boolean mResizeClip = false; 81faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase boolean mReparent = false; 82d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase private static final String LOG_TAG = "ChangeBounds"; 83faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase 84faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase private static RectEvaluator sRectEvaluator = new RectEvaluator(); 85faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase 86ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount public ChangeBounds() {} 87ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount 88ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount public ChangeBounds(Context context, AttributeSet attrs) { 89ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount super(context, attrs); 90ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount } 91ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount 92199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase @Override 93199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase public String[] getTransitionProperties() { 94199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase return sTransitionProperties; 95199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase } 96199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase 97faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase public void setResizeClip(boolean resizeClip) { 98faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase mResizeClip = resizeClip; 99faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase } 100faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase 101faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase /** 102d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase * Setting this flag tells ChangeBounds to track the before/after parent 103faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase * of every view using this transition. The flag is not enabled by 104faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase * default because it requires the parent instances to be the same 105faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase * in the two scenes or else all parents must use ids to allow 106faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase * the transition to determine which parents are the same. 107faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase * 108faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase * @param reparent true if the transition should track the parent 109faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase * container of target views and animate parent changes. 110faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase */ 111faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase public void setReparent(boolean reparent) { 112faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase mReparent = reparent; 113faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase } 114faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase 115d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase private void captureValues(TransitionValues values) { 116faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase View view = values.view; 117faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase values.values.put(PROPNAME_BOUNDS, new Rect(view.getLeft(), view.getTop(), 118faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase view.getRight(), view.getBottom())); 119faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase values.values.put(PROPNAME_PARENT, values.view.getParent()); 1204d1ecf5daf6ad0b5420f3f3657e2cbaeea630240George Mount if (mReparent) { 1214d1ecf5daf6ad0b5420f3f3657e2cbaeea630240George Mount values.view.getLocationInWindow(tempLocation); 1224d1ecf5daf6ad0b5420f3f3657e2cbaeea630240George Mount values.values.put(PROPNAME_WINDOW_X, tempLocation[0]); 1234d1ecf5daf6ad0b5420f3f3657e2cbaeea630240George Mount values.values.put(PROPNAME_WINDOW_Y, tempLocation[1]); 1244d1ecf5daf6ad0b5420f3f3657e2cbaeea630240George Mount } 125faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase } 126faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase 127faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase @Override 128d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase public void captureStartValues(TransitionValues transitionValues) { 129d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase captureValues(transitionValues); 130d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase } 131d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase 132d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase @Override 133d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase public void captureEndValues(TransitionValues transitionValues) { 134d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase captureValues(transitionValues); 135d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase } 136d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase 1374d1ecf5daf6ad0b5420f3f3657e2cbaeea630240George Mount private boolean parentMatches(View startParent, View endParent) { 1384d1ecf5daf6ad0b5420f3f3657e2cbaeea630240George Mount boolean parentMatches = true; 1394d1ecf5daf6ad0b5420f3f3657e2cbaeea630240George Mount if (mReparent) { 1404d1ecf5daf6ad0b5420f3f3657e2cbaeea630240George Mount TransitionValues endValues = getMatchedTransitionValues(startParent, true); 1414d1ecf5daf6ad0b5420f3f3657e2cbaeea630240George Mount if (endValues == null) { 1424d1ecf5daf6ad0b5420f3f3657e2cbaeea630240George Mount parentMatches = startParent == endParent; 1434d1ecf5daf6ad0b5420f3f3657e2cbaeea630240George Mount } else { 1444d1ecf5daf6ad0b5420f3f3657e2cbaeea630240George Mount parentMatches = endParent == endValues.view; 1454d1ecf5daf6ad0b5420f3f3657e2cbaeea630240George Mount } 1464d1ecf5daf6ad0b5420f3f3657e2cbaeea630240George Mount } 1474d1ecf5daf6ad0b5420f3f3657e2cbaeea630240George Mount return parentMatches; 1484d1ecf5daf6ad0b5420f3f3657e2cbaeea630240George Mount } 1494d1ecf5daf6ad0b5420f3f3657e2cbaeea630240George Mount 150d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase @Override 151d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase public Animator createAnimator(final ViewGroup sceneRoot, TransitionValues startValues, 152faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase TransitionValues endValues) { 153faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase if (startValues == null || endValues == null) { 154faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase return null; 155faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase } 1562ea7f8b9c5f903050d42c1af57406bf528979f45Chet Haase Map<String, Object> startParentVals = startValues.values; 1572ea7f8b9c5f903050d42c1af57406bf528979f45Chet Haase Map<String, Object> endParentVals = endValues.values; 1582ea7f8b9c5f903050d42c1af57406bf528979f45Chet Haase ViewGroup startParent = (ViewGroup) startParentVals.get(PROPNAME_PARENT); 1592ea7f8b9c5f903050d42c1af57406bf528979f45Chet Haase ViewGroup endParent = (ViewGroup) endParentVals.get(PROPNAME_PARENT); 160faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase if (startParent == null || endParent == null) { 161faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase return null; 162faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase } 1632ea7f8b9c5f903050d42c1af57406bf528979f45Chet Haase final View view = endValues.view; 1644d1ecf5daf6ad0b5420f3f3657e2cbaeea630240George Mount if (parentMatches(startParent, endParent)) { 165faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase Rect startBounds = (Rect) startValues.values.get(PROPNAME_BOUNDS); 166faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase Rect endBounds = (Rect) endValues.values.get(PROPNAME_BOUNDS); 167faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase int startLeft = startBounds.left; 168faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase int endLeft = endBounds.left; 169faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase int startTop = startBounds.top; 170faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase int endTop = endBounds.top; 171faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase int startRight = startBounds.right; 172faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase int endRight = endBounds.right; 173faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase int startBottom = startBounds.bottom; 174faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase int endBottom = endBounds.bottom; 175faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase int startWidth = startRight - startLeft; 176faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase int startHeight = startBottom - startTop; 177faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase int endWidth = endRight - endLeft; 178faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase int endHeight = endBottom - endTop; 179faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase int numChanges = 0; 1802cacfe3a98ff4f777503cedca5e6fc44e6df269fGeorge Mount if ((startWidth != 0 && startHeight != 0) || (endWidth != 0 && endHeight != 0)) { 181ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount if (startLeft != endLeft || startTop != endTop) ++numChanges; 182ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount if (startRight != endRight || startBottom != endBottom) ++numChanges; 183faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase } 184faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase if (numChanges > 0) { 185faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase if (!mResizeClip) { 1862ea7f8b9c5f903050d42c1af57406bf528979f45Chet Haase if (startLeft != endLeft) view.setLeft(startLeft); 1872ea7f8b9c5f903050d42c1af57406bf528979f45Chet Haase if (startTop != endTop) view.setTop(startTop); 1882ea7f8b9c5f903050d42c1af57406bf528979f45Chet Haase if (startRight != endRight) view.setRight(startRight); 1892ea7f8b9c5f903050d42c1af57406bf528979f45Chet Haase if (startBottom != endBottom) view.setBottom(startBottom); 190ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount ObjectAnimator topLeftAnimator = null; 191ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount if (startLeft != endLeft || startTop != endTop) { 192ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount Path topLeftPath = getPathMotion().getPath(startLeft, startTop, 193ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount endLeft, endTop); 194ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount topLeftAnimator = ObjectAnimator.ofInt(view, "left", "top", topLeftPath); 195faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase } 196ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount ObjectAnimator bottomRightAnimator = null; 197ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount if (startRight != endRight || startBottom != endBottom) { 198ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount Path bottomRightPath = getPathMotion().getPath(startRight, startBottom, 199ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount endRight, endBottom); 200ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount bottomRightAnimator = ObjectAnimator.ofInt(view, "right", "bottom", 201ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount bottomRightPath); 202faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase } 2032dcab18c6c9381122df6e06a93912e53dff69408Paul Soulos Animator anim = TransitionUtils.mergeAnimators(topLeftAnimator, 2042dcab18c6c9381122df6e06a93912e53dff69408Paul Soulos bottomRightAnimator); 205faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase if (view.getParent() instanceof ViewGroup) { 206faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase final ViewGroup parent = (ViewGroup) view.getParent(); 207faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase parent.suppressLayout(true); 208199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase TransitionListener transitionListener = new TransitionListenerAdapter() { 209199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase boolean mCanceled = false; 210199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase 211faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase @Override 212199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase public void onTransitionCancel(Transition transition) { 213faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase parent.suppressLayout(false); 214199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase mCanceled = true; 215199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase } 216199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase 217199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase @Override 218199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase public void onTransitionEnd(Transition transition) { 219199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase if (!mCanceled) { 220199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase parent.suppressLayout(false); 221199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase } 222199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase } 223199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase 224199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase @Override 225199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase public void onTransitionPause(Transition transition) { 226199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase parent.suppressLayout(false); 227199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase } 228199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase 229199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase @Override 230199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase public void onTransitionResume(Transition transition) { 231199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase parent.suppressLayout(true); 232faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase } 233199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase }; 234199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase addListener(transitionListener); 235faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase } 236faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase return anim; 237faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase } else { 2382ea7f8b9c5f903050d42c1af57406bf528979f45Chet Haase if (startWidth != endWidth) view.setRight(endLeft + 2392ea7f8b9c5f903050d42c1af57406bf528979f45Chet Haase Math.max(startWidth, endWidth)); 2402ea7f8b9c5f903050d42c1af57406bf528979f45Chet Haase if (startHeight != endHeight) view.setBottom(endTop + 2412ea7f8b9c5f903050d42c1af57406bf528979f45Chet Haase Math.max(startHeight, endHeight)); 2422ea7f8b9c5f903050d42c1af57406bf528979f45Chet Haase // TODO: don't clobber TX/TY 2432ea7f8b9c5f903050d42c1af57406bf528979f45Chet Haase if (startLeft != endLeft) view.setTranslationX(startLeft - endLeft); 2442ea7f8b9c5f903050d42c1af57406bf528979f45Chet Haase if (startTop != endTop) view.setTranslationY(startTop - endTop); 245faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase // Animate location with translationX/Y and size with clip bounds 246faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase float transXDelta = endLeft - startLeft; 247faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase float transYDelta = endTop - startTop; 248faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase int widthDelta = endWidth - startWidth; 249faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase int heightDelta = endHeight - startHeight; 250faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase numChanges = 0; 251faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase if (transXDelta != 0) numChanges++; 252faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase if (transYDelta != 0) numChanges++; 253faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase if (widthDelta != 0 || heightDelta != 0) numChanges++; 254ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount ObjectAnimator translationAnimator = null; 255ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount if (transXDelta != 0 || transYDelta != 0) { 256ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount Path topLeftPath = getPathMotion().getPath(0, 0, transXDelta, transYDelta); 257ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount translationAnimator = ObjectAnimator.ofFloat(view, View.TRANSLATION_X, 258ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount View.TRANSLATION_Y, topLeftPath); 259faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase } 260ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount ObjectAnimator clipAnimator = null; 261faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase if (widthDelta != 0 || heightDelta != 0) { 262faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase Rect tempStartBounds = new Rect(0, 0, startWidth, startHeight); 263faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase Rect tempEndBounds = new Rect(0, 0, endWidth, endHeight); 264ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount clipAnimator = ObjectAnimator.ofObject(view, "clipBounds", sRectEvaluator, 265ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount tempStartBounds, tempEndBounds); 266faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase } 2672dcab18c6c9381122df6e06a93912e53dff69408Paul Soulos Animator anim = TransitionUtils.mergeAnimators(translationAnimator, 2682dcab18c6c9381122df6e06a93912e53dff69408Paul Soulos clipAnimator); 269faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase if (view.getParent() instanceof ViewGroup) { 270faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase final ViewGroup parent = (ViewGroup) view.getParent(); 271faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase parent.suppressLayout(true); 272199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase TransitionListener transitionListener = new TransitionListenerAdapter() { 273199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase boolean mCanceled = false; 274199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase 275faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase @Override 276199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase public void onTransitionCancel(Transition transition) { 277faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase parent.suppressLayout(false); 278199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase mCanceled = true; 279199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase } 280199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase 281199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase @Override 282199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase public void onTransitionEnd(Transition transition) { 283199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase if (!mCanceled) { 284199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase parent.suppressLayout(false); 285199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase } 286199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase } 287199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase 288199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase @Override 289199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase public void onTransitionPause(Transition transition) { 290199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase parent.suppressLayout(false); 291199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase } 292199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase 293199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase @Override 294199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase public void onTransitionResume(Transition transition) { 295199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase parent.suppressLayout(true); 296faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase } 297199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase }; 298199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase addListener(transitionListener); 299faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase } 300faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase anim.addListener(new AnimatorListenerAdapter() { 301faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase @Override 302faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase public void onAnimationEnd(Animator animation) { 303faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase view.setClipBounds(null); 304faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase } 305faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase }); 306faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase return anim; 307faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase } 308faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase } 309faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase } else { 310faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase int startX = (Integer) startValues.values.get(PROPNAME_WINDOW_X); 311faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase int startY = (Integer) startValues.values.get(PROPNAME_WINDOW_Y); 312faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase int endX = (Integer) endValues.values.get(PROPNAME_WINDOW_X); 313faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase int endY = (Integer) endValues.values.get(PROPNAME_WINDOW_Y); 314faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase // TODO: also handle size changes: check bounds and animate size changes 315faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase if (startX != endX || startY != endY) { 316faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase sceneRoot.getLocationInWindow(tempLocation); 317faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase Bitmap bitmap = Bitmap.createBitmap(view.getWidth(), view.getHeight(), 318faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase Bitmap.Config.ARGB_8888); 319faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase Canvas canvas = new Canvas(bitmap); 320faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase view.draw(canvas); 321faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase final BitmapDrawable drawable = new BitmapDrawable(bitmap); 322b5ef7f8c6d4629b2998de6c0b27bc1a4779b3e49George Mount final float transitionAlpha = view.getTransitionAlpha(); 323b5ef7f8c6d4629b2998de6c0b27bc1a4779b3e49George Mount view.setTransitionAlpha(0); 324faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase sceneRoot.getOverlay().add(drawable); 325ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount Path topLeftPath = getPathMotion().getPath(startX - tempLocation[0], 326ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount startY - tempLocation[1], endX - tempLocation[0], endY - tempLocation[1]); 327ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount PropertyValuesHolder origin = PropertyValuesHolder.ofObject( 328ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount DRAWABLE_ORIGIN_PROPERTY, null, topLeftPath); 329ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount ObjectAnimator anim = ObjectAnimator.ofPropertyValuesHolder(drawable, origin); 330faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase anim.addListener(new AnimatorListenerAdapter() { 331faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase @Override 332faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase public void onAnimationEnd(Animator animation) { 333faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase sceneRoot.getOverlay().remove(drawable); 334b5ef7f8c6d4629b2998de6c0b27bc1a4779b3e49George Mount view.setTransitionAlpha(transitionAlpha); 335faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase } 336faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase }); 3372ea7f8b9c5f903050d42c1af57406bf528979f45Chet Haase return anim; 338faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase } 339faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase } 3402ea7f8b9c5f903050d42c1af57406bf528979f45Chet Haase return null; 341faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase } 342faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase} 343