Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b7573c2d authored by George Mount's avatar George Mount
Browse files

Add resizeClip attribute to ChangeBounds.

Bug 17765948

Added the XML attribute for the resizeClip property of
ChangeBounds. Also updated support for resizing a view
using its clip bounds and position to fix the TODO.

Change-Id: I358d07d6a28fed4b7f39f9ccb9d89b9325f64239
parent 104d2484
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -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);
  }
+208 −110
Original line number Diff line number Diff line
@@ -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.
 *
 * <p>A ChangeBounds transition can be described in a resource file by using the
 * tag <code>changeBounds</code>, along with the other standard
 * tag <code>changeBounds</code>, using its attributes of
 * {@link android.R.styleable#ChangeBounds} along with the other standard
 * attributes of {@link android.R.styleable#Transition}.</p>
 */
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<View, PointF> BOTTOM_RIGHT_ONLY_PROPERTY =
            new Property<View, PointF>(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<View, PointF> TOP_LEFT_ONLY_PROPERTY =
            new Property<View, PointF>(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<View, PointF> POSITION_PROPERTY =
            new Property<View, PointF>(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 <code>resizeClip</code> is true, ChangeBounds resizes the view using the clipBounds
     * instead of changing the dimensions of the view during the animation. When
     * <code>resizeClip</code> is false, ChangeBounds resizes the View by changing its dimensions.
     *
     * <p>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.</p>
     *
     * @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,27 +287,41 @@ 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);
                    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
@@ -226,73 +331,73 @@ public class ChangeBounds extends Transition {
                                    endRight, endBottom);
                            ObjectAnimator bottomRightAnimator = ObjectAnimator.ofObject(viewBounds,
                                    BOTTOM_RIGHT_PROPERTY, null, bottomRightPath);
                    AnimatorSet anim = new AnimatorSet();
                    anim.playTogether(topLeftAnimator, bottomRightAnimator);
                            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);
                    }
                } else {
                    int maxWidth = Math.max(startWidth, endWidth);
                    int maxHeight = Math.max(startHeight, endHeight);

                    if (view.getParent() instanceof ViewGroup) {
                        final ViewGroup parent = (ViewGroup) view.getParent();
                        parent.suppressLayout(true);
                        TransitionListener transitionListener = new TransitionListenerAdapter() {
                            boolean mCanceled = false;
                    view.setLeftTopRightBottom(startLeft, startTop, startLeft + maxWidth,
                            startTop + maxHeight);

                            @Override
                            public void onTransitionCancel(Transition transition) {
                                parent.suppressLayout(false);
                                mCanceled = true;
                    ObjectAnimator positionAnimator = null;
                    if (startLeft != endLeft || startTop != endTop) {
                        Path topLeftPath = getPathMotion().getPath(startLeft, startTop, endLeft,
                                endTop);
                        positionAnimator = ObjectAnimator.ofObject(view, POSITION_PROPERTY, null,
                                topLeftPath);
                    }

                            @Override
                            public void onTransitionEnd(Transition transition) {
                                if (!mCanceled) {
                                    parent.suppressLayout(false);
                    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 (!startClip.equals(endClip)) {
                        view.setClipBounds(startClip);
                        clipAnimator = ObjectAnimator.ofObject(view, "clipBounds", sRectEvaluator,
                                startClip, endClip);
                        clipAnimator.addListener(new AnimatorListenerAdapter() {
                            private boolean mIsCanceled;

                            @Override
                            public void onTransitionPause(Transition transition) {
                                parent.suppressLayout(false);
                            public void onAnimationCancel(Animator animation) {
                                mIsCanceled = true;
                            }

                            @Override
                            public void onTransitionResume(Transition transition) {
                                parent.suppressLayout(true);
                            }
                        };
                        addListener(transitionListener);
                            public void onAnimationEnd(Animator animation) {
                                if (!mIsCanceled) {
                                    view.setClipBounds(finalClip);
                                    view.setLeftTopRightBottom(endLeft, endTop, endRight,
                                            endBottom);
                                }
                    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);
                            }
                    ObjectAnimator clipAnimator = null;
                    if (widthDelta != 0 || heightDelta != 0) {
                        Rect tempStartBounds = new Rect(0, 0, startWidth, startHeight);
                        Rect tempEndBounds = new Rect(0, 0, endWidth, endHeight);
                        clipAnimator = ObjectAnimator.ofObject(view, "clipBounds", sRectEvaluator,
                                tempStartBounds, tempEndBounds);
                        });
                    }
                    Animator anim = TransitionUtils.mergeAnimators(translationAnimator,
                    anim = TransitionUtils.mergeAnimators(positionAnimator,
                            clipAnimator);
                }
                if (view.getParent() instanceof ViewGroup) {
                    final ViewGroup parent = (ViewGroup) view.getParent();
                    parent.suppressLayout(true);
@@ -324,15 +429,8 @@ public class ChangeBounds extends Transition {
                    };
                    addListener(transitionListener);
                }
                    anim.addListener(new AnimatorListenerAdapter() {
                        @Override
                        public void onAnimationEnd(Animator animation) {
                            view.setClipBounds(null);
                        }
                    });
                return anim;
            }
            }
        } else {
            int startX = (Integer) startValues.values.get(PROPNAME_WINDOW_X);
            int startY = (Integer) startValues.values.get(PROPNAME_WINDOW_Y);
+1 −1
Original line number Diff line number Diff line
@@ -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
+11 −0
Original line number Diff line number Diff line
@@ -5702,6 +5702,17 @@
        <attr name="reparent" format="boolean"/>
    </declare-styleable>

    <!-- Use <code>changeBounds</code>as the root tag of the XML resource that
         describes a {@link android.transition.ChangeBounds} transition.
         The attributes of the {@link android.R.styleable#Transition Transition}
         resource are available in addition to the specific attributes of ChangeBounds
         described here. -->
    <declare-styleable name="ChangeBounds">
        <!-- Resize the view by adjusting the clipBounds rather than changing the
             dimensions of the view itself. The default value is false. -->
        <attr name="resizeClip" format="boolean"/>
    </declare-styleable>

    <!-- Use <code>transitionManager</code> as the root tag of the XML resource that
         describes a {@link android.transition.TransitionManager
         TransitionManager}. -->
+1 −0
Original line number Diff line number Diff line
@@ -2589,5 +2589,6 @@

    <!-- @hide -->
    <public-padding type="attr" name="private_resource_pad" end="0x01010500" />
    <public type="attr" name="resizeClip"/>

</resources>