blob: 12e0d73db389318c9e12715b87719a3920b5059e [file] [log] [blame]
Chet Haasefaebd8f2012-05-18 14:17:57 -07001/*
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 */
Chet Haase6ebe3de2013-06-17 16:50:50 -070016
Chet Haased82c8ac2013-08-26 14:20:16 -070017package android.transition;
Chet Haasefaebd8f2012-05-18 14:17:57 -070018
19import android.animation.Animator;
20import android.animation.AnimatorListenerAdapter;
21import android.animation.ObjectAnimator;
22import android.util.Log;
23import android.view.View;
24import android.view.ViewGroup;
25
26/**
27 * This transition tracks changes to the visibility of target views in the
28 * start and end scenes and fades views in or out when they become visible
29 * or non-visible. Visibility is determined by both the
30 * {@link View#setVisibility(int)} state of the view as well as whether it
31 * is parented in the current view hierarchy.
Chet Haased82c8ac2013-08-26 14:20:16 -070032 *
33 * <p>A Fade transition can be described in a resource file by using the
34 * tag <code>fade</code>, along with the standard
35 * attributes of {@link android.R.styleable#Fade} and
36 * {@link android.R.styleable#Transition}.</p>
37
Chet Haasefaebd8f2012-05-18 14:17:57 -070038 */
39public class Fade extends Visibility {
40
Chet Haasec43524f2013-07-16 14:40:11 -070041 private static boolean DBG = Transition.DBG && false;
42
Chet Haasefaebd8f2012-05-18 14:17:57 -070043 private static final String LOG_TAG = "Fade";
Chet Haase199acdf2013-07-24 18:40:55 -070044 private static final String PROPNAME_ALPHA = "android:fade:alpha";
Chet Haase6ebe3de2013-06-17 16:50:50 -070045 private static final String PROPNAME_SCREEN_X = "android:fade:screenX";
46 private static final String PROPNAME_SCREEN_Y = "android:fade:screenY";
Chet Haasefaebd8f2012-05-18 14:17:57 -070047
48 /**
49 * Fading mode used in {@link #Fade(int)} to make the transition
50 * operate on targets that are appearing. Maybe be combined with
51 * {@link #OUT} to fade both in and out.
52 */
53 public static final int IN = 0x1;
54 /**
55 * Fading mode used in {@link #Fade(int)} to make the transition
56 * operate on targets that are disappearing. Maybe be combined with
57 * {@link #IN} to fade both in and out.
58 */
59 public static final int OUT = 0x2;
60
61 private int mFadingMode;
62
63 /**
64 * Constructs a Fade transition that will fade targets in and out.
65 */
66 public Fade() {
67 this(IN | OUT);
68 }
69
70 /**
71 * Constructs a Fade transition that will fade targets in
72 * and/or out, according to the value of fadingMode.
73 *
74 * @param fadingMode The behavior of this transition, a combination of
75 * {@link #IN} and {@link #OUT}.
76 */
77 public Fade(int fadingMode) {
78 mFadingMode = fadingMode;
79 }
80
81 /**
82 * Utility method to handle creating and running the Animator.
83 */
Chet Haase199acdf2013-07-24 18:40:55 -070084 private Animator createAnimation(View view, float startAlpha, float endAlpha,
85 AnimatorListenerAdapter listener) {
86 if (startAlpha == endAlpha) {
87 // run listener if we're noop'ing the animation, to get the end-state results now
88 if (listener != null) {
89 listener.onAnimationEnd(null);
90 }
91 return null;
92 }
Chet Haasefaebd8f2012-05-18 14:17:57 -070093 final ObjectAnimator anim = ObjectAnimator.ofFloat(view, "alpha", startAlpha, endAlpha);
94 if (listener != null) {
95 anim.addListener(listener);
Chet Haase199acdf2013-07-24 18:40:55 -070096 anim.addPauseListener(listener);
Chet Haasefaebd8f2012-05-18 14:17:57 -070097 }
Chet Haasefaebd8f2012-05-18 14:17:57 -070098 return anim;
99 }
100
Chet Haased82c8ac2013-08-26 14:20:16 -0700101 private void captureValues(TransitionValues transitionValues) {
102 float alpha = transitionValues.view.getAlpha();
103 transitionValues.values.put(PROPNAME_ALPHA, alpha);
Chet Haase6ebe3de2013-06-17 16:50:50 -0700104 int[] loc = new int[2];
Chet Haased82c8ac2013-08-26 14:20:16 -0700105 transitionValues.view.getLocationOnScreen(loc);
106 transitionValues.values.put(PROPNAME_SCREEN_X, loc[0]);
107 transitionValues.values.put(PROPNAME_SCREEN_Y, loc[1]);
Chet Haase6ebe3de2013-06-17 16:50:50 -0700108 }
109
110 @Override
Chet Haased82c8ac2013-08-26 14:20:16 -0700111 public void captureStartValues(TransitionValues transitionValues) {
112 super.captureStartValues(transitionValues);
113 captureValues(transitionValues);
114 }
115
116
117 @Override
118 public void captureEndValues(TransitionValues transitionValues) {
119 super.captureEndValues(transitionValues);
120 }
121
122 @Override
123 public Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues,
Chet Haase199acdf2013-07-24 18:40:55 -0700124 TransitionValues endValues) {
Chet Haased82c8ac2013-08-26 14:20:16 -0700125 Animator animator = super.createAnimator(sceneRoot, startValues, endValues);
Chet Haase199acdf2013-07-24 18:40:55 -0700126 if (animator == null && startValues != null && endValues != null) {
127 boolean endVisible = isVisible(endValues);
128 final View endView = endValues.view;
129 float endAlpha = endView.getAlpha();
130 float startAlpha = (Float) startValues.values.get(PROPNAME_ALPHA);
131 if ((endVisible && startAlpha < endAlpha && (mFadingMode & Fade.IN) != 0) ||
132 (!endVisible && startAlpha > endAlpha && (mFadingMode & Fade.OUT) != 0)) {
133 animator = createAnimation(endView, startAlpha, endAlpha, null);
134 }
135 }
136 return animator;
137 }
138
139 @Override
Chet Haased82c8ac2013-08-26 14:20:16 -0700140 public Animator onAppear(ViewGroup sceneRoot,
Chet Haase6ebe3de2013-06-17 16:50:50 -0700141 TransitionValues startValues, int startVisibility,
142 TransitionValues endValues, int endVisibility) {
Chet Haasedc57d9d2013-07-10 11:27:54 -0700143 if ((mFadingMode & IN) != IN || endValues == null) {
Chet Haasefaebd8f2012-05-18 14:17:57 -0700144 return null;
145 }
Chet Haasedc57d9d2013-07-10 11:27:54 -0700146 final View endView = endValues.view;
Chet Haased82c8ac2013-08-26 14:20:16 -0700147 if (DBG) {
148 View startView = (startValues != null) ? startValues.view : null;
149 Log.d(LOG_TAG, "Fade.onDisappear: startView, startVis, endView, endVis = " +
150 startView + ", " + startVisibility + ", " + endView + ", " + endVisibility);
151 }
Chet Haase199acdf2013-07-24 18:40:55 -0700152 // if alpha < 1, just fade it in from the current value
153 if (endView.getAlpha() == 1.0f) {
154 endView.setAlpha(0);
155 }
156 return createAnimation(endView, endView.getAlpha(), 1, null);
Chet Haasefaebd8f2012-05-18 14:17:57 -0700157 }
158
159 @Override
Chet Haased82c8ac2013-08-26 14:20:16 -0700160 public Animator onDisappear(ViewGroup sceneRoot,
Chet Haase6ebe3de2013-06-17 16:50:50 -0700161 TransitionValues startValues, int startVisibility,
162 TransitionValues endValues, int endVisibility) {
Chet Haasefaebd8f2012-05-18 14:17:57 -0700163 if ((mFadingMode & OUT) != OUT) {
Chet Haase2ea7f8b2013-06-21 15:00:05 -0700164 return null;
Chet Haasefaebd8f2012-05-18 14:17:57 -0700165 }
Chet Haase6ebe3de2013-06-17 16:50:50 -0700166 View view;
167 View startView = (startValues != null) ? startValues.view : null;
168 View endView = (endValues != null) ? endValues.view : null;
Chet Haasec43524f2013-07-16 14:40:11 -0700169 if (DBG) {
Chet Haased82c8ac2013-08-26 14:20:16 -0700170 Log.d(LOG_TAG, "Fade.onDisappear: startView, startVis, endView, endVis = " +
Chet Haasefaebd8f2012-05-18 14:17:57 -0700171 startView + ", " + startVisibility + ", " + endView + ", " + endVisibility);
172 }
Chet Haasefaebd8f2012-05-18 14:17:57 -0700173 View overlayView = null;
174 View viewToKeep = null;
Chet Haase199acdf2013-07-24 18:40:55 -0700175 if (endView == null || endView.getParent() == null) {
Chet Haasefaebd8f2012-05-18 14:17:57 -0700176 // view was removed: add the start view to the Overlay
177 view = startView;
178 overlayView = view;
179 } else {
180 // visibility change
181 if (endVisibility == View.INVISIBLE) {
182 view = endView;
183 viewToKeep = view;
184 } else {
185 // Becoming GONE
186 if (startView == endView) {
187 view = endView;
188 viewToKeep = view;
189 } else {
190 view = startView;
191 overlayView = view;
192 }
193 }
194 }
Chet Haase2ea7f8b2013-06-21 15:00:05 -0700195 final int finalVisibility = endVisibility;
Chet Haasefaebd8f2012-05-18 14:17:57 -0700196 // TODO: add automatic facility to Visibility superclass for keeping views around
197 if (overlayView != null) {
198 // TODO: Need to do this for general case of adding to overlay
Chet Haase6ebe3de2013-06-17 16:50:50 -0700199 int screenX = (Integer) startValues.values.get(PROPNAME_SCREEN_X);
200 int screenY = (Integer) startValues.values.get(PROPNAME_SCREEN_Y);
201 int[] loc = new int[2];
202 sceneRoot.getLocationOnScreen(loc);
203 overlayView.offsetLeftAndRight((screenX - loc[0]) - overlayView.getLeft());
204 overlayView.offsetTopAndBottom((screenY - loc[1]) - overlayView.getTop());
Chet Haasefaebd8f2012-05-18 14:17:57 -0700205 sceneRoot.getOverlay().add(overlayView);
Chet Haase2ea7f8b2013-06-21 15:00:05 -0700206 // TODO: add automatic facility to Visibility superclass for keeping views around
207 final float startAlpha = view.getAlpha();
208 float endAlpha = 0;
209 final View finalView = view;
210 final View finalOverlayView = overlayView;
211 final View finalViewToKeep = viewToKeep;
212 final ViewGroup finalSceneRoot = sceneRoot;
Chet Haase199acdf2013-07-24 18:40:55 -0700213 final AnimatorListenerAdapter endListener = new AnimatorListenerAdapter() {
Chet Haase2ea7f8b2013-06-21 15:00:05 -0700214 @Override
215 public void onAnimationEnd(Animator animation) {
216 finalView.setAlpha(startAlpha);
217 // TODO: restore view offset from overlay repositioning
218 if (finalViewToKeep != null) {
219 finalViewToKeep.setVisibility(finalVisibility);
220 }
221 if (finalOverlayView != null) {
222 finalSceneRoot.getOverlay().remove(finalOverlayView);
223 }
224 }
Chet Haase199acdf2013-07-24 18:40:55 -0700225
226 @Override
227 public void onAnimationPause(Animator animation) {
228 if (finalOverlayView != null) {
229 finalSceneRoot.getOverlay().remove(finalOverlayView);
230 }
231 }
232
233 @Override
234 public void onAnimationResume(Animator animation) {
235 if (finalOverlayView != null) {
236 finalSceneRoot.getOverlay().add(finalOverlayView);
237 }
238 }
Chet Haase2ea7f8b2013-06-21 15:00:05 -0700239 };
Chet Haase199acdf2013-07-24 18:40:55 -0700240 return createAnimation(view, startAlpha, endAlpha, endListener);
Chet Haasefaebd8f2012-05-18 14:17:57 -0700241 }
242 if (viewToKeep != null) {
243 // TODO: find a different way to do this, like just changing the view to be
244 // VISIBLE for the duration of the transition
245 viewToKeep.setVisibility((View.VISIBLE));
Chet Haase2ea7f8b2013-06-21 15:00:05 -0700246 // TODO: add automatic facility to Visibility superclass for keeping views around
247 final float startAlpha = view.getAlpha();
248 float endAlpha = 0;
249 final View finalView = view;
250 final View finalOverlayView = overlayView;
251 final View finalViewToKeep = viewToKeep;
252 final ViewGroup finalSceneRoot = sceneRoot;
Chet Haase199acdf2013-07-24 18:40:55 -0700253 final AnimatorListenerAdapter endListener = new AnimatorListenerAdapter() {
254 boolean mCanceled = false;
255 float mPausedAlpha = -1;
256
257 @Override
258 public void onAnimationPause(Animator animation) {
259 if (finalViewToKeep != null && !mCanceled) {
260 finalViewToKeep.setVisibility(finalVisibility);
261 }
262 mPausedAlpha = finalView.getAlpha();
263 finalView.setAlpha(startAlpha);
264 }
265
266 @Override
267 public void onAnimationResume(Animator animation) {
268 if (finalViewToKeep != null && !mCanceled) {
269 finalViewToKeep.setVisibility(View.VISIBLE);
270 }
271 finalView.setAlpha(mPausedAlpha);
272 }
273
274 @Override
275 public void onAnimationCancel(Animator animation) {
276 mCanceled = true;
277 if (mPausedAlpha >= 0) {
278 finalView.setAlpha(mPausedAlpha);
279 }
280 }
281
Chet Haase2ea7f8b2013-06-21 15:00:05 -0700282 @Override
283 public void onAnimationEnd(Animator animation) {
Chet Haase199acdf2013-07-24 18:40:55 -0700284 if (!mCanceled) {
285 finalView.setAlpha(startAlpha);
286 }
Chet Haase2ea7f8b2013-06-21 15:00:05 -0700287 // TODO: restore view offset from overlay repositioning
Chet Haase199acdf2013-07-24 18:40:55 -0700288 if (finalViewToKeep != null && !mCanceled) {
Chet Haase2ea7f8b2013-06-21 15:00:05 -0700289 finalViewToKeep.setVisibility(finalVisibility);
290 }
291 if (finalOverlayView != null) {
292 finalSceneRoot.getOverlay().remove(finalOverlayView);
293 }
Chet Haasefaebd8f2012-05-18 14:17:57 -0700294 }
Chet Haase2ea7f8b2013-06-21 15:00:05 -0700295 };
Chet Haase199acdf2013-07-24 18:40:55 -0700296 return createAnimation(view, startAlpha, endAlpha, endListener);
Chet Haasefaebd8f2012-05-18 14:17:57 -0700297 }
Chet Haase2ea7f8b2013-06-21 15:00:05 -0700298 return null;
Chet Haasefaebd8f2012-05-18 14:17:57 -0700299 }
300
301}