1 /* 2 * Copyright (C) 2013 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.transition; 18 19 import com.android.internal.R; 20 21 import android.animation.Animator; 22 import android.animation.AnimatorListenerAdapter; 23 import android.animation.ObjectAnimator; 24 import android.content.Context; 25 import android.content.res.TypedArray; 26 import android.util.AttributeSet; 27 import android.util.Log; 28 import android.view.View; 29 import android.view.ViewGroup; 30 31 /** 32 * This transition tracks changes to the visibility of target views in the 33 * start and end scenes and fades views in or out when they become visible 34 * or non-visible. Visibility is determined by both the 35 * {@link View#setVisibility(int)} state of the view as well as whether it 36 * is parented in the current view hierarchy. 37 * 38 * <p>The ability of this transition to fade out a particular view, and the 39 * way that that fading operation takes place, is based on 40 * the situation of the view in the view hierarchy. For example, if a view was 41 * simply removed from its parent, then the view will be added into a {@link 42 * android.view.ViewGroupOverlay} while fading. If a visible view is 43 * changed to be {@link View#GONE} or {@link View#INVISIBLE}, then the 44 * visibility will be changed to {@link View#VISIBLE} for the duration of 45 * the animation. However, if a view is in a hierarchy which is also altering 46 * its visibility, the situation can be more complicated. In general, if a 47 * view that is no longer in the hierarchy in the end scene still has a 48 * parent (so its parent hierarchy was removed, but it was not removed from 49 * its parent), then it will be left alone to avoid side-effects from 50 * improperly removing it from its parent. The only exception to this is if 51 * the previous {@link Scene} was 52 * {@link Scene#getSceneForLayout(android.view.ViewGroup, int, android.content.Context) 53 * created from a layout resource file}, then it is considered safe to un-parent 54 * the starting scene view in order to fade it out.</p> 55 * 56 * <p>A Fade transition can be described in a resource file by using the 57 * tag <code>fade</code>, along with the standard 58 * attributes of {@link android.R.styleable#Fade} and 59 * {@link android.R.styleable#Transition}.</p> 60 61 */ 62 public class Fade extends Visibility { 63 64 private static boolean DBG = Transition.DBG && false; 65 66 private static final String LOG_TAG = "Fade"; 67 68 /** 69 * Fading mode used in {@link #Fade(int)} to make the transition 70 * operate on targets that are appearing. Maybe be combined with 71 * {@link #OUT} to fade both in and out. Equivalent to 72 * {@link Visibility#MODE_IN}. 73 */ 74 public static final int IN = Visibility.MODE_IN; 75 76 /** 77 * Fading mode used in {@link #Fade(int)} to make the transition 78 * operate on targets that are disappearing. Maybe be combined with 79 * {@link #IN} to fade both in and out. Equivalent to 80 * {@link Visibility#MODE_OUT}. 81 */ 82 public static final int OUT = Visibility.MODE_OUT; 83 84 /** 85 * Constructs a Fade transition that will fade targets in and out. 86 */ Fade()87 public Fade() { 88 } 89 90 /** 91 * Constructs a Fade transition that will fade targets in 92 * and/or out, according to the value of fadingMode. 93 * 94 * @param fadingMode The behavior of this transition, a combination of 95 * {@link #IN} and {@link #OUT}. 96 */ Fade(int fadingMode)97 public Fade(int fadingMode) { 98 setMode(fadingMode); 99 } 100 Fade(Context context, AttributeSet attrs)101 public Fade(Context context, AttributeSet attrs) { 102 super(context, attrs); 103 TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.Fade); 104 int fadingMode = a.getInt(R.styleable.Fade_fadingMode, getMode()); 105 setMode(fadingMode); 106 } 107 108 /** 109 * Utility method to handle creating and running the Animator. 110 */ createAnimation(View view, float startAlpha, float endAlpha)111 private Animator createAnimation(View view, float startAlpha, float endAlpha) { 112 if (startAlpha == endAlpha) { 113 return null; 114 } 115 view.setTransitionAlpha(startAlpha); 116 final ObjectAnimator anim = ObjectAnimator.ofFloat(view, "transitionAlpha", endAlpha); 117 if (DBG) { 118 Log.d(LOG_TAG, "Created animator " + anim); 119 } 120 FadeAnimatorListener listener = new FadeAnimatorListener(view); 121 anim.addListener(listener); 122 anim.addPauseListener(listener); 123 return anim; 124 } 125 126 @Override onAppear(ViewGroup sceneRoot, View view, TransitionValues startValues, TransitionValues endValues)127 public Animator onAppear(ViewGroup sceneRoot, View view, 128 TransitionValues startValues, 129 TransitionValues endValues) { 130 if (DBG) { 131 View startView = (startValues != null) ? startValues.view : null; 132 Log.d(LOG_TAG, "Fade.onAppear: startView, startVis, endView, endVis = " + 133 startView + ", " + view); 134 } 135 return createAnimation(view, 0, 1); 136 } 137 138 @Override onDisappear(ViewGroup sceneRoot, final View view, TransitionValues startValues, TransitionValues endValues)139 public Animator onDisappear(ViewGroup sceneRoot, final View view, TransitionValues startValues, 140 TransitionValues endValues) { 141 return createAnimation(view, 1, 0); 142 } 143 144 private static class FadeAnimatorListener extends AnimatorListenerAdapter { 145 private final View mView; 146 private boolean mCanceled = false; 147 private float mPausedAlpha = -1; 148 private boolean mLayerTypeChanged = false; 149 FadeAnimatorListener(View view)150 public FadeAnimatorListener(View view) { 151 mView = view; 152 } 153 154 @Override onAnimationStart(Animator animator)155 public void onAnimationStart(Animator animator) { 156 if (mView.hasOverlappingRendering() && mView.getLayerType() == View.LAYER_TYPE_NONE) { 157 mLayerTypeChanged = true; 158 mView.setLayerType(View.LAYER_TYPE_HARDWARE, null); 159 } 160 } 161 162 @Override onAnimationCancel(Animator animator)163 public void onAnimationCancel(Animator animator) { 164 mCanceled = true; 165 if (mPausedAlpha >= 0) { 166 mView.setTransitionAlpha(mPausedAlpha); 167 } 168 } 169 170 @Override onAnimationEnd(Animator animator)171 public void onAnimationEnd(Animator animator) { 172 if (!mCanceled) { 173 mView.setTransitionAlpha(1); 174 } 175 if (mLayerTypeChanged) { 176 mView.setLayerType(View.LAYER_TYPE_NONE, null); 177 } 178 } 179 180 @Override onAnimationPause(Animator animator)181 public void onAnimationPause(Animator animator) { 182 mPausedAlpha = mView.getTransitionAlpha(); 183 mView.setTransitionAlpha(1); 184 } 185 186 @Override onAnimationResume(Animator animator)187 public void onAnimationResume(Animator animator) { 188 mView.setTransitionAlpha(mPausedAlpha); 189 } 190 } 191 } 192