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 android.animation.Animator; 20 import android.animation.AnimatorListenerAdapter; 21 import android.animation.ObjectAnimator; 22 import android.content.Context; 23 import android.content.res.TypedArray; 24 import android.util.AttributeSet; 25 import android.util.Log; 26 import android.view.View; 27 import android.view.ViewGroup; 28 29 import com.android.internal.R; 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 public class Fade extends Visibility { 62 static final String PROPNAME_TRANSITION_ALPHA = "android:fade:transitionAlpha"; 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(@isibilityMode int fadingMode)97 public Fade(@VisibilityMode 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 a.recycle(); 107 } 108 109 @Override captureStartValues(TransitionValues transitionValues)110 public void captureStartValues(TransitionValues transitionValues) { 111 super.captureStartValues(transitionValues); 112 transitionValues.values.put(PROPNAME_TRANSITION_ALPHA, 113 transitionValues.view.getTransitionAlpha()); 114 } 115 116 /** 117 * Utility method to handle creating and running the Animator. 118 */ createAnimation(final View view, float startAlpha, final float endAlpha)119 private Animator createAnimation(final View view, float startAlpha, final float endAlpha) { 120 if (startAlpha == endAlpha) { 121 return null; 122 } 123 view.setTransitionAlpha(startAlpha); 124 final ObjectAnimator anim = ObjectAnimator.ofFloat(view, "transitionAlpha", endAlpha); 125 if (DBG) { 126 Log.d(LOG_TAG, "Created animator " + anim); 127 } 128 final FadeAnimatorListener listener = new FadeAnimatorListener(view); 129 anim.addListener(listener); 130 addListener(new TransitionListenerAdapter() { 131 @Override 132 public void onTransitionEnd(Transition transition) { 133 view.setTransitionAlpha(1); 134 transition.removeListener(this); 135 } 136 }); 137 return anim; 138 } 139 140 @Override onAppear(ViewGroup sceneRoot, View view, TransitionValues startValues, TransitionValues endValues)141 public Animator onAppear(ViewGroup sceneRoot, View view, 142 TransitionValues startValues, 143 TransitionValues endValues) { 144 if (DBG) { 145 View startView = (startValues != null) ? startValues.view : null; 146 Log.d(LOG_TAG, "Fade.onAppear: startView, startVis, endView, endVis = " + 147 startView + ", " + view); 148 } 149 float startAlpha = getStartAlpha(startValues, 0); 150 if (startAlpha == 1) { 151 startAlpha = 0; 152 } 153 return createAnimation(view, startAlpha, 1); 154 } 155 156 @Override onDisappear(ViewGroup sceneRoot, final View view, TransitionValues startValues, TransitionValues endValues)157 public Animator onDisappear(ViewGroup sceneRoot, final View view, TransitionValues startValues, 158 TransitionValues endValues) { 159 float startAlpha = getStartAlpha(startValues, 1); 160 return createAnimation(view, startAlpha, 0); 161 } 162 getStartAlpha(TransitionValues startValues, float fallbackValue)163 private static float getStartAlpha(TransitionValues startValues, float fallbackValue) { 164 float startAlpha = fallbackValue; 165 if (startValues != null) { 166 Float startAlphaFloat = (Float) startValues.values.get(PROPNAME_TRANSITION_ALPHA); 167 if (startAlphaFloat != null) { 168 startAlpha = startAlphaFloat; 169 } 170 } 171 return startAlpha; 172 } 173 174 private static class FadeAnimatorListener extends AnimatorListenerAdapter { 175 private final View mView; 176 private boolean mLayerTypeChanged = false; 177 FadeAnimatorListener(View view)178 public FadeAnimatorListener(View view) { 179 mView = view; 180 } 181 182 @Override onAnimationStart(Animator animator)183 public void onAnimationStart(Animator animator) { 184 if (mView.hasOverlappingRendering() && mView.getLayerType() == View.LAYER_TYPE_NONE) { 185 mLayerTypeChanged = true; 186 mView.setLayerType(View.LAYER_TYPE_HARDWARE, null); 187 } 188 } 189 190 @Override onAnimationEnd(Animator animator)191 public void onAnimationEnd(Animator animator) { 192 mView.setTransitionAlpha(1); 193 if (mLayerTypeChanged) { 194 mView.setLayerType(View.LAYER_TYPE_NONE, null); 195 } 196 } 197 } 198 } 199