diff --git a/api/current.txt b/api/current.txt index bfe8d7a65e376..4b8d61585f4c5 100644 --- a/api/current.txt +++ b/api/current.txt @@ -1002,6 +1002,7 @@ package android { field public static final int requiredForAllUsers = 16843728; // 0x10103d0 field public static final int requiresFadingEdge = 16843685; // 0x10103a5 field public static final int requiresSmallestWidthDp = 16843620; // 0x1010364 + field public static final int resizeClip = 16844033; // 0x1010501 field public static final int resizeMode = 16843619; // 0x1010363 field public static final int resizeable = 16843405; // 0x101028d field public static final int resource = 16842789; // 0x1010025 @@ -31316,6 +31317,7 @@ package android.transition { ctor public ChangeBounds(android.content.Context, android.util.AttributeSet); method public void captureEndValues(android.transition.TransitionValues); method public void captureStartValues(android.transition.TransitionValues); + method public boolean getResizeClip(); method public deprecated void setReparent(boolean); method public void setResizeClip(boolean); } diff --git a/core/java/android/transition/ChangeBounds.java b/core/java/android/transition/ChangeBounds.java index 2c55141a5ce63..c82587b76fe17 100644 --- a/core/java/android/transition/ChangeBounds.java +++ b/core/java/android/transition/ChangeBounds.java @@ -18,6 +18,7 @@ package android.transition; import android.animation.AnimatorSet; import android.content.Context; +import android.content.res.TypedArray; import android.graphics.PointF; import android.animation.Animator; @@ -36,6 +37,8 @@ import android.util.Property; import android.view.View; import android.view.ViewGroup; +import com.android.internal.R; + import java.util.Map; /** @@ -43,17 +46,20 @@ import java.util.Map; * the scene change and animates those changes during the transition. * *

A ChangeBounds transition can be described in a resource file by using the - * tag changeBounds, along with the other standard + * tag changeBounds, using its attributes of + * {@link android.R.styleable#ChangeBounds} along with the other standard * attributes of {@link android.R.styleable#Transition}.

*/ public class ChangeBounds extends Transition { private static final String PROPNAME_BOUNDS = "android:changeBounds:bounds"; + private static final String PROPNAME_CLIP = "android:changeBounds:clip"; private static final String PROPNAME_PARENT = "android:changeBounds:parent"; private static final String PROPNAME_WINDOW_X = "android:changeBounds:windowX"; private static final String PROPNAME_WINDOW_Y = "android:changeBounds:windowY"; private static final String[] sTransitionProperties = { PROPNAME_BOUNDS, + PROPNAME_CLIP, PROPNAME_PARENT, PROPNAME_WINDOW_X, PROPNAME_WINDOW_Y @@ -103,6 +109,57 @@ public class ChangeBounds extends Transition { } }; + private static final Property BOTTOM_RIGHT_ONLY_PROPERTY = + new Property(PointF.class, "bottomRight") { + @Override + public void set(View view, PointF bottomRight) { + int left = view.getLeft(); + int top = view.getTop(); + int right = Math.round(bottomRight.x); + int bottom = Math.round(bottomRight.y); + view.setLeftTopRightBottom(left, top, right, bottom); + } + + @Override + public PointF get(View view) { + return null; + } + }; + + private static final Property TOP_LEFT_ONLY_PROPERTY = + new Property(PointF.class, "topLeft") { + @Override + public void set(View view, PointF topLeft) { + int left = Math.round(topLeft.x); + int top = Math.round(topLeft.y); + int right = view.getRight(); + int bottom = view.getBottom(); + view.setLeftTopRightBottom(left, top, right, bottom); + } + + @Override + public PointF get(View view) { + return null; + } + }; + + private static final Property POSITION_PROPERTY = + new Property(PointF.class, "position") { + @Override + public void set(View view, PointF topLeft) { + int left = Math.round(topLeft.x); + int top = Math.round(topLeft.y); + int right = left + view.getWidth(); + int bottom = top + view.getHeight(); + view.setLeftTopRightBottom(left, top, right, bottom); + } + + @Override + public PointF get(View view) { + return null; + } + }; + int[] tempLocation = new int[2]; boolean mResizeClip = false; boolean mReparent = false; @@ -114,6 +171,11 @@ public class ChangeBounds extends Transition { public ChangeBounds(Context context, AttributeSet attrs) { super(context, attrs); + + TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ChangeBounds); + boolean resizeClip = a.getBoolean(R.styleable.ChangeBounds_resizeClip, false); + a.recycle(); + setResizeClip(resizeClip); } @Override @@ -121,10 +183,36 @@ public class ChangeBounds extends Transition { return sTransitionProperties; } + /** + * When resizeClip is true, ChangeBounds resizes the view using the clipBounds + * instead of changing the dimensions of the view during the animation. When + * resizeClip is false, ChangeBounds resizes the View by changing its dimensions. + * + *

When resizeClip is set to true, the clip bounds is modified by ChangeBounds. Therefore, + * {@link android.transition.ChangeClipBounds} is not compatible with ChangeBounds + * in this mode.

+ * + * @param resizeClip Used to indicate whether the view bounds should be modified or the + * clip bounds should be modified by ChangeBounds. + * @see android.view.View#setClipBounds(android.graphics.Rect) + * @attr ref android.R.styleable#ChangeBounds_resizeClip + */ public void setResizeClip(boolean resizeClip) { mResizeClip = resizeClip; } + /** + * Returns true when the ChangeBounds will resize by changing the clip bounds during the + * view animation or false when bounds are changed. The default value is false. + * + * @return true when the ChangeBounds will resize by changing the clip bounds during the + * view animation or false when bounds are changed. The default value is false. + * @attr ref android.R.styleable#ChangeBounds_resizeClip + */ + public boolean getResizeClip() { + return mResizeClip; + } + /** * Setting this flag tells ChangeBounds to track the before/after parent * of every view using this transition. The flag is not enabled by @@ -153,6 +241,9 @@ public class ChangeBounds extends Transition { values.values.put(PROPNAME_WINDOW_X, tempLocation[0]); values.values.put(PROPNAME_WINDOW_Y, tempLocation[1]); } + if (mResizeClip) { + values.values.put(PROPNAME_CLIP, view.getClipBounds()); + } } } @@ -196,142 +287,149 @@ public class ChangeBounds extends Transition { if (parentMatches(startParent, endParent)) { Rect startBounds = (Rect) startValues.values.get(PROPNAME_BOUNDS); Rect endBounds = (Rect) endValues.values.get(PROPNAME_BOUNDS); - int startLeft = startBounds.left; - int endLeft = endBounds.left; - int startTop = startBounds.top; - int endTop = endBounds.top; - int startRight = startBounds.right; - int endRight = endBounds.right; - int startBottom = startBounds.bottom; - int endBottom = endBounds.bottom; - int startWidth = startRight - startLeft; - int startHeight = startBottom - startTop; - int endWidth = endRight - endLeft; - int endHeight = endBottom - endTop; + final int startLeft = startBounds.left; + final int endLeft = endBounds.left; + final int startTop = startBounds.top; + final int endTop = endBounds.top; + final int startRight = startBounds.right; + final int endRight = endBounds.right; + final int startBottom = startBounds.bottom; + final int endBottom = endBounds.bottom; + final int startWidth = startRight - startLeft; + final int startHeight = startBottom - startTop; + final int endWidth = endRight - endLeft; + final int endHeight = endBottom - endTop; + Rect startClip = (Rect) startValues.values.get(PROPNAME_CLIP); + Rect endClip = (Rect) endValues.values.get(PROPNAME_CLIP); int numChanges = 0; if ((startWidth != 0 && startHeight != 0) || (endWidth != 0 && endHeight != 0)) { if (startLeft != endLeft || startTop != endTop) ++numChanges; if (startRight != endRight || startBottom != endBottom) ++numChanges; } + if ((startClip != null && !startClip.equals(endClip)) || + (startClip == null && endClip != null)) { + ++numChanges; + } if (numChanges > 0) { + Animator anim; if (!mResizeClip) { view.setLeftTopRightBottom(startLeft, startTop, startRight, startBottom); - ViewBounds viewBounds = new ViewBounds(view); - Path topLeftPath = getPathMotion().getPath(startLeft, startTop, - endLeft, endTop); - ObjectAnimator topLeftAnimator = ObjectAnimator - .ofObject(viewBounds, TOP_LEFT_PROPERTY, null, topLeftPath); + if (numChanges == 2) { + if (startWidth == endWidth && startHeight == endHeight) { + Path topLeftPath = getPathMotion().getPath(startLeft, startTop, endLeft, + endTop); + anim = ObjectAnimator.ofObject(view, POSITION_PROPERTY, null, + topLeftPath); + } else { + final ViewBounds viewBounds = new ViewBounds(view); + Path topLeftPath = getPathMotion().getPath(startLeft, startTop, + endLeft, endTop); + ObjectAnimator topLeftAnimator = ObjectAnimator + .ofObject(viewBounds, TOP_LEFT_PROPERTY, null, topLeftPath); - Path bottomRightPath = getPathMotion().getPath(startRight, startBottom, - endRight, endBottom); - ObjectAnimator bottomRightAnimator = ObjectAnimator.ofObject(viewBounds, - BOTTOM_RIGHT_PROPERTY, null, bottomRightPath); - AnimatorSet anim = new AnimatorSet(); - anim.playTogether(topLeftAnimator, bottomRightAnimator); - - if (view.getParent() instanceof ViewGroup) { - final ViewGroup parent = (ViewGroup) view.getParent(); - parent.suppressLayout(true); - TransitionListener transitionListener = new TransitionListenerAdapter() { - boolean mCanceled = false; - - @Override - public void onTransitionCancel(Transition transition) { - parent.suppressLayout(false); - mCanceled = true; - } - - @Override - public void onTransitionEnd(Transition transition) { - if (!mCanceled) { - parent.suppressLayout(false); - } - } - - @Override - public void onTransitionPause(Transition transition) { - parent.suppressLayout(false); - } - - @Override - public void onTransitionResume(Transition transition) { - parent.suppressLayout(true); - } - }; - addListener(transitionListener); + Path bottomRightPath = getPathMotion().getPath(startRight, startBottom, + endRight, endBottom); + ObjectAnimator bottomRightAnimator = ObjectAnimator.ofObject(viewBounds, + BOTTOM_RIGHT_PROPERTY, null, bottomRightPath); + AnimatorSet set = new AnimatorSet(); + set.playTogether(topLeftAnimator, bottomRightAnimator); + anim = set; + set.addListener(new AnimatorListenerAdapter() { + // We need a strong reference to viewBounds until the + // animator ends. + private ViewBounds mViewBounds = viewBounds; + }); + } + } else if (startLeft != endLeft || startTop != endTop) { + Path topLeftPath = getPathMotion().getPath(startLeft, startTop, + endLeft, endTop); + anim = ObjectAnimator.ofObject(view, TOP_LEFT_ONLY_PROPERTY, null, + topLeftPath); + } else { + Path bottomRight = getPathMotion().getPath(startRight, startBottom, + endRight, endBottom); + anim = ObjectAnimator.ofObject(view, BOTTOM_RIGHT_ONLY_PROPERTY, null, + bottomRight); } - return anim; } else { - if (startWidth != endWidth) view.setRight(endLeft + - Math.max(startWidth, endWidth)); - if (startHeight != endHeight) view.setBottom(endTop + - Math.max(startHeight, endHeight)); - // TODO: don't clobber TX/TY - if (startLeft != endLeft) view.setTranslationX(startLeft - endLeft); - if (startTop != endTop) view.setTranslationY(startTop - endTop); - // Animate location with translationX/Y and size with clip bounds - float transXDelta = endLeft - startLeft; - float transYDelta = endTop - startTop; - int widthDelta = endWidth - startWidth; - int heightDelta = endHeight - startHeight; - numChanges = 0; - if (transXDelta != 0) numChanges++; - if (transYDelta != 0) numChanges++; - if (widthDelta != 0 || heightDelta != 0) numChanges++; - ObjectAnimator translationAnimator = null; - if (transXDelta != 0 || transYDelta != 0) { - Path topLeftPath = getPathMotion().getPath(0, 0, transXDelta, transYDelta); - translationAnimator = ObjectAnimator.ofFloat(view, View.TRANSLATION_X, - View.TRANSLATION_Y, topLeftPath); + int maxWidth = Math.max(startWidth, endWidth); + int maxHeight = Math.max(startHeight, endHeight); + + view.setLeftTopRightBottom(startLeft, startTop, startLeft + maxWidth, + startTop + maxHeight); + + ObjectAnimator positionAnimator = null; + if (startLeft != endLeft || startTop != endTop) { + Path topLeftPath = getPathMotion().getPath(startLeft, startTop, endLeft, + endTop); + positionAnimator = ObjectAnimator.ofObject(view, POSITION_PROPERTY, null, + topLeftPath); + } + final Rect finalClip = endClip; + if (startClip == null) { + startClip = new Rect(0, 0, startWidth, startHeight); + } + if (endClip == null) { + endClip = new Rect(0, 0, endWidth, endHeight); } ObjectAnimator clipAnimator = null; - if (widthDelta != 0 || heightDelta != 0) { - Rect tempStartBounds = new Rect(0, 0, startWidth, startHeight); - Rect tempEndBounds = new Rect(0, 0, endWidth, endHeight); + if (!startClip.equals(endClip)) { + view.setClipBounds(startClip); clipAnimator = ObjectAnimator.ofObject(view, "clipBounds", sRectEvaluator, - tempStartBounds, tempEndBounds); - } - Animator anim = TransitionUtils.mergeAnimators(translationAnimator, - clipAnimator); - if (view.getParent() instanceof ViewGroup) { - final ViewGroup parent = (ViewGroup) view.getParent(); - parent.suppressLayout(true); - TransitionListener transitionListener = new TransitionListenerAdapter() { - boolean mCanceled = false; + startClip, endClip); + clipAnimator.addListener(new AnimatorListenerAdapter() { + private boolean mIsCanceled; @Override - public void onTransitionCancel(Transition transition) { - parent.suppressLayout(false); - mCanceled = true; + public void onAnimationCancel(Animator animation) { + mIsCanceled = true; } @Override - public void onTransitionEnd(Transition transition) { - if (!mCanceled) { - parent.suppressLayout(false); + public void onAnimationEnd(Animator animation) { + if (!mIsCanceled) { + view.setClipBounds(finalClip); + view.setLeftTopRightBottom(endLeft, endTop, endRight, + endBottom); } } + }); + } + anim = TransitionUtils.mergeAnimators(positionAnimator, + clipAnimator); + } + if (view.getParent() instanceof ViewGroup) { + final ViewGroup parent = (ViewGroup) view.getParent(); + parent.suppressLayout(true); + TransitionListener transitionListener = new TransitionListenerAdapter() { + boolean mCanceled = false; - @Override - public void onTransitionPause(Transition transition) { + @Override + public void onTransitionCancel(Transition transition) { + parent.suppressLayout(false); + mCanceled = true; + } + + @Override + public void onTransitionEnd(Transition transition) { + if (!mCanceled) { parent.suppressLayout(false); } - - @Override - public void onTransitionResume(Transition transition) { - parent.suppressLayout(true); - } - }; - addListener(transitionListener); - } - anim.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - view.setClipBounds(null); } - }); - return anim; + + @Override + public void onTransitionPause(Transition transition) { + parent.suppressLayout(false); + } + + @Override + public void onTransitionResume(Transition transition) { + parent.suppressLayout(true); + } + }; + addListener(transitionListener); } + return anim; } } else { int startX = (Integer) startValues.values.get(PROPNAME_WINDOW_X); diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 8664a242ee125..9a4dbe936112a 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -15676,7 +15676,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, sizeChange(newWidth, newHeight, oldWidth, oldHeight); } - if ((mViewFlags & VISIBILITY_MASK) == VISIBLE) { + if ((mViewFlags & VISIBILITY_MASK) == VISIBLE || mGhostView != null) { // If we are visible, force the DRAWN bit to on so that // this invalidate will go through (at least to our parent). // This is because someone may have invalidated this view diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index d9884801d369a..7b4df49390e05 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -5702,6 +5702,17 @@ + + + + + + diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index 7568252d912b4..45208ab6b4d58 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -2589,5 +2589,6 @@ +