summaryrefslogtreecommitdiffstats
path: root/core/java/android/transition/Crossfade.java
diff options
context:
space:
mode:
Diffstat (limited to 'core/java/android/transition/Crossfade.java')
-rw-r--r--core/java/android/transition/Crossfade.java296
1 files changed, 296 insertions, 0 deletions
diff --git a/core/java/android/transition/Crossfade.java b/core/java/android/transition/Crossfade.java
new file mode 100644
index 0000000..69ce872
--- /dev/null
+++ b/core/java/android/transition/Crossfade.java
@@ -0,0 +1,296 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.transition;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.animation.RectEvaluator;
+import android.animation.ValueAnimator;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Rect;
+import android.graphics.drawable.BitmapDrawable;
+import android.util.Log;
+import android.view.SurfaceView;
+import android.view.TextureView;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewOverlay;
+
+import java.util.Map;
+
+/**
+ * This transition captures bitmap representations of target views before and
+ * after the scene change and fades between them.
+ *
+ * <p>Note: This transition is not compatible with {@link TextureView}
+ * or {@link SurfaceView}.</p>
+ *
+ * @hide
+ */
+public class Crossfade extends Transition {
+ // TODO: Add a hook that lets a Transition call user code to query whether it should run on
+ // a given target view. This would save bitmap comparisons in this transition, for example.
+
+ private static final String LOG_TAG = "Crossfade";
+
+ private static final String PROPNAME_BITMAP = "android:crossfade:bitmap";
+ private static final String PROPNAME_DRAWABLE = "android:crossfade:drawable";
+ private static final String PROPNAME_BOUNDS = "android:crossfade:bounds";
+
+ private static RectEvaluator sRectEvaluator = new RectEvaluator();
+
+ private int mFadeBehavior = FADE_BEHAVIOR_REVEAL;
+ private int mResizeBehavior = RESIZE_BEHAVIOR_SCALE;
+
+ /**
+ * Flag specifying that the fading animation should cross-fade
+ * between the old and new representation of all affected target
+ * views. This means that the old representation will fade out
+ * while the new one fades in. This effect may work well on views
+ * without solid backgrounds, such as TextViews.
+ *
+ * @see #setFadeBehavior(int)
+ */
+ public static final int FADE_BEHAVIOR_CROSSFADE = 0;
+ /**
+ * Flag specifying that the fading animation should reveal the
+ * new representation of all affected target views. This means
+ * that the old representation will fade out, gradually
+ * revealing the new representation, which remains opaque
+ * the whole time. This effect may work well on views
+ * with solid backgrounds, such as ImageViews.
+ *
+ * @see #setFadeBehavior(int)
+ */
+ public static final int FADE_BEHAVIOR_REVEAL = 1;
+ /**
+ * Flag specifying that the fading animation should first fade
+ * out the original representation completely and then fade in the
+ * new one. This effect may be more suitable than the other
+ * fade behaviors for views with.
+ *
+ * @see #setFadeBehavior(int)
+ */
+ public static final int FADE_BEHAVIOR_OUT_IN = 2;
+
+ /**
+ * Flag specifying that the transition should not animate any
+ * changes in size between the old and new target views.
+ * This means that no scaling will take place as a result of
+ * this transition
+ *
+ * @see #setResizeBehavior(int)
+ */
+ public static final int RESIZE_BEHAVIOR_NONE = 0;
+ /**
+ * Flag specifying that the transition should animate any
+ * changes in size between the old and new target views.
+ * This means that the animation will scale the start/end
+ * representations of affected views from the starting size
+ * to the ending size over the course of the animation.
+ * This effect may work well on images, but is not recommended
+ * for text.
+ *
+ * @see #setResizeBehavior(int)
+ */
+ public static final int RESIZE_BEHAVIOR_SCALE = 1;
+
+ // TODO: Add fade/resize behaviors to xml resources
+
+ /**
+ * Sets the type of fading animation that will be run, one of
+ * {@link #FADE_BEHAVIOR_CROSSFADE} and {@link #FADE_BEHAVIOR_REVEAL}.
+ *
+ * @param fadeBehavior The type of fading animation to use when this
+ * transition is run.
+ */
+ public Crossfade setFadeBehavior(int fadeBehavior) {
+ if (fadeBehavior >= FADE_BEHAVIOR_CROSSFADE && fadeBehavior <= FADE_BEHAVIOR_OUT_IN) {
+ mFadeBehavior = fadeBehavior;
+ }
+ return this;
+ }
+
+ /**
+ * Returns the fading behavior of the animation.
+ *
+ * @return This crossfade object.
+ * @see #setFadeBehavior(int)
+ */
+ public int getFadeBehavior() {
+ return mFadeBehavior;
+ }
+
+ /**
+ * Sets the type of resizing behavior that will be used during the
+ * transition animation, one of {@link #RESIZE_BEHAVIOR_NONE} and
+ * {@link #RESIZE_BEHAVIOR_SCALE}.
+ *
+ * @param resizeBehavior The type of resizing behavior to use when this
+ * transition is run.
+ */
+ public Crossfade setResizeBehavior(int resizeBehavior) {
+ if (resizeBehavior >= RESIZE_BEHAVIOR_NONE && resizeBehavior <= RESIZE_BEHAVIOR_SCALE) {
+ mResizeBehavior = resizeBehavior;
+ }
+ return this;
+ }
+
+ /**
+ * Returns the resizing behavior of the animation.
+ *
+ * @return This crossfade object.
+ * @see #setResizeBehavior(int)
+ */
+ public int getResizeBehavior() {
+ return mResizeBehavior;
+ }
+
+ @Override
+ public Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues,
+ TransitionValues endValues) {
+ if (startValues == null || endValues == null) {
+ return null;
+ }
+ final boolean useParentOverlay = mFadeBehavior != FADE_BEHAVIOR_REVEAL;
+ 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);
+ 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);
+ }
+ if (startDrawable != null && endDrawable != null && !startBitmap.sameAs(endBitmap)) {
+ ViewOverlay overlay = useParentOverlay ?
+ ((ViewGroup) view.getParent()).getOverlay() : view.getOverlay();
+ if (mFadeBehavior == FADE_BEHAVIOR_REVEAL) {
+ overlay.add(endDrawable);
+ }
+ overlay.add(startDrawable);
+ // 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
+ // to mimic the change in the size of the view itself between scenes.
+ ObjectAnimator anim;
+ if (mFadeBehavior == FADE_BEHAVIOR_OUT_IN) {
+ // Fade out completely halfway through the transition
+ anim = ObjectAnimator.ofInt(startDrawable, "alpha", 255, 0, 0);
+ } else {
+ anim = ObjectAnimator.ofInt(startDrawable, "alpha", 0);
+ }
+ anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ // TODO: some way to auto-invalidate views based on drawable changes? callbacks?
+ view.invalidate(startDrawable.getBounds());
+ }
+ });
+ ObjectAnimator anim1 = null;
+ if (mFadeBehavior == FADE_BEHAVIOR_OUT_IN) {
+ // start fading in halfway through the transition
+ anim1 = ObjectAnimator.ofFloat(view, View.ALPHA, 0, 0, 1);
+ } else if (mFadeBehavior == FADE_BEHAVIOR_CROSSFADE) {
+ anim1 = ObjectAnimator.ofFloat(view, View.ALPHA, 0, 1);
+ }
+ if (Transition.DBG) {
+ Log.d(LOG_TAG, "Crossfade: created anim " + anim + " for start, end values " +
+ startValues + ", " + endValues);
+ }
+ anim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ ViewOverlay overlay = useParentOverlay ?
+ ((ViewGroup) view.getParent()).getOverlay() : view.getOverlay();
+ overlay.remove(startDrawable);
+ if (mFadeBehavior == FADE_BEHAVIOR_REVEAL) {
+ overlay.remove(endDrawable);
+ }
+ }
+ });
+ AnimatorSet set = new AnimatorSet();
+ set.playTogether(anim);
+ if (anim1 != null) {
+ set.playTogether(anim1);
+ }
+ if (mResizeBehavior == RESIZE_BEHAVIOR_SCALE && !startBounds.equals(endBounds)) {
+ if (Transition.DBG) {
+ Log.d(LOG_TAG, "animating from startBounds to endBounds: " +
+ startBounds + ", " + endBounds);
+ }
+ Animator anim2 = ObjectAnimator.ofObject(startDrawable, "bounds",
+ sRectEvaluator, startBounds, endBounds);
+ set.playTogether(anim2);
+ if (mResizeBehavior == RESIZE_BEHAVIOR_SCALE) {
+ // TODO: How to handle resizing with a CROSSFADE (vs. REVEAL) effect
+ // when we are animating the view directly?
+ Animator anim3 = ObjectAnimator.ofObject(endDrawable, "bounds",
+ sRectEvaluator, startBounds, endBounds);
+ set.playTogether(anim3);
+ }
+ }
+ return set;
+ } else {
+ return null;
+ }
+ }
+
+ private void captureValues(TransitionValues transitionValues) {
+ View view = transitionValues.view;
+ Rect bounds = new Rect(0, 0, view.getWidth(), view.getHeight());
+ if (mFadeBehavior != FADE_BEHAVIOR_REVEAL) {
+ bounds.offset(view.getLeft(), view.getTop());
+ }
+ transitionValues.values.put(PROPNAME_BOUNDS, bounds);
+
+ if (Transition.DBG) {
+ Log.d(LOG_TAG, "Captured bounds " + transitionValues.values.get(PROPNAME_BOUNDS));
+ }
+ Bitmap bitmap = Bitmap.createBitmap(view.getWidth(), view.getHeight(),
+ Bitmap.Config.ARGB_8888);
+ if (view instanceof TextureView) {
+ bitmap = ((TextureView) view).getBitmap();
+ } else {
+ Canvas c = new Canvas(bitmap);
+ view.draw(c);
+ }
+ transitionValues.values.put(PROPNAME_BITMAP, bitmap);
+ // TODO: I don't have resources, can't call the non-deprecated method?
+ BitmapDrawable drawable = new BitmapDrawable(bitmap);
+ // TODO: lrtb will be wrong if the view has transXY set
+ drawable.setBounds(bounds);
+ transitionValues.values.put(PROPNAME_DRAWABLE, drawable);
+ }
+
+ @Override
+ public void captureStartValues(TransitionValues transitionValues) {
+ captureValues(transitionValues);
+ }
+
+ @Override
+ public void captureEndValues(TransitionValues transitionValues) {
+ captureValues(transitionValues);
+ }
+}