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