ChangeBounds.java revision b7573c2dbaff442a0f9f814bdc05aaa685574870
15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/* 25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Copyright (C) 2013 The Android Open Source Project 35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * 45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Licensed under the Apache License, Version 2.0 (the "License"); 52a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * you may not use this file except in compliance with the License. 62a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * You may obtain a copy of the License at 75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * 89ab5563a3196760eb381d102cbb2bc0f7abc6a50Ben Murdoch * http://www.apache.org/licenses/LICENSE-2.0 95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * 1068043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles) * Unless required by applicable law or agreed to in writing, software 112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * distributed under the License is distributed on an "AS IS" BASIS, 122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * See the License for the specific language governing permissions and 14a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) * limitations under the License. 15a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) */ 16a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) 1768043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)package android.transition; 182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)import android.animation.AnimatorSet; 205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import android.content.Context; 215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import android.content.res.TypedArray; 2268043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)import android.graphics.PointF; 235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import android.animation.Animator; 255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import android.animation.AnimatorListenerAdapter; 265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import android.animation.ObjectAnimator; 2768043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)import android.animation.PropertyValuesHolder; 285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import android.animation.RectEvaluator; 295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import android.graphics.Bitmap; 305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import android.graphics.Canvas; 315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import android.graphics.Path; 325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import android.graphics.Rect; 335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import android.graphics.drawable.BitmapDrawable; 345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import android.graphics.drawable.Drawable; 355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import android.util.AttributeSet; 365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import android.util.Property; 372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)import android.view.View; 382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)import android.view.ViewGroup; 392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)import com.android.internal.R; 412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)import java.util.Map; 432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)/** 452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * This transition captures the layout bounds of target views before and after 462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * the scene change and animates those changes during the transition. 472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * 482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * <p>A ChangeBounds transition can be described in a resource file by using the 492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * tag <code>changeBounds</code>, using its attributes of 50c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) * {@link android.R.styleable#ChangeBounds} along with the other standard 51c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) * attributes of {@link android.R.styleable#Transition}.</p> 52c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) */ 532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)public class ChangeBounds extends Transition { 542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) private static final String PROPNAME_BOUNDS = "android:changeBounds:bounds"; 562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) private static final String PROPNAME_CLIP = "android:changeBounds:clip"; 572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) private static final String PROPNAME_PARENT = "android:changeBounds:parent"; 582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) private static final String PROPNAME_WINDOW_X = "android:changeBounds:windowX"; 592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) private static final String PROPNAME_WINDOW_Y = "android:changeBounds:windowY"; 602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) private static final String[] sTransitionProperties = { 615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) PROPNAME_BOUNDS, 622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) PROPNAME_CLIP, 632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) PROPNAME_PARENT, 645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) PROPNAME_WINDOW_X, 652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) PROPNAME_WINDOW_Y 662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) }; 672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) private static final Property<Drawable, PointF> DRAWABLE_ORIGIN_PROPERTY = 692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) new Property<Drawable, PointF>(PointF.class, "boundsOrigin") { 702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) private Rect mBounds = new Rect(); 712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) @Override 732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) public void set(Drawable object, PointF value) { 742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) object.copyBounds(mBounds); 752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) mBounds.offsetTo(Math.round(value.x), Math.round(value.y)); 762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) object.setBounds(mBounds); 772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) @Override 802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) public PointF get(Drawable object) { 812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) object.copyBounds(mBounds); 822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return new PointF(mBounds.left, mBounds.top); 832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) }; 855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) private static final Property<ViewBounds, PointF> TOP_LEFT_PROPERTY = 872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) new Property<ViewBounds, PointF>(PointF.class, "topLeft") { 882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) @Override 895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) public void set(ViewBounds viewBounds, PointF topLeft) { 902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) viewBounds.setTopLeft(topLeft); 915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) @Override 945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) public PointF get(ViewBounds viewBounds) { 955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return null; 965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 97b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) }; 985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) private static final Property<ViewBounds, PointF> BOTTOM_RIGHT_PROPERTY = 1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) new Property<ViewBounds, PointF>(PointF.class, "bottomRight") { 1018bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles) @Override 1028bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles) public void set(ViewBounds viewBounds, PointF bottomRight) { 1038bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles) viewBounds.setBottomRight(bottomRight); 1042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) @Override 1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) public PointF get(ViewBounds viewBounds) { 1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return null; 1092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) }; 1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 112c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) private static final Property<View, PointF> BOTTOM_RIGHT_ONLY_PROPERTY = 113b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) new Property<View, PointF>(PointF.class, "bottomRight") { 114c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) @Override 115c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) public void set(View view, PointF bottomRight) { 116c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) int left = view.getLeft(); 117c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) int top = view.getTop(); 1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) int right = Math.round(bottomRight.x); 1192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) int bottom = Math.round(bottomRight.y); 1202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) view.setLeftTopRightBottom(left, top, right, bottom); 1212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 1222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 1232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) @Override 1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) public PointF get(View view) { 1252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return null; 126b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) } 1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) }; 1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) private static final Property<View, PointF> TOP_LEFT_ONLY_PROPERTY = 13090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) new Property<View, PointF>(PointF.class, "topLeft") { 13190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) @Override 1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) public void set(View view, PointF topLeft) { 133a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) int left = Math.round(topLeft.x); 134a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) int top = Math.round(topLeft.y); 1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) int right = view.getRight(); 1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) int bottom = view.getBottom(); 1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) view.setLeftTopRightBottom(left, top, right, bottom); 1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) @Override 14190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) public PointF get(View view) { 1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return null; 1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) }; 1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 14668043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles) private static final Property<View, PointF> POSITION_PROPERTY = 1474e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) new Property<View, PointF>(PointF.class, "position") { 148a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) @Override 1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) public void set(View view, PointF topLeft) { 1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) int left = Math.round(topLeft.x); 1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) int top = Math.round(topLeft.y); 1525f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) int right = left + view.getWidth(); 1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) int bottom = top + view.getHeight(); 1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) view.setLeftTopRightBottom(left, top, right, bottom); 1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) @Override 1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) public PointF get(View view) { 1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return null; 1602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) }; 1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) int[] tempLocation = new int[2]; 1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) boolean mResizeClip = false; 165b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) boolean mReparent = false; 1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) private static final String LOG_TAG = "ChangeBounds"; 1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) private static RectEvaluator sRectEvaluator = new RectEvaluator(); 1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) public ChangeBounds() {} 171c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) 1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) public ChangeBounds(Context context, AttributeSet attrs) { 1732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) super(context, attrs); 1742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ChangeBounds); 1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) boolean resizeClip = a.getBoolean(R.styleable.ChangeBounds_resizeClip, false); 1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) a.recycle(); 1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) setResizeClip(resizeClip); 1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) @Override 182c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) public String[] getTransitionProperties() { 183c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) return sTransitionProperties; 184c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) } 185c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) 186c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) /** 187b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) * When <code>resizeClip</code> is true, ChangeBounds resizes the view using the clipBounds 188c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) * instead of changing the dimensions of the view during the animation. When 189c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) * <code>resizeClip</code> is false, ChangeBounds resizes the View by changing its dimensions. 190c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) * 19190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) * <p>When resizeClip is set to true, the clip bounds is modified by ChangeBounds. Therefore, 19290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) * {@link android.transition.ChangeClipBounds} is not compatible with ChangeBounds 193c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) * in this mode.</p> 194a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) * 195a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) * @param resizeClip Used to indicate whether the view bounds should be modified or the 196c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) * clip bounds should be modified by ChangeBounds. 197c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) * @see android.view.View#setClipBounds(android.graphics.Rect) 198c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) * @attr ref android.R.styleable#ChangeBounds_resizeClip 199c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) */ 200c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) public void setResizeClip(boolean resizeClip) { 201c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) mResizeClip = resizeClip; 202c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) } 203c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) 20490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) /** 205c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) * Returns true when the ChangeBounds will resize by changing the clip bounds during the 206c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) * view animation or false when bounds are changed. The default value is false. 207c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) * 2085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @return true when the ChangeBounds will resize by changing the clip bounds during the 2095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * view animation or false when bounds are changed. The default value is false. 2105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @attr ref android.R.styleable#ChangeBounds_resizeClip 2112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) */ 212b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) public boolean getResizeClip() { 21390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) return mResizeClip; 21490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) } 2155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 216a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) /** 217a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) * Setting this flag tells ChangeBounds to track the before/after parent 2185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * of every view using this transition. The flag is not enabled by 2195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * default because it requires the parent instances to be the same 2205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * in the two scenes or else all parents must use ids to allow 2215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * the transition to determine which parents are the same. 2225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * 2235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param reparent true if the transition should track the parent 2245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * container of target views and animate parent changes. 2255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @deprecated Use {@link android.transition.ChangeTransform} to handle 22690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) * transitions between different parents. 2275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */ 2285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) public void setReparent(boolean reparent) { 2295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) mReparent = reparent; 2305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 2315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) private void captureValues(TransitionValues values) { 233b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) View view = values.view; 23490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) 23590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) if (view.isLaidOut() || view.getWidth() != 0 || view.getHeight() != 0) { 2365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) values.values.put(PROPNAME_BOUNDS, new Rect(view.getLeft(), view.getTop(), 237a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) view.getRight(), view.getBottom())); 238a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) values.values.put(PROPNAME_PARENT, values.view.getParent()); 2395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (mReparent) { 2405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) values.view.getLocationInWindow(tempLocation); 2415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) values.values.put(PROPNAME_WINDOW_X, tempLocation[0]); 2425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) values.values.put(PROPNAME_WINDOW_Y, tempLocation[1]); 2435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 24490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) if (mResizeClip) { 2455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) values.values.put(PROPNAME_CLIP, view.getClipBounds()); 2465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 2475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 2482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 249b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 25090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) @Override 25190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) public void captureStartValues(TransitionValues transitionValues) { 2525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) captureValues(transitionValues); 253a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) } 254a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) 2555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) @Override 2565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) public void captureEndValues(TransitionValues transitionValues) { 2575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) captureValues(transitionValues); 2585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 2595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 26090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) private boolean parentMatches(View startParent, View endParent) { 2615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) boolean parentMatches = true; 2625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (mReparent) { 2635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) TransitionValues endValues = getMatchedTransitionValues(startParent, true); 2645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (endValues == null) { 2655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) parentMatches = startParent == endParent; 2665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } else { 2675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) parentMatches = endParent == endValues.view; 2685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 2695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 270 return parentMatches; 271 } 272 273 @Override 274 public Animator createAnimator(final ViewGroup sceneRoot, TransitionValues startValues, 275 TransitionValues endValues) { 276 if (startValues == null || endValues == null) { 277 return null; 278 } 279 Map<String, Object> startParentVals = startValues.values; 280 Map<String, Object> endParentVals = endValues.values; 281 ViewGroup startParent = (ViewGroup) startParentVals.get(PROPNAME_PARENT); 282 ViewGroup endParent = (ViewGroup) endParentVals.get(PROPNAME_PARENT); 283 if (startParent == null || endParent == null) { 284 return null; 285 } 286 final View view = endValues.view; 287 if (parentMatches(startParent, endParent)) { 288 Rect startBounds = (Rect) startValues.values.get(PROPNAME_BOUNDS); 289 Rect endBounds = (Rect) endValues.values.get(PROPNAME_BOUNDS); 290 final int startLeft = startBounds.left; 291 final int endLeft = endBounds.left; 292 final int startTop = startBounds.top; 293 final int endTop = endBounds.top; 294 final int startRight = startBounds.right; 295 final int endRight = endBounds.right; 296 final int startBottom = startBounds.bottom; 297 final int endBottom = endBounds.bottom; 298 final int startWidth = startRight - startLeft; 299 final int startHeight = startBottom - startTop; 300 final int endWidth = endRight - endLeft; 301 final int endHeight = endBottom - endTop; 302 Rect startClip = (Rect) startValues.values.get(PROPNAME_CLIP); 303 Rect endClip = (Rect) endValues.values.get(PROPNAME_CLIP); 304 int numChanges = 0; 305 if ((startWidth != 0 && startHeight != 0) || (endWidth != 0 && endHeight != 0)) { 306 if (startLeft != endLeft || startTop != endTop) ++numChanges; 307 if (startRight != endRight || startBottom != endBottom) ++numChanges; 308 } 309 if ((startClip != null && !startClip.equals(endClip)) || 310 (startClip == null && endClip != null)) { 311 ++numChanges; 312 } 313 if (numChanges > 0) { 314 Animator anim; 315 if (!mResizeClip) { 316 view.setLeftTopRightBottom(startLeft, startTop, startRight, startBottom); 317 if (numChanges == 2) { 318 if (startWidth == endWidth && startHeight == endHeight) { 319 Path topLeftPath = getPathMotion().getPath(startLeft, startTop, endLeft, 320 endTop); 321 anim = ObjectAnimator.ofObject(view, POSITION_PROPERTY, null, 322 topLeftPath); 323 } else { 324 final ViewBounds viewBounds = new ViewBounds(view); 325 Path topLeftPath = getPathMotion().getPath(startLeft, startTop, 326 endLeft, endTop); 327 ObjectAnimator topLeftAnimator = ObjectAnimator 328 .ofObject(viewBounds, TOP_LEFT_PROPERTY, null, topLeftPath); 329 330 Path bottomRightPath = getPathMotion().getPath(startRight, startBottom, 331 endRight, endBottom); 332 ObjectAnimator bottomRightAnimator = ObjectAnimator.ofObject(viewBounds, 333 BOTTOM_RIGHT_PROPERTY, null, bottomRightPath); 334 AnimatorSet set = new AnimatorSet(); 335 set.playTogether(topLeftAnimator, bottomRightAnimator); 336 anim = set; 337 set.addListener(new AnimatorListenerAdapter() { 338 // We need a strong reference to viewBounds until the 339 // animator ends. 340 private ViewBounds mViewBounds = viewBounds; 341 }); 342 } 343 } else if (startLeft != endLeft || startTop != endTop) { 344 Path topLeftPath = getPathMotion().getPath(startLeft, startTop, 345 endLeft, endTop); 346 anim = ObjectAnimator.ofObject(view, TOP_LEFT_ONLY_PROPERTY, null, 347 topLeftPath); 348 } else { 349 Path bottomRight = getPathMotion().getPath(startRight, startBottom, 350 endRight, endBottom); 351 anim = ObjectAnimator.ofObject(view, BOTTOM_RIGHT_ONLY_PROPERTY, null, 352 bottomRight); 353 } 354 } else { 355 int maxWidth = Math.max(startWidth, endWidth); 356 int maxHeight = Math.max(startHeight, endHeight); 357 358 view.setLeftTopRightBottom(startLeft, startTop, startLeft + maxWidth, 359 startTop + maxHeight); 360 361 ObjectAnimator positionAnimator = null; 362 if (startLeft != endLeft || startTop != endTop) { 363 Path topLeftPath = getPathMotion().getPath(startLeft, startTop, endLeft, 364 endTop); 365 positionAnimator = ObjectAnimator.ofObject(view, POSITION_PROPERTY, null, 366 topLeftPath); 367 } 368 final Rect finalClip = endClip; 369 if (startClip == null) { 370 startClip = new Rect(0, 0, startWidth, startHeight); 371 } 372 if (endClip == null) { 373 endClip = new Rect(0, 0, endWidth, endHeight); 374 } 375 ObjectAnimator clipAnimator = null; 376 if (!startClip.equals(endClip)) { 377 view.setClipBounds(startClip); 378 clipAnimator = ObjectAnimator.ofObject(view, "clipBounds", sRectEvaluator, 379 startClip, endClip); 380 clipAnimator.addListener(new AnimatorListenerAdapter() { 381 private boolean mIsCanceled; 382 383 @Override 384 public void onAnimationCancel(Animator animation) { 385 mIsCanceled = true; 386 } 387 388 @Override 389 public void onAnimationEnd(Animator animation) { 390 if (!mIsCanceled) { 391 view.setClipBounds(finalClip); 392 view.setLeftTopRightBottom(endLeft, endTop, endRight, 393 endBottom); 394 } 395 } 396 }); 397 } 398 anim = TransitionUtils.mergeAnimators(positionAnimator, 399 clipAnimator); 400 } 401 if (view.getParent() instanceof ViewGroup) { 402 final ViewGroup parent = (ViewGroup) view.getParent(); 403 parent.suppressLayout(true); 404 TransitionListener transitionListener = new TransitionListenerAdapter() { 405 boolean mCanceled = false; 406 407 @Override 408 public void onTransitionCancel(Transition transition) { 409 parent.suppressLayout(false); 410 mCanceled = true; 411 } 412 413 @Override 414 public void onTransitionEnd(Transition transition) { 415 if (!mCanceled) { 416 parent.suppressLayout(false); 417 } 418 } 419 420 @Override 421 public void onTransitionPause(Transition transition) { 422 parent.suppressLayout(false); 423 } 424 425 @Override 426 public void onTransitionResume(Transition transition) { 427 parent.suppressLayout(true); 428 } 429 }; 430 addListener(transitionListener); 431 } 432 return anim; 433 } 434 } else { 435 int startX = (Integer) startValues.values.get(PROPNAME_WINDOW_X); 436 int startY = (Integer) startValues.values.get(PROPNAME_WINDOW_Y); 437 int endX = (Integer) endValues.values.get(PROPNAME_WINDOW_X); 438 int endY = (Integer) endValues.values.get(PROPNAME_WINDOW_Y); 439 // TODO: also handle size changes: check bounds and animate size changes 440 if (startX != endX || startY != endY) { 441 sceneRoot.getLocationInWindow(tempLocation); 442 Bitmap bitmap = Bitmap.createBitmap(view.getWidth(), view.getHeight(), 443 Bitmap.Config.ARGB_8888); 444 Canvas canvas = new Canvas(bitmap); 445 view.draw(canvas); 446 final BitmapDrawable drawable = new BitmapDrawable(bitmap); 447 final float transitionAlpha = view.getTransitionAlpha(); 448 view.setTransitionAlpha(0); 449 sceneRoot.getOverlay().add(drawable); 450 Path topLeftPath = getPathMotion().getPath(startX - tempLocation[0], 451 startY - tempLocation[1], endX - tempLocation[0], endY - tempLocation[1]); 452 PropertyValuesHolder origin = PropertyValuesHolder.ofObject( 453 DRAWABLE_ORIGIN_PROPERTY, null, topLeftPath); 454 ObjectAnimator anim = ObjectAnimator.ofPropertyValuesHolder(drawable, origin); 455 anim.addListener(new AnimatorListenerAdapter() { 456 @Override 457 public void onAnimationEnd(Animator animation) { 458 sceneRoot.getOverlay().remove(drawable); 459 view.setTransitionAlpha(transitionAlpha); 460 } 461 }); 462 return anim; 463 } 464 } 465 return null; 466 } 467 468 private static class ViewBounds { 469 private int mLeft; 470 private int mTop; 471 private int mRight; 472 private int mBottom; 473 private boolean mIsTopLeftSet; 474 private boolean mIsBottomRightSet; 475 private View mView; 476 477 public ViewBounds(View view) { 478 mView = view; 479 } 480 481 public void setTopLeft(PointF topLeft) { 482 mLeft = Math.round(topLeft.x); 483 mTop = Math.round(topLeft.y); 484 mIsTopLeftSet = true; 485 if (mIsBottomRightSet) { 486 setLeftTopRightBottom(); 487 } 488 } 489 490 public void setBottomRight(PointF bottomRight) { 491 mRight = Math.round(bottomRight.x); 492 mBottom = Math.round(bottomRight.y); 493 mIsBottomRightSet = true; 494 if (mIsTopLeftSet) { 495 setLeftTopRightBottom(); 496 } 497 } 498 499 private void setLeftTopRightBottom() { 500 mView.setLeftTopRightBottom(mLeft, mTop, mRight, mBottom); 501 mIsTopLeftSet = false; 502 mIsBottomRightSet = false; 503 } 504 } 505} 506