• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2008 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.graphics.drawable;
18 
19 import android.content.pm.ActivityInfo.Config;
20 import android.content.res.Resources;
21 import android.graphics.Canvas;
22 import android.os.SystemClock;
23 
24 /**
25  * An extension of LayerDrawables that is intended to cross-fade between
26  * the first and second layer. To start the transition, call {@link #startTransition(int)}. To
27  * display just the first layer, call {@link #resetTransition()}.
28  * <p>
29  * It can be defined in an XML file with the <code>&lt;transition></code> element.
30  * Each Drawable in the transition is defined in a nested <code>&lt;item></code>. For more
31  * information, see the guide to <a
32  * href="{@docRoot}guide/topics/resources/drawable-resource.html">Drawable Resources</a>.</p>
33  *
34  * @attr ref android.R.styleable#LayerDrawableItem_left
35  * @attr ref android.R.styleable#LayerDrawableItem_top
36  * @attr ref android.R.styleable#LayerDrawableItem_right
37  * @attr ref android.R.styleable#LayerDrawableItem_bottom
38  * @attr ref android.R.styleable#LayerDrawableItem_drawable
39  * @attr ref android.R.styleable#LayerDrawableItem_id
40  *
41  */
42 public class TransitionDrawable extends LayerDrawable implements Drawable.Callback {
43 
44     /**
45      * A transition is about to start.
46      */
47     private static final int TRANSITION_STARTING = 0;
48 
49     /**
50      * The transition has started and the animation is in progress
51      */
52     private static final int TRANSITION_RUNNING = 1;
53 
54     /**
55      * No transition will be applied
56      */
57     private static final int TRANSITION_NONE = 2;
58 
59     /**
60      * The current state of the transition. One of {@link #TRANSITION_STARTING},
61      * {@link #TRANSITION_RUNNING} and {@link #TRANSITION_NONE}
62      */
63     private int mTransitionState = TRANSITION_NONE;
64 
65     private boolean mReverse;
66     private long mStartTimeMillis;
67     private int mFrom;
68     private int mTo;
69     private int mDuration;
70     private int mOriginalDuration;
71     private int mAlpha = 0;
72     private boolean mCrossFade;
73 
74     /**
75      * Create a new transition drawable with the specified list of layers. At least
76      * 2 layers are required for this drawable to work properly.
77      */
TransitionDrawable(Drawable[] layers)78     public TransitionDrawable(Drawable[] layers) {
79         this(new TransitionState(null, null, null), layers);
80     }
81 
82     /**
83      * Create a new transition drawable with no layer. To work correctly, at least 2
84      * layers must be added to this drawable.
85      *
86      * @see #TransitionDrawable(Drawable[])
87      */
TransitionDrawable()88     TransitionDrawable() {
89         this(new TransitionState(null, null, null), (Resources) null);
90     }
91 
TransitionDrawable(TransitionState state, Resources res)92     private TransitionDrawable(TransitionState state, Resources res) {
93         super(state, res);
94     }
95 
TransitionDrawable(TransitionState state, Drawable[] layers)96     private TransitionDrawable(TransitionState state, Drawable[] layers) {
97         super(layers, state);
98     }
99 
100     @Override
createConstantState(LayerState state, Resources res)101     LayerState createConstantState(LayerState state, Resources res) {
102         return new TransitionState((TransitionState) state, this, res);
103     }
104 
105     /**
106      * Begin the second layer on top of the first layer.
107      *
108      * @param durationMillis The length of the transition in milliseconds
109      */
startTransition(int durationMillis)110     public void startTransition(int durationMillis) {
111         mFrom = 0;
112         mTo = 255;
113         mAlpha = 0;
114         mDuration = mOriginalDuration = durationMillis;
115         mReverse = false;
116         mTransitionState = TRANSITION_STARTING;
117         invalidateSelf();
118     }
119 
120     /**
121      * Show the second layer on top of the first layer immediately
122      *
123      * @hide
124      */
showSecondLayer()125     public void showSecondLayer() {
126         mAlpha = 255;
127         mReverse = false;
128         mTransitionState = TRANSITION_NONE;
129         invalidateSelf();
130     }
131 
132     /**
133      * Show only the first layer.
134      */
resetTransition()135     public void resetTransition() {
136         mAlpha = 0;
137         mTransitionState = TRANSITION_NONE;
138         invalidateSelf();
139     }
140 
141     /**
142      * Reverses the transition, picking up where the transition currently is.
143      * If the transition is not currently running, this will start the transition
144      * with the specified duration. If the transition is already running, the last
145      * known duration will be used.
146      *
147      * @param duration The duration to use if no transition is running.
148      */
reverseTransition(int duration)149     public void reverseTransition(int duration) {
150         final long time = SystemClock.uptimeMillis();
151         // Animation is over
152         if (time - mStartTimeMillis > mDuration) {
153             if (mTo == 0) {
154                 mFrom = 0;
155                 mTo = 255;
156                 mAlpha = 0;
157                 mReverse = false;
158             } else {
159                 mFrom = 255;
160                 mTo = 0;
161                 mAlpha = 255;
162                 mReverse = true;
163             }
164             mDuration = mOriginalDuration = duration;
165             mTransitionState = TRANSITION_STARTING;
166             invalidateSelf();
167             return;
168         }
169 
170         mReverse = !mReverse;
171         mFrom = mAlpha;
172         mTo = mReverse ? 0 : 255;
173         mDuration = (int) (mReverse ? time - mStartTimeMillis :
174                 mOriginalDuration - (time - mStartTimeMillis));
175         mTransitionState = TRANSITION_STARTING;
176     }
177 
178     @Override
draw(Canvas canvas)179     public void draw(Canvas canvas) {
180         boolean done = true;
181 
182         switch (mTransitionState) {
183             case TRANSITION_STARTING:
184                 mStartTimeMillis = SystemClock.uptimeMillis();
185                 done = false;
186                 mTransitionState = TRANSITION_RUNNING;
187                 break;
188 
189             case TRANSITION_RUNNING:
190                 if (mStartTimeMillis >= 0) {
191                     float normalized = (float)
192                             (SystemClock.uptimeMillis() - mStartTimeMillis) / mDuration;
193                     done = normalized >= 1.0f;
194                     normalized = Math.min(normalized, 1.0f);
195                     mAlpha = (int) (mFrom  + (mTo - mFrom) * normalized);
196                 }
197                 break;
198         }
199 
200         final int alpha = mAlpha;
201         final boolean crossFade = mCrossFade;
202         final ChildDrawable[] array = mLayerState.mChildren;
203 
204         if (done) {
205             // the setAlpha() calls below trigger invalidation and redraw. If we're done, just draw
206             // the appropriate drawable[s] and return
207             if (!crossFade || alpha == 0) {
208                 array[0].mDrawable.draw(canvas);
209             }
210             if (alpha == 0xFF) {
211                 array[1].mDrawable.draw(canvas);
212             }
213             return;
214         }
215 
216         Drawable d;
217         d = array[0].mDrawable;
218         if (crossFade) {
219             d.setAlpha(255 - alpha);
220         }
221         d.draw(canvas);
222         if (crossFade) {
223             d.setAlpha(0xFF);
224         }
225 
226         if (alpha > 0) {
227             d = array[1].mDrawable;
228             d.setAlpha(alpha);
229             d.draw(canvas);
230             d.setAlpha(0xFF);
231         }
232 
233         if (!done) {
234             invalidateSelf();
235         }
236     }
237 
238     /**
239      * Enables or disables the cross fade of the drawables. When cross fade
240      * is disabled, the first drawable is always drawn opaque. With cross
241      * fade enabled, the first drawable is drawn with the opposite alpha of
242      * the second drawable. Cross fade is disabled by default.
243      *
244      * @param enabled True to enable cross fading, false otherwise.
245      */
setCrossFadeEnabled(boolean enabled)246     public void setCrossFadeEnabled(boolean enabled) {
247         mCrossFade = enabled;
248     }
249 
250     /**
251      * Indicates whether the cross fade is enabled for this transition.
252      *
253      * @return True if cross fading is enabled, false otherwise.
254      */
isCrossFadeEnabled()255     public boolean isCrossFadeEnabled() {
256         return mCrossFade;
257     }
258 
259     static class TransitionState extends LayerState {
TransitionState(TransitionState orig, TransitionDrawable owner, Resources res)260         TransitionState(TransitionState orig, TransitionDrawable owner, Resources res) {
261             super(orig, owner, res);
262         }
263 
264         @Override
newDrawable()265         public Drawable newDrawable() {
266             return new TransitionDrawable(this, (Resources) null);
267         }
268 
269         @Override
newDrawable(Resources res)270         public Drawable newDrawable(Resources res) {
271             return new TransitionDrawable(this, res);
272         }
273 
274         @Override
getChangingConfigurations()275         public @Config int getChangingConfigurations() {
276             return mChangingConfigurations;
277         }
278     }
279 }
280