/* * Copyright (C) 2009 Muthu Ramadoss. All rights reserved. * * Modified from Romain Guy Shelves project to suit Books-Exchange requirements. * Original source from Shelves - http://code.google.com/p/shelves/ */ /* * Copyright (C) 2008 The Android Open Source Project, Romain Guy * * 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 com.androidrocks.bex.drawable; import android.graphics.Canvas; import android.graphics.ColorFilter; import android.graphics.PixelFormat; import android.graphics.Bitmap; import android.graphics.Paint; import android.graphics.drawable.Drawable; import android.os.SystemClock; import android.os.Handler; public class CrossFadeDrawable extends Drawable { private static final int TRANSITION_STARTING = 0; private static final int TRANSITION_RUNNING = 1; private static final int TRANSITION_NONE = 2; private int mTransitionState = TRANSITION_NONE; private boolean mCrossFade; private boolean mReverse; private long mStartTimeMillis; private int mFrom; private int mTo; private int mDuration; private int mOriginalDuration; private int mAlpha; private Bitmap mStart; private Bitmap mEnd; private final Paint mStartPaint = new Paint(Paint.FILTER_BITMAP_FLAG); private final Paint mEndPaint = new Paint(Paint.FILTER_BITMAP_FLAG); private float mStartX; private float mStartY; private float mEndX; private float mEndY; private final Handler mHandler; private final Runnable mInvalidater; public CrossFadeDrawable(Bitmap start, Bitmap end) { mStart = start; mEnd = end; mHandler = new Handler(); mInvalidater = new Runnable() { public void run() { invalidateSelf(); } }; } /** * Begin the second layer on top of the first layer. * * @param durationMillis The length of the transition in milliseconds */ public void startTransition(int durationMillis) { mFrom = 0; mTo = 255; mAlpha = 0; mOriginalDuration = mDuration = durationMillis; mReverse = false; mTransitionState = mStart != mEnd ? TRANSITION_STARTING : TRANSITION_NONE; 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(int duration) { final long time = SystemClock.uptimeMillis(); if (time - mStartTimeMillis > mOriginalDuration) { if (mAlpha == 0) { mFrom = 0; mTo = 255; mAlpha = 0; mReverse = false; } else { mFrom = 255; mTo = 0; mAlpha = 255; mReverse = true; } mDuration = mOriginalDuration = duration; mTransitionState = TRANSITION_STARTING; mHandler.post(mInvalidater); return; } mReverse = !mReverse; mFrom = mAlpha; mTo = mReverse ? 0 : 255; mDuration = (int) (mReverse ? time - mStartTimeMillis : mOriginalDuration - (time - mStartTimeMillis)); mTransitionState = TRANSITION_STARTING; } @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; mAlpha = (int) (mFrom + (mTo - mFrom) * Math.min(normalized, 1.0f)); if (done) { mTransitionState = TRANSITION_NONE; mHandler.post(mInvalidater); } } break; } final int alpha = mAlpha; final boolean crossFade = mCrossFade; Bitmap bitmap = mStart; Paint paint = mStartPaint; if (!crossFade || 255 - alpha > 0) { if (crossFade) { paint.setAlpha(255 - alpha); } canvas.drawBitmap(bitmap, mStartX, mStartY, paint); if (crossFade) { paint.setAlpha(0xFF); } } if (alpha > 0) { bitmap = mEnd; paint = mEndPaint; paint.setAlpha(alpha); canvas.drawBitmap(bitmap, mEndX, mEndY, paint); paint.setAlpha(0xFF); } if (!done) { mHandler.post(mInvalidater); } } Bitmap getStart() { return mStart; } void setStart(Bitmap start) { mStart = start; invalidateSelf(); } Bitmap getEnd() { return mEnd; } public void setEnd(Bitmap end) { mEnd = end; invalidateSelf(); } @Override public void setBounds(int left, int top, int right, int bottom) { super.setBounds(left, top, right, bottom); final int width = right - left; final int height = bottom - top; mStartX = (width - mStart.getWidth()) / 2.0f; mStartY = height - mStart.getHeight(); mEndX = (width - mEnd.getWidth()) / 2.0f; mEndY = height - mEnd.getHeight(); } @Override public int getIntrinsicWidth() { return Math.max(mStart.getWidth(), mEnd.getWidth()); } @Override public int getIntrinsicHeight() { return Math.max(mStart.getHeight(), mEnd.getHeight()); } @Override public int getMinimumWidth() { return Math.max(mStart.getWidth(), mEnd.getWidth()); } @Override public int getMinimumHeight() { return Math.max(mStart.getHeight(), mEnd.getHeight()); } @Override public void setDither(boolean dither) { mStartPaint.setDither(true); mEndPaint.setDither(true); } @Override public void setFilterBitmap(boolean filter) { mStartPaint.setFilterBitmap(true); mEndPaint.setFilterBitmap(true); } /** * Ignored. */ public void setAlpha(int alpha) { } public void setColorFilter(ColorFilter cf) { mStartPaint.setColorFilter(cf); mEndPaint.setColorFilter(cf); } public int getOpacity() { return PixelFormat.TRANSLUCENT; } /** * 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. * * @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. */ boolean isCrossFadeEnabled() { return mCrossFade; } }