From c46181a963be736186ae29101625a05b5c1f0ba8 Mon Sep 17 00:00:00 2001 From: Chet Haase Date: Mon, 16 Sep 2013 13:56:21 -0700 Subject: Use transition-only alpha property for fading transitions The original bug is fixed already, but showed up some problems in the underlying fade-transition implementation. This fix addresses those and other issues. The biggest part of the change should help transition robustness in general, as it removes the dependency on the public 'alpha' property of views and uses, instead, a new hidden property on views called 'transitionAlpha'. This is a value which is normally opaque (1), but which can be used by transitions (only) to animate the translucency of views without disturbing the actual 'alpha' value which might be manipulated outside of transitions. This should make transitions much more robust in general. In implementing and testing this overall fix, I noticed a couple of things about transitions that were simply wrong (such as starting fades from the wrong start value, and incorrectly avoiding transitions on some views that didn't happen to have ids), and those are fixed in this CL as well. Issue #10726905 ActionBar weirdness in People app Issue #10727937 Menu items in gallery appear in faded color after selecting an image/album by long press Change-Id: If1618446db10c1bfcff4761449241de4f559afc1 --- core/java/android/transition/Fade.java | 100 +++++++++--------------- core/java/android/transition/Transition.java | 2 +- core/java/android/transition/TransitionSet.java | 2 +- core/java/android/view/View.java | 69 ++++++++++++++-- 4 files changed, 99 insertions(+), 74 deletions(-) diff --git a/core/java/android/transition/Fade.java b/core/java/android/transition/Fade.java index 4cc2c4280d74..5f948bd1e5df 100644 --- a/core/java/android/transition/Fade.java +++ b/core/java/android/transition/Fade.java @@ -41,7 +41,6 @@ public class Fade extends Visibility { private static boolean DBG = Transition.DBG && false; private static final String LOG_TAG = "Fade"; - private static final String PROPNAME_ALPHA = "android:fade:alpha"; private static final String PROPNAME_SCREEN_X = "android:fade:screenX"; private static final String PROPNAME_SCREEN_Y = "android:fade:screenY"; @@ -90,7 +89,8 @@ public class Fade extends Visibility { } return null; } - final ObjectAnimator anim = ObjectAnimator.ofFloat(view, "alpha", startAlpha, endAlpha); + final ObjectAnimator anim = ObjectAnimator.ofFloat(view, "transitionAlpha", startAlpha, + endAlpha); if (DBG) { Log.d(LOG_TAG, "Created animator " + anim); } @@ -102,8 +102,6 @@ public class Fade extends Visibility { } private void captureValues(TransitionValues transitionValues) { - float alpha = transitionValues.view.getAlpha(); - transitionValues.values.put(PROPNAME_ALPHA, alpha); int[] loc = new int[2]; transitionValues.view.getLocationOnScreen(loc); transitionValues.values.put(PROPNAME_SCREEN_X, loc[0]); @@ -116,29 +114,6 @@ public class Fade extends Visibility { captureValues(transitionValues); } - - @Override - public void captureEndValues(TransitionValues transitionValues) { - super.captureEndValues(transitionValues); - } - - @Override - public Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues, - TransitionValues endValues) { - Animator animator = super.createAnimator(sceneRoot, startValues, endValues); - if (animator == null && startValues != null && endValues != null) { - boolean endVisible = isVisible(endValues); - final View endView = endValues.view; - float endAlpha = endView.getAlpha(); - float startAlpha = (Float) startValues.values.get(PROPNAME_ALPHA); - if ((endVisible && startAlpha < endAlpha && (mFadingMode & Fade.IN) != 0) || - (!endVisible && startAlpha > endAlpha && (mFadingMode & Fade.OUT) != 0)) { - animator = createAnimation(endView, startAlpha, endAlpha, null); - } - } - return animator; - } - @Override public Animator onAppear(ViewGroup sceneRoot, TransitionValues startValues, int startVisibility, @@ -152,40 +127,37 @@ public class Fade extends Visibility { Log.d(LOG_TAG, "Fade.onAppear: startView, startVis, endView, endVis = " + startView + ", " + startVisibility + ", " + endView + ", " + endVisibility); } - // if alpha < 1, just fade it in from the current value - if (endView.getAlpha() == 1.0f) { - endView.setAlpha(0); - TransitionListener transitionListener = new TransitionListenerAdapter() { - boolean mCanceled = false; - float mPausedAlpha; + endView.setTransitionAlpha(0); + TransitionListener transitionListener = new TransitionListenerAdapter() { + boolean mCanceled = false; + float mPausedAlpha; - @Override - public void onTransitionCancel(Transition transition) { - endView.setAlpha(1); - mCanceled = true; - } + @Override + public void onTransitionCancel(Transition transition) { + endView.setTransitionAlpha(1); + mCanceled = true; + } - @Override - public void onTransitionEnd(Transition transition) { - if (!mCanceled) { - endView.setAlpha(1); - } + @Override + public void onTransitionEnd(Transition transition) { + if (!mCanceled) { + endView.setTransitionAlpha(1); } + } - @Override - public void onTransitionPause(Transition transition) { - mPausedAlpha = endView.getAlpha(); - endView.setAlpha(1); - } + @Override + public void onTransitionPause(Transition transition) { + mPausedAlpha = endView.getTransitionAlpha(); + endView.setTransitionAlpha(1); + } - @Override - public void onTransitionResume(Transition transition) { - endView.setAlpha(mPausedAlpha); - } - }; - addListener(transitionListener); - } - return createAnimation(endView, endView.getAlpha(), 1, null); + @Override + public void onTransitionResume(Transition transition) { + endView.setTransitionAlpha(mPausedAlpha); + } + }; + addListener(transitionListener); + return createAnimation(endView, 0, 1, null); } @Override @@ -236,7 +208,7 @@ public class Fade extends Visibility { overlayView.offsetTopAndBottom((screenY - loc[1]) - overlayView.getTop()); sceneRoot.getOverlay().add(overlayView); // TODO: add automatic facility to Visibility superclass for keeping views around - final float startAlpha = view.getAlpha(); + final float startAlpha = 1; float endAlpha = 0; final View finalView = view; final View finalOverlayView = overlayView; @@ -245,7 +217,7 @@ public class Fade extends Visibility { final AnimatorListenerAdapter endListener = new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { - finalView.setAlpha(startAlpha); + finalView.setTransitionAlpha(startAlpha); // TODO: restore view offset from overlay repositioning if (finalViewToKeep != null) { finalViewToKeep.setVisibility(finalVisibility); @@ -276,7 +248,7 @@ public class Fade extends Visibility { // 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(); + final float startAlpha = 1; float endAlpha = 0; final View finalView = view; final View finalOverlayView = overlayView; @@ -291,8 +263,8 @@ public class Fade extends Visibility { if (finalViewToKeep != null && !mCanceled) { finalViewToKeep.setVisibility(finalVisibility); } - mPausedAlpha = finalView.getAlpha(); - finalView.setAlpha(startAlpha); + mPausedAlpha = finalView.getTransitionAlpha(); + finalView.setTransitionAlpha(startAlpha); } @Override @@ -300,21 +272,21 @@ public class Fade extends Visibility { if (finalViewToKeep != null && !mCanceled) { finalViewToKeep.setVisibility(View.VISIBLE); } - finalView.setAlpha(mPausedAlpha); + finalView.setTransitionAlpha(mPausedAlpha); } @Override public void onAnimationCancel(Animator animation) { mCanceled = true; if (mPausedAlpha >= 0) { - finalView.setAlpha(mPausedAlpha); + finalView.setTransitionAlpha(mPausedAlpha); } } @Override public void onAnimationEnd(Animator animation) { if (!mCanceled) { - finalView.setAlpha(startAlpha); + finalView.setTransitionAlpha(startAlpha); } // TODO: restore view offset from overlay repositioning if (finalViewToKeep != null && !mCanceled) { diff --git a/core/java/android/transition/Transition.java b/core/java/android/transition/Transition.java index 60b47087fe44..4a991534d52e 100644 --- a/core/java/android/transition/Transition.java +++ b/core/java/android/transition/Transition.java @@ -353,7 +353,7 @@ public abstract class Transition implements Cloneable { if (endValues.viewValues.get(view) != null) { end = endValues.viewValues.get(view); endCopy.remove(view); - } else { + } else if (id != View.NO_ID) { end = endValues.idValues.get(id); View removeView = null; for (View viewToRemove : endCopy.keySet()) { diff --git a/core/java/android/transition/TransitionSet.java b/core/java/android/transition/TransitionSet.java index f72b36e3e243..6fdd3093161f 100644 --- a/core/java/android/transition/TransitionSet.java +++ b/core/java/android/transition/TransitionSet.java @@ -354,7 +354,7 @@ public class TransitionSet extends Transition { clone.mTransitions = new ArrayList(); int numTransitions = mTransitions.size(); for (int i = 0; i < numTransitions; ++i) { - clone.mTransitions.add((Transition) mTransitions.get(i).clone()); + clone.addTransition((Transition) mTransitions.get(i).clone()); } return clone; } diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index a5db6eee79ac..4680e769f050 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -2894,6 +2894,13 @@ public class View implements Drawable.Callback, KeyEvent.Callback, */ @ViewDebug.ExportedProperty float mAlpha = 1f; + + /** + * The opacity of the view as manipulated by the Fade transition. This is a hidden + * property only used by transitions, which is composited with the other alpha + * values to calculate the final visual alpha value. + */ + float mTransitionAlpha = 1f; } TransformationInfo mTransformationInfo; @@ -5335,7 +5342,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, View view = (View) current; // We have attach info so this view is attached and there is no // need to check whether we reach to ViewRootImpl on the way up. - if (view.getAlpha() <= 0 || view.getVisibility() != VISIBLE) { + if (view.getAlpha() <= 0 || view.getTransitionAlpha() <= 0 || + view.getVisibility() != VISIBLE) { return false; } current = view.mParent; @@ -9786,7 +9794,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, mPrivateFlags &= ~PFLAG_ALPHA_SET; invalidateViewProperty(true, false); if (mDisplayList != null) { - mDisplayList.setAlpha(alpha); + mDisplayList.setAlpha(getFinalAlpha()); } } } @@ -9813,13 +9821,58 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } else { mPrivateFlags &= ~PFLAG_ALPHA_SET; if (mDisplayList != null) { - mDisplayList.setAlpha(alpha); + mDisplayList.setAlpha(getFinalAlpha()); } } } return false; } + /** + * This property is hidden and intended only for use by the Fade transition, which + * animates it to produce a visual translucency that does not side-effect (or get + * affected by) the real alpha property. This value is composited with the other + * alpha value (and the AlphaAnimation value, when that is present) to produce + * a final visual translucency result, which is what is passed into the DisplayList. + * + * @hide + */ + public void setTransitionAlpha(float alpha) { + ensureTransformationInfo(); + if (mTransformationInfo.mTransitionAlpha != alpha) { + mTransformationInfo.mTransitionAlpha = alpha; + mPrivateFlags &= ~PFLAG_ALPHA_SET; + invalidateViewProperty(true, false); + if (mDisplayList != null) { + mDisplayList.setAlpha(getFinalAlpha()); + } + } + } + + /** + * Calculates the visual alpha of this view, which is a combination of the actual + * alpha value and the transitionAlpha value (if set). + */ + private float getFinalAlpha() { + if (mTransformationInfo != null) { + return mTransformationInfo.mAlpha * mTransformationInfo.mTransitionAlpha; + } + return 1; + } + + /** + * This property is hidden and intended only for use by the Fade transition, which + * animates it to produce a visual translucency that does not side-effect (or get + * affected by) the real alpha property. This value is composited with the other + * alpha value (and the AlphaAnimation value, when that is present) to produce + * a final visual translucency result, which is what is passed into the DisplayList. + * + * @hide + */ + public float getTransitionAlpha() { + return mTransformationInfo != null ? mTransformationInfo.mTransitionAlpha : 1; + } + /** * Top position of this view relative to its parent. * @@ -10913,7 +10966,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, @ViewDebug.ExportedProperty(category = "drawing") public boolean isOpaque() { return (mPrivateFlags & PFLAG_OPAQUE_MASK) == PFLAG_OPAQUE_MASK && - ((mTransformationInfo != null ? mTransformationInfo.mAlpha : 1.0f) >= 1.0f); + getFinalAlpha() >= 1.0f; } /** @@ -13868,7 +13921,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } } if (mTransformationInfo != null) { - alpha *= mTransformationInfo.mAlpha; + alpha *= getFinalAlpha(); if (alpha < 1) { final int multipliedAlpha = (int) (255 * alpha); if (onSetAlpha(multipliedAlpha)) { @@ -14057,8 +14110,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } } - float alpha = useDisplayListProperties ? 1 : getAlpha(); - if (transformToApply != null || alpha < 1 || !hasIdentityMatrix() || + float alpha = useDisplayListProperties ? 1 : (getAlpha() * getTransitionAlpha()); + if (transformToApply != null || alpha < 1 || !hasIdentityMatrix() || (mPrivateFlags3 & PFLAG3_VIEW_IS_ANIMATING_ALPHA) == PFLAG3_VIEW_IS_ANIMATING_ALPHA) { if (transformToApply != null || !childHasIdentityMatrix) { int transX = 0; @@ -14115,7 +14168,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, layerFlags |= Canvas.CLIP_TO_LAYER_SAVE_FLAG; } if (useDisplayListProperties) { - displayList.setAlpha(alpha * getAlpha()); + displayList.setAlpha(alpha * getAlpha() * getTransitionAlpha()); } else if (layerType == LAYER_TYPE_NONE) { final int scrollX = hasDisplayList ? 0 : sx; final int scrollY = hasDisplayList ? 0 : sy; -- cgit v1.2.3-59-g8ed1b