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

Commit 2ea7f8b9 authored by Chet Haase's avatar Chet Haase
Browse files

Refactoring/simplifying Transition code/API

Transitions used to be three phase:
- captureValues(): get all relevant property values in the
affected view targets
- setup(): set appropriate start values for affected views
prior to any transitions being played
- play(): create/play Animators for affected views

Now the second and third phases have been collapsed (and named
"play()"). This single step sets initial values for target views
and creates any Animators that should be played during the transition.
The transition mechanism stores these Animators and then starts
them at the appropriate time in the overall transition.

Issue #9507585 Transitions: Simplify Transition.play() design

Change-Id: I3fc67599b38fe49eee885dc5d32444db90b7703b
parent 862a301b
Loading
Loading
Loading
Loading
+1 −12
Original line number Diff line number Diff line
@@ -18389,7 +18389,6 @@ package android.print.pdf {
  public final class PdfDocument {
    method public void close();
    method protected final void finalize() throws java.lang.Throwable;
    method public void finishPage(android.print.pdf.PdfDocument.Page);
    method public java.util.List<android.print.pdf.PdfDocument.PageInfo> getPages();
    method public static android.print.pdf.PdfDocument open();
@@ -28524,7 +28523,6 @@ package android.view.transition {
    method protected void captureValues(android.view.transition.TransitionValues, boolean);
    method public int getFadeBehavior();
    method public int getResizeBehavior();
    method protected android.animation.Animator play(android.view.ViewGroup, android.view.transition.TransitionValues, android.view.transition.TransitionValues);
    method public void setFadeBehavior(int);
    method public void setResizeBehavior(int);
    field public static final int FADE_BEHAVIOR_CROSSFADE = 0; // 0x0
@@ -28543,7 +28541,6 @@ package android.view.transition {
  public class Move extends android.view.transition.Transition {
    ctor public Move();
    method protected void captureValues(android.view.transition.TransitionValues, boolean);
    method protected android.animation.Animator play(android.view.ViewGroup, android.view.transition.TransitionValues, android.view.transition.TransitionValues);
    method public void setReparent(boolean);
    method public void setResizeClip(boolean);
  }
@@ -28551,13 +28548,11 @@ package android.view.transition {
  public class Recolor extends android.view.transition.Transition {
    ctor public Recolor();
    method protected void captureValues(android.view.transition.TransitionValues, boolean);
    method protected android.animation.Animator play(android.view.ViewGroup, android.view.transition.TransitionValues, android.view.transition.TransitionValues);
  }
  public class Rotate extends android.view.transition.Transition {
    ctor public Rotate();
    method protected void captureValues(android.view.transition.TransitionValues, boolean);
    method protected android.animation.Animator play(android.view.ViewGroup, android.view.transition.TransitionValues, android.view.transition.TransitionValues);
  }
  public final class Scene {
@@ -28578,7 +28573,6 @@ package android.view.transition {
  public class TextChange extends android.view.transition.Transition {
    ctor public TextChange();
    method protected void captureValues(android.view.transition.TransitionValues, boolean);
    method protected android.animation.Animator play(android.view.ViewGroup, android.view.transition.TransitionValues, android.view.transition.TransitionValues);
  }
  public abstract class Transition implements java.lang.Cloneable {
@@ -28597,14 +28591,13 @@ package android.view.transition {
    method protected void onTransitionCancel();
    method protected void onTransitionEnd();
    method protected void onTransitionStart();
    method protected abstract android.animation.Animator play(android.view.ViewGroup, android.view.transition.TransitionValues, android.view.transition.TransitionValues);
    method protected android.animation.Animator play(android.view.ViewGroup, android.view.transition.TransitionValues, android.view.transition.TransitionValues);
    method public void removeListener(android.view.transition.Transition.TransitionListener);
    method public android.view.transition.Transition setDuration(long);
    method public void setInterpolator(android.animation.TimeInterpolator);
    method public void setStartDelay(long);
    method public android.view.transition.Transition setTargetIds(int...);
    method public android.view.transition.Transition setTargets(android.view.View...);
    method protected boolean setup(android.view.ViewGroup, android.view.transition.TransitionValues, android.view.transition.TransitionValues);
  }
  public static abstract interface Transition.TransitionListener {
@@ -28618,7 +28611,6 @@ package android.view.transition {
    ctor public TransitionGroup(int);
    method public void addTransitions(android.view.transition.Transition...);
    method protected void captureValues(android.view.transition.TransitionValues, boolean);
    method protected android.animation.Animator play(android.view.ViewGroup, android.view.transition.TransitionValues, android.view.transition.TransitionValues);
    method public void removeTransition(android.view.transition.Transition);
    method public void setOrdering(int);
    field public static final int SEQUENTIALLY = 1; // 0x1
@@ -28657,9 +28649,6 @@ package android.view.transition {
    method protected android.animation.Animator appear(android.view.ViewGroup, android.view.transition.TransitionValues, int, android.view.transition.TransitionValues, int);
    method protected void captureValues(android.view.transition.TransitionValues, boolean);
    method protected android.animation.Animator disappear(android.view.ViewGroup, android.view.transition.TransitionValues, int, android.view.transition.TransitionValues, int);
    method protected android.animation.Animator play(android.view.ViewGroup, android.view.transition.TransitionValues, android.view.transition.TransitionValues);
    method protected boolean setupAppear(android.view.ViewGroup, android.view.transition.TransitionValues, int, android.view.transition.TransitionValues, int);
    method protected boolean setupDisappear(android.view.ViewGroup, android.view.transition.TransitionValues, int, android.view.transition.TransitionValues, int);
  }
}
+55 −71
Original line number Diff line number Diff line
@@ -140,18 +140,20 @@ public class Crossfade extends Transition {
    }

    @Override
    protected boolean setup(ViewGroup sceneRoot, TransitionValues startValues,
    protected Animator play(ViewGroup sceneRoot, TransitionValues startValues,
            TransitionValues endValues) {
        if (startValues == null || endValues == null) {
            return false;
            return null;
        }
        final View view = endValues.view;
        Map<String, Object> startVals = startValues.values;
        Map<String, Object> endVals = endValues.values;
        Rect startBounds = (Rect) startVals.get(PROPNAME_BOUNDS);
        Rect endBounds = (Rect) endVals.get(PROPNAME_BOUNDS);
        Bitmap startBitmap = (Bitmap) startVals.get(PROPNAME_BITMAP);
        Bitmap endBitmap = (Bitmap) endVals.get(PROPNAME_BITMAP);
        Drawable startDrawable = (Drawable) startVals.get(PROPNAME_DRAWABLE);
        Drawable endDrawable = (Drawable) endVals.get(PROPNAME_DRAWABLE);
        final BitmapDrawable startDrawable = (BitmapDrawable) startVals.get(PROPNAME_DRAWABLE);
        final BitmapDrawable endDrawable = (BitmapDrawable) endVals.get(PROPNAME_DRAWABLE);
        if (Transition.DBG) {
            Log.d(LOG_TAG, "StartBitmap.sameAs(endBitmap) = " + startBitmap.sameAs(endBitmap) +
                    " for start, end: " + startBitmap + ", " + endBitmap);
@@ -163,27 +165,6 @@ public class Crossfade extends Transition {
                overlay.add(endDrawable);
            }
            overlay.add(startDrawable);
            return true;
        } else {
            return false;
        }
    }

    @Override
    protected Animator play(ViewGroup sceneRoot, TransitionValues startValues,
            TransitionValues endValues) {
        if (startValues == null || endValues == null) {
            return null;
        }
        Map<String, Object> startVals = startValues.values;
        Map<String, Object> endVals = endValues.values;

        final View view = endValues.view;
        Rect startBounds = (Rect) startVals.get(PROPNAME_BOUNDS);
        Rect endBounds = (Rect) endVals.get(PROPNAME_BOUNDS);
        final BitmapDrawable startDrawable = (BitmapDrawable) startVals.get(PROPNAME_DRAWABLE);
        final BitmapDrawable endDrawable = (BitmapDrawable) endVals.get(PROPNAME_DRAWABLE);

            // The transition works by placing the end drawable under the start drawable and
            // gradually fading out the start drawable. So it's not really a cross-fade, but rather
            // a reveal of the end scene over time. Also, animate the bounds of both drawables
@@ -237,6 +218,9 @@ public class Crossfade extends Transition {
                }
            }
            return set;
        } else {
            return null;
        }
    }

    @Override
+46 −78
Original line number Diff line number Diff line
@@ -92,18 +92,6 @@ public class Fade extends Visibility {
        values.values.put(PROPNAME_SCREEN_Y, loc[1]);
    }

    @Override
    protected boolean setupAppear(ViewGroup sceneRoot,
            TransitionValues startValues, int startVisibility,
            TransitionValues endValues, int endVisibility) {
        View endView = (endValues != null) ? endValues.view : null;
        if ((mFadingMode & IN) != IN) {
            return false;
        }
        endView.setAlpha(0);
        return true;
    }

    @Override
    protected Animator appear(ViewGroup sceneRoot,
            TransitionValues startValues, int startVisibility,
@@ -112,17 +100,16 @@ public class Fade extends Visibility {
        if ((mFadingMode & IN) != IN) {
            return null;
        }
        // TODO: hack - retain original value from before setupAppear
        endView.setAlpha(0);
        return runAnimation(endView, 0, 1, null);
        // TODO: end listener to make sure we end at 1 no matter what
    }

    @Override
    protected boolean setupDisappear(ViewGroup sceneRoot,
    protected Animator disappear(ViewGroup sceneRoot,
            TransitionValues startValues, int startVisibility,
            TransitionValues endValues, int endVisibility) {
        if ((mFadingMode & OUT) != OUT) {
            return false;
            return null;
        }
        View view;
        View startView = (startValues != null) ? startValues.view : null;
@@ -153,6 +140,7 @@ public class Fade extends Visibility {
                }
            }
        }
        final int finalVisibility = endVisibility;
        // TODO: add automatic facility to Visibility superclass for keeping views around
        if (overlayView != null) {
            // TODO: Need to do this for general case of adding to overlay
@@ -163,54 +151,32 @@ public class Fade extends Visibility {
            overlayView.offsetLeftAndRight((screenX - loc[0]) - overlayView.getLeft());
            overlayView.offsetTopAndBottom((screenY - loc[1]) - overlayView.getTop());
            sceneRoot.getOverlay().add(overlayView);
            return true;
        }
        if (viewToKeep != null) {
            // TODO: find a different way to do this, like just changing the view to be
            // VISIBLE for the duration of the transition
            viewToKeep.setVisibility((View.VISIBLE));
            return true;
        }
        return false;
    }

            // TODO: add automatic facility to Visibility superclass for keeping views around
            final float startAlpha = view.getAlpha();
            float endAlpha = 0;
            final View finalView = view;
            final View finalOverlayView = overlayView;
            final View finalViewToKeep = viewToKeep;
            final ViewGroup finalSceneRoot = sceneRoot;
            final Animator.AnimatorListener endListener = new AnimatorListenerAdapter() {
                @Override
    protected Animator disappear(ViewGroup sceneRoot,
            TransitionValues startValues, int startVisibility,
            TransitionValues endValues, int endVisibility) {
        if ((mFadingMode & OUT) != OUT) {
            return null;
        }
        View startView = (startValues != null) ? startValues.view : null;
        View endView = (endValues != null) ? endValues.view : null;
        if (Transition.DBG) {
            Log.d(LOG_TAG, "Fade.disappear: startView, startVis, endView, endVis = " +
                startView + ", " + startVisibility + ", " + endView + ", " + endVisibility);
                public void onAnimationEnd(Animator animation) {
                    finalView.setAlpha(startAlpha);
                    // TODO: restore view offset from overlay repositioning
                    if (finalViewToKeep != null) {
                        finalViewToKeep.setVisibility(finalVisibility);
                    }
        View view;
        View overlayView = null;
        View viewToKeep = null;
        final int finalVisibility = endVisibility;
        if (endView == null) {
            // view was removed: add the start view to the Overlay
            view = startView;
            overlayView = view;
        } else {
            // visibility change
            if (endVisibility == View.INVISIBLE) {
                view = endView;
                viewToKeep = view;
            } else {
                // Becoming GONE
                if (startView == endView) {
                    view = endView;
                    viewToKeep = view;
                } else {
                    view = startView;
                    overlayView = view;
                    if (finalOverlayView != null) {
                        finalSceneRoot.getOverlay().remove(finalOverlayView);
                    }
                }
            };
            return runAnimation(view, startAlpha, endAlpha, endListener);
        }
        if (viewToKeep != null) {
            // TODO: find a different way to do this, like just changing the view to be
            // VISIBLE for the duration of the transition
            viewToKeep.setVisibility((View.VISIBLE));
            // TODO: add automatic facility to Visibility superclass for keeping views around
            final float startAlpha = view.getAlpha();
            float endAlpha = 0;
@@ -233,5 +199,7 @@ public class Fade extends Visibility {
            };
            return runAnimation(view, startAlpha, endAlpha, endListener);
        }
        return null;
    }

}
 No newline at end of file
+25 −90
Original line number Diff line number Diff line
@@ -84,24 +84,21 @@ public class Move extends Transition {
        if (startValues == null || endValues == null) {
            return null;
        }
        final View view = endValues.view;
        if (view.getParent() == null) {
            // TODO: Might want to make it possible to Move an disappearing view.
            // This workaround is here because if a parallel Fade is not running on the view
            // Then it won't get added to the hierarchy and the animator below will not fire,
            // causing the transition to not end
            return null;
        }
        // TODO: need to handle non-VG case?
        ViewGroup startParent = (ViewGroup) startValues.values.get(PROPNAME_PARENT);
        ViewGroup endParent = (ViewGroup) endValues.values.get(PROPNAME_PARENT);
        Map<String, Object> startParentVals = startValues.values;
        Map<String, Object> endParentVals = endValues.values;
        ViewGroup startParent = (ViewGroup) startParentVals.get(PROPNAME_PARENT);
        ViewGroup endParent = (ViewGroup) endParentVals.get(PROPNAME_PARENT);
        if (startParent == null || endParent == null) {
            return null;
        }
        final View view = endValues.view;
        boolean parentsEqual = (startParent == endParent) ||
                (startParent.getId() == endParent.getId());
        // TODO: Might want reparenting to be separate/subclass transition, or at least
        // triggered by a property on Move. Otherwise, we're forcing the requirement that
        // all parents in layouts have IDs to avoid layout-inflation resulting in a side-effect
        // of reparenting the views.
        if (!mReparent || parentsEqual) {
            // Common case - view belongs to the same layout before/after. Just animate its bounds
            Rect startBounds = (Rect) startValues.values.get(PROPNAME_BOUNDS);
            Rect endBounds = (Rect) endValues.values.get(PROPNAME_BOUNDS);
            int startLeft = startBounds.left;
@@ -118,13 +115,6 @@ public class Move extends Transition {
            int endHeight = endBottom - endTop;
            int numChanges = 0;
            if (startWidth != 0 && startHeight != 0 && endWidth != 0 && endHeight != 0) {
                if (Transition.DBG) {
                    Log.v(LOG_TAG, "Target = " + endValues.view);
                    Log.v(LOG_TAG, "    start bounds: " + startLeft + ", " + startTop + ", " +
                            startRight + ", " + startBottom);
                    Log.v(LOG_TAG, "    end bounds: " + endLeft + ", " + endTop + ", " +
                            endRight + ", " + endBottom);
                }
                if (startLeft != endLeft) ++numChanges;
                if (startTop != endTop) ++numChanges;
                if (startRight != endRight) ++numChanges;
@@ -134,6 +124,10 @@ public class Move extends Transition {
                if (!mResizeClip) {
                    PropertyValuesHolder pvh[] = new PropertyValuesHolder[numChanges];
                    int pvhIndex = 0;
                    if (startLeft != endLeft) view.setLeft(startLeft);
                    if (startTop != endTop) view.setTop(startTop);
                    if (startRight != endRight) view.setRight(startRight);
                    if (startBottom != endBottom) view.setBottom(startBottom);
                    if (startLeft != endLeft) {
                        pvh[pvhIndex++] = PropertyValuesHolder.ofInt("left", startLeft, endLeft);
                    }
@@ -161,6 +155,13 @@ public class Move extends Transition {
                    }
                    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;
@@ -206,71 +207,6 @@ public class Move extends Transition {
                    return anim;
                }
            }
        } else {
            return (ObjectAnimator) endValues.values.get("drawableAnim");
        }
        return null;
    }

    @Override
    protected boolean setup(final ViewGroup sceneRoot, TransitionValues startValues,
            TransitionValues endValues) {
        if (startValues == null || endValues == null) {
            return false;
        }
        Map<String, Object> startParentVals = startValues.values;
        Map<String, Object> endParentVals = endValues.values;
        ViewGroup startParent = (ViewGroup) startParentVals.get(PROPNAME_PARENT);
        ViewGroup endParent = (ViewGroup) endParentVals.get(PROPNAME_PARENT);
        if (startParent == null || endParent == null) {
            return false;
        }
        final View view = endValues.view;
        boolean parentsEqual = (startParent == endParent) ||
                (startParent.getId() == endParent.getId());
        // TODO: Might want reparenting to be separate/subclass transition, or at least
        // triggered by a property on Move. Otherwise, we're forcing the requirement that
        // all parents in layouts have IDs to avoid layout-inflation resulting in a side-effect
        // of reparenting the views.
        if (!mReparent || parentsEqual) {
            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;
            int numChanges = 0;
            if (startWidth != 0 && startHeight != 0 && endWidth != 0 && endHeight != 0) {
                if (startLeft != endLeft) ++numChanges;
                if (startTop != endTop) ++numChanges;
                if (startRight != endRight) ++numChanges;
                if (startBottom != endBottom) ++numChanges;
            }
            if (numChanges > 0) {
                if (!mResizeClip) {
                    if (startLeft != endLeft) view.setLeft(startLeft);
                    if (startTop != endTop) view.setTop(startTop);
                    if (startRight != endRight) view.setRight(startRight);
                    if (startBottom != endBottom) view.setBottom(startBottom);
                } 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);
                }
                return true;
            }
        } else {
            int startX = (Integer) startValues.values.get(PROPNAME_WINDOW_X);
            int startY = (Integer) startValues.values.get(PROPNAME_WINDOW_Y);
@@ -286,14 +222,14 @@ public class Move extends Transition {
                final BitmapDrawable drawable = new BitmapDrawable(bitmap);
                view.setVisibility(View.INVISIBLE);
                sceneRoot.getOverlay().add(drawable);
                Rect startBounds = new Rect(startX - tempLocation[0], startY - tempLocation[1],
                Rect startBounds1 = new Rect(startX - tempLocation[0], startY - tempLocation[1],
                        startX - tempLocation[0] + view.getWidth(),
                        startY - tempLocation[1] + view.getHeight());
                Rect endBounds = new Rect(endX - tempLocation[0], endY - tempLocation[1],
                Rect endBounds1 = new Rect(endX - tempLocation[0], endY - tempLocation[1],
                        endX - tempLocation[0] + view.getWidth(),
                        endY - tempLocation[1] + view.getHeight());
                ObjectAnimator anim = ObjectAnimator.ofObject(drawable, "bounds",
                        sRectEvaluator, startBounds, endBounds);
                        sRectEvaluator, startBounds1, endBounds1);
                anim.addListener(new AnimatorListenerAdapter() {
                    @Override
                    public void onAnimationEnd(Animator animation) {
@@ -301,10 +237,9 @@ public class Move extends Transition {
                        view.setVisibility(View.VISIBLE);
                    }
                });
                endParentVals.put("drawableAnim", anim);
                return true;
                return anim;
            }
        }
        return false;
        return null;
    }
}
+6 −40
Original line number Diff line number Diff line
@@ -51,10 +51,10 @@ public class Recolor extends Transition {
    }

    @Override
    protected boolean setup(ViewGroup sceneRoot, TransitionValues startValues,
    protected Animator play(ViewGroup sceneRoot, TransitionValues startValues,
            TransitionValues endValues) {
        if (startValues == null || endValues == null) {
            return false;
            return null;
        }
        final View view = endValues.view;
        Drawable startBackground = (Drawable) startValues.values.get(PROPNAME_BACKGROUND);
@@ -66,6 +66,8 @@ public class Recolor extends Transition {
            if (startColor.getColor() != endColor.getColor()) {
                endColor.setColor(startColor.getColor());
                changed = true;
                return ObjectAnimator.ofObject(endBackground, "color",
                        new ArgbEvaluator(), startColor.getColor(), endColor.getColor());
            }
        }
        if (view instanceof TextView) {
@@ -75,46 +77,10 @@ public class Recolor extends Transition {
            if (start != end) {
                textView.setTextColor(end);
                changed = true;
            }
        }
        return changed;
    }

    @Override
    protected Animator play(ViewGroup sceneRoot, TransitionValues startValues,
            TransitionValues endValues) {
        if (startValues == null || endValues == null) {
            return null;
        }
        ObjectAnimator anim = null;
        final View view = endValues.view;
        Map<String, Object> startVals = startValues.values;
        Map<String, Object> endVals = endValues.values;
        Drawable startBackground = (Drawable) startVals.get(PROPNAME_BACKGROUND);
        Drawable endBackground = (Drawable) endVals.get(PROPNAME_BACKGROUND);
        if (startBackground instanceof ColorDrawable && endBackground instanceof ColorDrawable) {
            ColorDrawable startColor = (ColorDrawable) startBackground;
            ColorDrawable endColor = (ColorDrawable) endBackground;
            if (startColor.getColor() != endColor.getColor()) {
                anim = ObjectAnimator.ofObject(endBackground, "color",
                        new ArgbEvaluator(), startColor.getColor(), endColor.getColor());
                if (getStartDelay() > 0) {
                    endColor.setColor(startColor.getColor());
                }
            }
        }
        if (view instanceof TextView) {
            TextView textView = (TextView) view;
            int start = (Integer) startValues.values.get(PROPNAME_TEXT_COLOR);
            int end = (Integer) endValues.values.get(PROPNAME_TEXT_COLOR);
            if (start != end) {
                anim = ObjectAnimator.ofObject(textView, "textColor",
                return ObjectAnimator.ofObject(textView, "textColor",
                        new ArgbEvaluator(), start, end);
                if (getStartDelay() > 0) {
                    textView.setTextColor(end);
                }
            }
        }
        return anim;
        return null;
    }
}
Loading