Home | History | Annotate | Download | only in drawable
      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      */
     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      */
     88     TransitionDrawable() {
     89         this(new TransitionState(null, null, null), (Resources) null);
     90     }
     91 
     92     private TransitionDrawable(TransitionState state, Resources res) {
     93         super(state, res);
     94     }
     95 
     96     private TransitionDrawable(TransitionState state, Drawable[] layers) {
     97         super(layers, state);
     98     }
     99 
    100     @Override
    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      */
    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      */
    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      */
    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      */
    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
    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      */
    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      */
    255     public boolean isCrossFadeEnabled() {
    256         return mCrossFade;
    257     }
    258 
    259     static class TransitionState extends LayerState {
    260         TransitionState(TransitionState orig, TransitionDrawable owner, Resources res) {
    261             super(orig, owner, res);
    262         }
    263 
    264         @Override
    265         public Drawable newDrawable() {
    266             return new TransitionDrawable(this, (Resources) null);
    267         }
    268 
    269         @Override
    270         public Drawable newDrawable(Resources res) {
    271             return new TransitionDrawable(this, res);
    272         }
    273 
    274         @Override
    275         public @Config int getChangingConfigurations() {
    276             return mChangingConfigurations;
    277         }
    278     }
    279 }
    280