/* * Copyright (C) 2008 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package legency.graphic.drawable; import android.graphics.Canvas; import android.graphics.drawable.Drawable; import android.graphics.drawable.LayerDrawable; import android.os.SystemClock; /** * An extension of LayerDrawables that is intended to cross-fade between the * first and second layer. To start the transition, call * {@link #startTransition(int)}. To display just the first layer, call * {@link #resetTransition()}. * <p> * It can be defined in an XML file with the <code><transition></code> * element. Each Drawable in the transition is defined in a nested * <code><item></code>. For more * information, see the guide to <a * href="{@docRoot}. For more information, see the guide to <a href="{@docRoot} * guide/topics/resources/drawable-resource.html">Drawable Resources</a>. * </p> * * @attr ref android.R.styleable#LayerDrawableItem_left * @attr ref android.R.styleable#LayerDrawableItem_top * @attr ref android.R.styleable#LayerDrawableItem_right * @attr ref android.R.styleable#LayerDrawableItem_bottom * @attr ref android.R.styleable#LayerDrawableItem_drawable * @attr ref android.R.styleable#LayerDrawableItem_id */ public class TransitionDrawable extends LayerDrawable implements Drawable.Callback { /** * A transition is about to start. */ private static final int TRANSITION_STARTING = 0; /** * The transition has started and the animation is in progress */ private static final int TRANSITION_RUNNING = 1; /** * No transition will be applied */ private static final int TRANSITION_NONE = 2; private static final int TRANSITION_RATE = 3; /** * The current state of the transition. One of {@link #TRANSITION_STARTING}, * {@link #TRANSITION_RUNNING} and {@link #TRANSITION_NONE} */ private int mTransitionState = TRANSITION_NONE; private boolean mReverse; private long mStartTimeMillis; private int mFrom; private int mTo; private long mDuration; private long mOriginalDuration; private int mAlpha = 0; private boolean mCrossFade; private float rate; /** * Create a new transition drawable with the specified list of layers. At * least 2 layers are required for this drawable to work properly. */ public TransitionDrawable(Drawable[] layers) { super(layers); } /** * Begin the second layer on top of the first layer. * * @param durationMillis The length of the transition in milliseconds */ public void startTransition(long durationMillis) { mFrom = 0; mTo = 255; mAlpha = 0; mDuration = mOriginalDuration = durationMillis; mReverse = false; mTransitionState = TRANSITION_STARTING; invalidateSelf(); } /** * Show only the first layer. */ public void resetTransition() { mAlpha = 0; mTransitionState = TRANSITION_NONE; invalidateSelf(); } /** * Reverses the transition, picking up where the transition currently is. If * the transition is not currently running, this will start the transition * with the specified duration. If the transition is already running, the * last known duration will be used. * * @param duration The duration to use if no transition is running. */ public void reverseTransition(long duration) { final long time = SystemClock.uptimeMillis(); // Animation is over if (time - mStartTimeMillis > mDuration) { if (mTo == 0) { mFrom = 0; mTo = 255; mAlpha = 0; mReverse = false; } else { mFrom = 255; mTo = 0; mAlpha = 255; mReverse = true; } mDuration = mOriginalDuration = duration; mTransitionState = TRANSITION_STARTING; invalidateSelf(); return; } mReverse = !mReverse; mFrom = mAlpha; mTo = mReverse ? 0 : 255; mDuration = (int) (mReverse ? time - mStartTimeMillis : mOriginalDuration - (time - mStartTimeMillis)); mTransitionState = TRANSITION_STARTING; } public void reverse(long duration) { mTo = 0; mFrom = mAlpha = ((rate != 0) ? (int) (255 * rate) : 255); mDuration = mOriginalDuration = duration; mTransitionState = TRANSITION_STARTING; invalidateSelf(); } @Override public void draw(Canvas canvas) { boolean done = true; switch (mTransitionState) { case TRANSITION_STARTING: mStartTimeMillis = SystemClock.uptimeMillis(); done = false; mTransitionState = TRANSITION_RUNNING; break; case TRANSITION_RUNNING: if (mStartTimeMillis >= 0) { float normalized = (float) (SystemClock.uptimeMillis() - mStartTimeMillis) / mDuration; done = normalized >= 1.0f; normalized = Math.min(normalized, 1.0f); mAlpha = (int) (mFrom + (mTo - mFrom) * normalized); } break; case TRANSITION_RATE: float min = Math.min(rate, 1); mAlpha = (int) (mFrom + (mTo - mFrom) * min); done = false; break; } final int alpha = mAlpha; final boolean crossFade = mCrossFade; Drawable org = getDrawable(0); Drawable drawable = getDrawable(1); if (done) { // the setAlpha() calls below trigger invalidation and redraw. If // we're done, just draw // the appropriate drawable[s] and return if (!crossFade || alpha == 0) { org.draw(canvas); } if (alpha == 0xFF) { drawable.draw(canvas); } return; } Drawable d; d = org; if (crossFade) { d.setAlpha(255 - alpha); } d.draw(canvas); if (crossFade) { d.setAlpha(0xFF); } if (alpha > 0) { d = drawable; d.setAlpha(alpha); d.draw(canvas); d.setAlpha(0xFF); } if (!done) { invalidateSelf(); } } void setRate() { } /** * Enables or disables the cross fade of the drawables. When cross fade is * disabled, the first drawable is always drawn opaque. With cross fade * enabled, the first drawable is drawn with the opposite alpha of the * second drawable. Cross fade is disabled by default. * * @param enabled True to enable cross fading, false otherwise. */ public void setCrossFadeEnabled(boolean enabled) { mCrossFade = enabled; } /** * Indicates whether the cross fade is enabled for this transition. * * @return True if cross fading is enabled, false otherwise. */ public boolean isCrossFadeEnabled() { return mCrossFade; } public void setRate(float rate) { this.rate = rate; mFrom = 0; mTo = 255; mAlpha = 0; mTransitionState = TRANSITION_RATE; invalidateSelf(); } }