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