diff --git a/core/java/android/transition/TextChange.java b/core/java/android/transition/ChangeText.java similarity index 84% rename from core/java/android/transition/TextChange.java rename to core/java/android/transition/ChangeText.java index cf190a1cd66e1..b1be70f32eed0 100644 --- a/core/java/android/transition/TextChange.java +++ b/core/java/android/transition/ChangeText.java @@ -37,7 +37,7 @@ import java.util.Map; * * @hide */ -public class TextChange extends Transition { +public class ChangeText extends Transition { private static final String LOG_TAG = "TextChange"; @@ -103,7 +103,7 @@ public class TextChange extends Transition { * transition is run. * @return this textChange object. */ - public TextChange setChangeBehavior(int changeBehavior) { + public ChangeText setChangeBehavior(int changeBehavior) { if (changeBehavior >= CHANGE_BEHAVIOR_KEEP && changeBehavior <= CHANGE_BEHAVIOR_OUT_IN) { mChangeBehavior = changeBehavior; } @@ -179,9 +179,13 @@ public class TextChange extends Transition { startSelectionStart = startSelectionEnd = endSelectionStart = endSelectionEnd = -1; } if (!startText.equals(endText)) { - view.setText(startText); - if (view instanceof EditText) { - setSelection(((EditText) view), startSelectionStart, startSelectionEnd); + final int startColor = (Integer) startVals.get(PROPNAME_TEXT_COLOR); + final int endColor = (Integer) endVals.get(PROPNAME_TEXT_COLOR); + if (mChangeBehavior != CHANGE_BEHAVIOR_IN) { + view.setText(startText); + if (view instanceof EditText) { + setSelection(((EditText) view), startSelectionStart, startSelectionEnd); + } } Animator anim; if (mChangeBehavior == CHANGE_BEHAVIOR_KEEP) { @@ -200,8 +204,6 @@ public class TextChange extends Transition { }); } else { // Fade out start text - final int startColor = (Integer) startVals.get(PROPNAME_TEXT_COLOR); - final int endColor = (Integer) endVals.get(PROPNAME_TEXT_COLOR); ValueAnimator outAnim = null, inAnim = null; if (mChangeBehavior == CHANGE_BEHAVIOR_OUT_IN || mChangeBehavior == CHANGE_BEHAVIOR_OUT) { @@ -210,8 +212,8 @@ public class TextChange extends Transition { @Override public void onAnimationUpdate(ValueAnimator animation) { int currAlpha = (Integer) animation.getAnimatedValue(); - view.setTextColor(currAlpha << 24 | Color.red(startColor) << 16 | - Color.green(startColor) << 8 | Color.red(startColor)); + view.setTextColor(currAlpha << 24 | startColor & 0xff0000 | + startColor & 0xff00 | startColor & 0xff); } }); outAnim.addListener(new AnimatorListenerAdapter() { @@ -225,6 +227,8 @@ public class TextChange extends Transition { endSelectionEnd); } } + // restore opaque alpha and correct end color + view.setTextColor(endColor); } }); } @@ -239,6 +243,13 @@ public class TextChange extends Transition { Color.green(endColor) << 8 | Color.red(endColor)); } }); + inAnim.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationCancel(Animator animation) { + // restore opaque alpha and correct end color + view.setTextColor(endColor); + } + }); } if (outAnim != null && inAnim != null) { anim = new AnimatorSet(); @@ -251,21 +262,32 @@ public class TextChange extends Transition { } } TransitionListener transitionListener = new TransitionListenerAdapter() { - boolean mCanceled = false; + int mPausedColor = 0; @Override public void onTransitionPause(Transition transition) { - view.setText(endText); - if (view instanceof EditText) { - setSelection(((EditText) view), endSelectionStart, endSelectionEnd); + if (mChangeBehavior != CHANGE_BEHAVIOR_IN) { + view.setText(endText); + if (view instanceof EditText) { + setSelection(((EditText) view), endSelectionStart, endSelectionEnd); + } + } + if (mChangeBehavior > CHANGE_BEHAVIOR_KEEP) { + mPausedColor = view.getCurrentTextColor(); + view.setTextColor(endColor); } } @Override public void onTransitionResume(Transition transition) { - view.setText(startText); - if (view instanceof EditText) { - setSelection(((EditText) view), startSelectionStart, startSelectionEnd); + if (mChangeBehavior != CHANGE_BEHAVIOR_IN) { + view.setText(startText); + if (view instanceof EditText) { + setSelection(((EditText) view), startSelectionStart, startSelectionEnd); + } + } + if (mChangeBehavior > CHANGE_BEHAVIOR_KEEP) { + view.setTextColor(mPausedColor); } } }; diff --git a/core/java/android/transition/Fade.java b/core/java/android/transition/Fade.java index 5f948bd1e5df7..8edb1ff744057 100644 --- a/core/java/android/transition/Fade.java +++ b/core/java/android/transition/Fade.java @@ -30,6 +30,24 @@ import android.view.ViewGroup; * {@link View#setVisibility(int)} state of the view as well as whether it * is parented in the current view hierarchy. * + *
The ability of this transition to fade out a particular view, and the + * way that that fading operation takes place, is based on + * the situation of the view in the view hierarchy. For example, if a view was + * simply removed from its parent, then the view will be added into a {@link + * android.view.ViewGroupOverlay} while fading. If a visible view is + * changed to be {@link View#GONE} or {@link View#INVISIBLE}, then the + * visibility will be changed to {@link View#VISIBLE} for the duration of + * the animation. However, if a view is in a hierarchy which is also altering + * its visibility, the situation can be more complicated. In general, if a + * view that is no longer in the hierarchy in the end scene still has a + * parent (so its parent hierarchy was removed, but it was not removed from + * its parent), then it will be left alone to avoid side-effects from + * improperly removing it from its parent. The only exception to this is if + * the previous {@link Scene} was + * {@link Scene#getSceneForLayout(android.view.ViewGroup, int, android.content.Context) + * created from a layout resource file}, then it is considered safe to un-parent + * the starting scene view in order to fade it out.
+ * *A Fade transition can be described in a resource file by using the
* tag Note that a view's visibility change is determined by both whether the view
- * itself is changing and whether its parent hierarchy's visibility is changing.
- * That is, a view that appears in the end scene will only trigger a call to
- * {@link #onAppear(android.view.ViewGroup, TransitionValues, int, TransitionValues, int)
- * appear()} if its parent hierarchy was stable between the start and end scenes.
- * This is done to avoid causing a visibility transition on every node in a hierarchy
- * when only the top-most node is the one that should be transitioned in/out.
- * Stability is determined by either the parent hierarchy views being the same
- * between scenes or, if scenes are inflated from layout resource files and thus
- * have result in different view instances, if the views represented by
- * the ids of those parents are stable. This means that visibility determination
- * is more effective with inflated view hierarchies if ids are used.
- * The exception to this is when the visibility subclass transition is
- * targeted at specific views, in which case the visibility of parent views
- * is ignored.fade, along with the standard
* attributes of {@link android.R.styleable#Fade} and
@@ -167,7 +185,7 @@ public class Fade extends Visibility {
if ((mFadingMode & OUT) != OUT) {
return null;
}
- View view;
+ View view = null;
View startView = (startValues != null) ? startValues.view : null;
View endView = (endValues != null) ? endValues.view : null;
if (DBG) {
@@ -177,9 +195,28 @@ public class Fade extends Visibility {
View overlayView = null;
View viewToKeep = null;
if (endView == null || endView.getParent() == null) {
- // view was removed: add the start view to the Overlay
- view = startView;
- overlayView = view;
+ if (endView != null) {
+ // endView was removed from its parent - add it to the overlay
+ view = overlayView = endView;
+ } else if (startView != null) {
+ // endView does not exist. Use startView only under certain
+ // conditions, because placing a view in an overlay necessitates
+ // it being removed from its current parent
+ if (startView.getParent() == null) {
+ // no parent - safe to use
+ view = overlayView = startView;
+ } else if (startView.getParent() instanceof View &&
+ startView.getParent().getParent() == null) {
+ View startParent = (View) startView.getParent();
+ int id = startParent.getId();
+ if (id != View.NO_ID && sceneRoot.findViewById(id) != null && mCanRemoveViews) {
+ // no parent, but its parent is unparented but the parent
+ // hierarchy has been replaced by a new hierarchy with the same id
+ // and it is safe to un-parent startView
+ view = overlayView = startView;
+ }
+ }
+ }
} else {
// visibility change
if (endVisibility == View.INVISIBLE) {
diff --git a/core/java/android/transition/Scene.java b/core/java/android/transition/Scene.java
index f81eeef153b5e..d798abec5a33b 100644
--- a/core/java/android/transition/Scene.java
+++ b/core/java/android/transition/Scene.java
@@ -157,11 +157,11 @@ public final class Scene {
public void enter() {
// Apply layout change, if any
- if (mLayoutId >= 0 || mLayout != null) {
+ if (mLayoutId > 0 || mLayout != null) {
// empty out parent container before adding to it
getSceneRoot().removeAllViews();
- if (mLayoutId >= 0) {
+ if (mLayoutId > 0) {
LayoutInflater.from(mContext).inflate(mLayoutId, mSceneRoot);
} else {
mSceneRoot.addView(mLayout);
@@ -242,4 +242,19 @@ public final class Scene {
mExitAction = action;
}
+
+ /**
+ * Returns whether this Scene was created by a layout resource file, determined
+ * by the layoutId passed into
+ * {@link #getSceneForLayout(android.view.ViewGroup, int, android.content.Context)}.
+ * This is called by TransitionManager to determine whether it is safe for views from
+ * this scene to be removed from their parents when the scene is exited, which is
+ * used by {@link Fade} to fade these views out (the views must be removed from
+ * their parent in order to add them to the overlay for fading purposes). If a
+ * Scene is not based on a resource file, then the impact of removing views
+ * arbitrarily is unknown and should be avoided.
+ */
+ boolean isCreatedFromLayoutResource() {
+ return (mLayoutId > 0);
+ }
}
\ No newline at end of file
diff --git a/core/java/android/transition/Transition.java b/core/java/android/transition/Transition.java
index a552fd4745833..dcf668b6c443d 100644
--- a/core/java/android/transition/Transition.java
+++ b/core/java/android/transition/Transition.java
@@ -118,6 +118,14 @@ public abstract class Transition implements Cloneable {
// Scene Root is set at createAnimator() time in the cloned Transition
ViewGroup mSceneRoot = null;
+ // Whether removing views from their parent is possible. This is only for views
+ // in the start scene, which are no longer in the view hierarchy. This property
+ // is determined by whether the previous Scene was created from a layout
+ // resource, and thus the views from the exited scene are going away anyway
+ // and can be removed as necessary to achieve a particular effect, such as
+ // removing them from parents to add them to overlays.
+ boolean mCanRemoveViews = false;
+
// Track all animators in use in case the transition gets canceled and needs to
// cancel running animators
private ArrayList