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