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 17package android.transition; 18 19import com.android.internal.R; 20 21import android.animation.Animator; 22import android.animation.AnimatorListenerAdapter; 23import android.animation.ObjectAnimator; 24import android.content.Context; 25import android.content.res.TypedArray; 26import android.util.AttributeSet; 27import android.util.Log; 28import android.view.View; 29import 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 */ 62public 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 */ 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 /** 109 * Utility method to handle creating and running the Animator. 110 */ 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 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 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 150 public FadeAnimatorListener(View view) { 151 mView = view; 152 } 153 154 @Override 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 163 public void onAnimationCancel(Animator animator) { 164 mCanceled = true; 165 if (mPausedAlpha >= 0) { 166 mView.setTransitionAlpha(mPausedAlpha); 167 } 168 } 169 170 @Override 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 181 public void onAnimationPause(Animator animator) { 182 mPausedAlpha = mView.getTransitionAlpha(); 183 mView.setTransitionAlpha(1); 184 } 185 186 @Override 187 public void onAnimationResume(Animator animator) { 188 mView.setTransitionAlpha(mPausedAlpha); 189 } 190 } 191} 192