[go: nahoru, domu]

Fade.java revision 23c61f6bc57a611d97d333bce0d8fe00ab81af4c
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 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.
32 *
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
38 */
39public class Fade extends Visibility {
40
41    private static boolean DBG = Transition.DBG && false;
42
43    private static final String LOG_TAG = "Fade";
44    private static final String PROPNAME_ALPHA = "android:fade:alpha";
45    private static final String PROPNAME_SCREEN_X = "android:fade:screenX";
46    private static final String PROPNAME_SCREEN_Y = "android:fade:screenY";
47
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     */
84    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        }
93        final ObjectAnimator anim = ObjectAnimator.ofFloat(view, "alpha", startAlpha, endAlpha);
94        if (DBG) {
95            Log.d(LOG_TAG, "Created animator " + anim);
96        }
97        if (listener != null) {
98            anim.addListener(listener);
99            anim.addPauseListener(listener);
100        }
101        return anim;
102    }
103
104    private void captureValues(TransitionValues transitionValues) {
105        float alpha = transitionValues.view.getAlpha();
106        transitionValues.values.put(PROPNAME_ALPHA, alpha);
107        int[] loc = new int[2];
108        transitionValues.view.getLocationOnScreen(loc);
109        transitionValues.values.put(PROPNAME_SCREEN_X, loc[0]);
110        transitionValues.values.put(PROPNAME_SCREEN_Y, loc[1]);
111    }
112
113    @Override
114    public void captureStartValues(TransitionValues transitionValues) {
115        super.captureStartValues(transitionValues);
116        captureValues(transitionValues);
117    }
118
119
120    @Override
121    public void captureEndValues(TransitionValues transitionValues) {
122        super.captureEndValues(transitionValues);
123    }
124
125    @Override
126    public Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues,
127            TransitionValues endValues) {
128        Animator animator = super.createAnimator(sceneRoot, startValues, endValues);
129        if (animator == null && startValues != null && endValues != null) {
130            boolean endVisible = isVisible(endValues);
131            final View endView = endValues.view;
132            float endAlpha = endView.getAlpha();
133            float startAlpha = (Float) startValues.values.get(PROPNAME_ALPHA);
134            if ((endVisible && startAlpha < endAlpha && (mFadingMode & Fade.IN) != 0) ||
135                    (!endVisible && startAlpha > endAlpha && (mFadingMode & Fade.OUT) != 0)) {
136                animator = createAnimation(endView, startAlpha, endAlpha, null);
137            }
138        }
139        return animator;
140    }
141
142    @Override
143    public Animator onAppear(ViewGroup sceneRoot,
144            TransitionValues startValues, int startVisibility,
145            TransitionValues endValues, int endVisibility) {
146        if ((mFadingMode & IN) != IN || endValues == null) {
147            return null;
148        }
149        final View endView = endValues.view;
150        if (DBG) {
151            View startView = (startValues != null) ? startValues.view : null;
152            Log.d(LOG_TAG, "Fade.onAppear: startView, startVis, endView, endVis = " +
153                    startView + ", " + startVisibility + ", " + endView + ", " + endVisibility);
154        }
155        // if alpha < 1, just fade it in from the current value
156        if (endView.getAlpha() == 1.0f) {
157            endView.setAlpha(0);
158            TransitionListener transitionListener = new TransitionListenerAdapter() {
159                boolean mCanceled = false;
160                float mPausedAlpha;
161
162                @Override
163                public void onTransitionCancel(Transition transition) {
164                    endView.setAlpha(1);
165                    mCanceled = true;
166                }
167
168                @Override
169                public void onTransitionEnd(Transition transition) {
170                    if (!mCanceled) {
171                        endView.setAlpha(1);
172                    }
173                }
174
175                @Override
176                public void onTransitionPause(Transition transition) {
177                    mPausedAlpha = endView.getAlpha();
178                    endView.setAlpha(1);
179                }
180
181                @Override
182                public void onTransitionResume(Transition transition) {
183                    endView.setAlpha(mPausedAlpha);
184                }
185            };
186            addListener(transitionListener);
187        }
188        return createAnimation(endView, endView.getAlpha(), 1, null);
189    }
190
191    @Override
192    public Animator onDisappear(ViewGroup sceneRoot,
193            TransitionValues startValues, int startVisibility,
194            TransitionValues endValues, int endVisibility) {
195        if ((mFadingMode & OUT) != OUT) {
196            return null;
197        }
198        View view;
199        View startView = (startValues != null) ? startValues.view : null;
200        View endView = (endValues != null) ? endValues.view : null;
201        if (DBG) {
202            Log.d(LOG_TAG, "Fade.onDisappear: startView, startVis, endView, endVis = " +
203                        startView + ", " + startVisibility + ", " + endView + ", " + endVisibility);
204        }
205        View overlayView = null;
206        View viewToKeep = null;
207        if (endView == null || endView.getParent() == null) {
208            // view was removed: add the start view to the Overlay
209            view = startView;
210            overlayView = view;
211        } else {
212            // visibility change
213            if (endVisibility == View.INVISIBLE) {
214                view = endView;
215                viewToKeep = view;
216            } else {
217                // Becoming GONE
218                if (startView == endView) {
219                    view = endView;
220                    viewToKeep = view;
221                } else {
222                    view = startView;
223                    overlayView = view;
224                }
225            }
226        }
227        final int finalVisibility = endVisibility;
228        // TODO: add automatic facility to Visibility superclass for keeping views around
229        if (overlayView != null) {
230            // TODO: Need to do this for general case of adding to overlay
231            int screenX = (Integer) startValues.values.get(PROPNAME_SCREEN_X);
232            int screenY = (Integer) startValues.values.get(PROPNAME_SCREEN_Y);
233            int[] loc = new int[2];
234            sceneRoot.getLocationOnScreen(loc);
235            overlayView.offsetLeftAndRight((screenX - loc[0]) - overlayView.getLeft());
236            overlayView.offsetTopAndBottom((screenY - loc[1]) - overlayView.getTop());
237            sceneRoot.getOverlay().add(overlayView);
238            // TODO: add automatic facility to Visibility superclass for keeping views around
239            final float startAlpha = view.getAlpha();
240            float endAlpha = 0;
241            final View finalView = view;
242            final View finalOverlayView = overlayView;
243            final View finalViewToKeep = viewToKeep;
244            final ViewGroup finalSceneRoot = sceneRoot;
245            final AnimatorListenerAdapter endListener = new AnimatorListenerAdapter() {
246                @Override
247                public void onAnimationEnd(Animator animation) {
248                    finalView.setAlpha(startAlpha);
249                    // TODO: restore view offset from overlay repositioning
250                    if (finalViewToKeep != null) {
251                        finalViewToKeep.setVisibility(finalVisibility);
252                    }
253                    if (finalOverlayView != null) {
254                        finalSceneRoot.getOverlay().remove(finalOverlayView);
255                    }
256                }
257
258                @Override
259                public void onAnimationPause(Animator animation) {
260                    if (finalOverlayView != null) {
261                        finalSceneRoot.getOverlay().remove(finalOverlayView);
262                    }
263                }
264
265                @Override
266                public void onAnimationResume(Animator animation) {
267                    if (finalOverlayView != null) {
268                        finalSceneRoot.getOverlay().add(finalOverlayView);
269                    }
270                }
271            };
272            return createAnimation(view, startAlpha, endAlpha, endListener);
273        }
274        if (viewToKeep != null) {
275            // TODO: find a different way to do this, like just changing the view to be
276            // VISIBLE for the duration of the transition
277            viewToKeep.setVisibility((View.VISIBLE));
278            // TODO: add automatic facility to Visibility superclass for keeping views around
279            final float startAlpha = view.getAlpha();
280            float endAlpha = 0;
281            final View finalView = view;
282            final View finalOverlayView = overlayView;
283            final View finalViewToKeep = viewToKeep;
284            final ViewGroup finalSceneRoot = sceneRoot;
285            final AnimatorListenerAdapter endListener = new AnimatorListenerAdapter() {
286                boolean mCanceled = false;
287                float mPausedAlpha = -1;
288
289                @Override
290                public void onAnimationPause(Animator animation) {
291                    if (finalViewToKeep != null && !mCanceled) {
292                        finalViewToKeep.setVisibility(finalVisibility);
293                    }
294                    mPausedAlpha = finalView.getAlpha();
295                    finalView.setAlpha(startAlpha);
296                }
297
298                @Override
299                public void onAnimationResume(Animator animation) {
300                    if (finalViewToKeep != null && !mCanceled) {
301                        finalViewToKeep.setVisibility(View.VISIBLE);
302                    }
303                    finalView.setAlpha(mPausedAlpha);
304                }
305
306                @Override
307                public void onAnimationCancel(Animator animation) {
308                    mCanceled = true;
309                    if (mPausedAlpha >= 0) {
310                        finalView.setAlpha(mPausedAlpha);
311                    }
312                }
313
314                @Override
315                public void onAnimationEnd(Animator animation) {
316                    if (!mCanceled) {
317                        finalView.setAlpha(startAlpha);
318                    }
319                    // TODO: restore view offset from overlay repositioning
320                    if (finalViewToKeep != null && !mCanceled) {
321                        finalViewToKeep.setVisibility(finalVisibility);
322                    }
323                    if (finalOverlayView != null) {
324                        finalSceneRoot.getOverlay().remove(finalOverlayView);
325                    }
326                }
327            };
328            return createAnimation(view, startAlpha, endAlpha, endListener);
329        }
330        return null;
331    }
332
333}