Fields Summary |
---|
private static final Object | sLock |
private static android.os.HandlerThread | sDecodingThread |
private static android.os.Handler | sDecodingThreadHandler |
private static BitmapProvider | sAllocatingBitmapProvider |
public static final int | LOOP_ONCELoop only once. |
public static final int | LOOP_INFLoop continuously. The OnFinishedListener will never be called. |
public static final int | LOOP_DEFAULTUse loop count stored in source data, or LOOP_ONCE if not present. |
private final FrameSequence | mFrameSequence |
private final FrameSequence.State | mFrameSequenceState |
private final android.graphics.Paint | mPaint |
private final android.graphics.Rect | mSrcRect |
private final Object | mLock |
private final BitmapProvider | mBitmapProvider |
private boolean | mDestroyed |
private android.graphics.Bitmap | mFrontBitmap |
private android.graphics.Bitmap | mBackBitmap |
private static final int | STATE_SCHEDULED |
private static final int | STATE_DECODING |
private static final int | STATE_WAITING_TO_SWAP |
private static final int | STATE_READY_TO_SWAP |
private int | mState |
private int | mCurrentLoop |
private int | mLoopBehavior |
private long | mLastSwap |
private long | mNextSwap |
private int | mNextFrameToDecode |
private OnFinishedListener | mOnFinishedListener |
private Runnable | mDecodeRunnableRuns on decoding thread, only modifies mBackBitmap's pixels |
private Runnable | mCallbackRunnable |
Methods Summary |
---|
private static android.graphics.Bitmap | acquireAndValidateBitmap(android.support.rastermill.FrameSequenceDrawable$BitmapProvider bitmapProvider, int minWidth, int minHeight)
Bitmap bitmap = bitmapProvider.acquireBitmap(minWidth, minHeight);
if (bitmap.getWidth() < minWidth
|| bitmap.getHeight() < minHeight
|| bitmap.getConfig() != Bitmap.Config.ARGB_8888) {
throw new IllegalArgumentException("Invalid bitmap provided");
}
return bitmap;
|
private void | checkDestroyedLocked()
if (mDestroyed) {
throw new IllegalStateException("Cannot perform operation on recycled drawable");
}
|
private void | destroy(android.support.rastermill.FrameSequenceDrawable$BitmapProvider bitmapProvider)
if (bitmapProvider == null) {
throw new IllegalStateException("BitmapProvider must be non-null");
}
Bitmap bitmapToReleaseA;
Bitmap bitmapToReleaseB;
synchronized (mLock) {
checkDestroyedLocked();
bitmapToReleaseA = mFrontBitmap;
bitmapToReleaseB = mBackBitmap;
mFrontBitmap = null;
mBackBitmap = null;
mDestroyed = true;
}
// For simplicity and safety, we don't destroy the state object here
bitmapProvider.releaseBitmap(bitmapToReleaseA);
bitmapProvider.releaseBitmap(bitmapToReleaseB);
|
public void | destroy()Marks the drawable as permanently recycled (and thus unusable), and releases any owned
Bitmaps drawable to its BitmapProvider, if attached.
If no BitmapProvider is attached to the drawable, recycle() is called on the Bitmaps.
destroy(mBitmapProvider);
|
public void | draw(android.graphics.Canvas canvas)
synchronized (mLock) {
checkDestroyedLocked();
if (mState == STATE_WAITING_TO_SWAP) {
// may have failed to schedule mark ready runnable,
// so go ahead and swap if swapping is due
if (mNextSwap - SystemClock.uptimeMillis() <= 0) {
mState = STATE_READY_TO_SWAP;
}
}
if (isRunning() && mState == STATE_READY_TO_SWAP) {
// Because draw has occurred, the view system is guaranteed to no longer hold a
// reference to the old mFrontBitmap, so we now use it to produce the next frame
Bitmap tmp = mBackBitmap;
mBackBitmap = mFrontBitmap;
mFrontBitmap = tmp;
mLastSwap = SystemClock.uptimeMillis();
boolean continueLooping = true;
if (mNextFrameToDecode == mFrameSequence.getFrameCount() - 1) {
mCurrentLoop++;
if ((mLoopBehavior == LOOP_ONCE && mCurrentLoop == 1) ||
(mLoopBehavior == LOOP_DEFAULT && mCurrentLoop == mFrameSequence.getDefaultLoopCount())) {
continueLooping = false;
}
}
if (continueLooping) {
scheduleDecodeLocked();
} else {
scheduleSelf(mCallbackRunnable, 0);
}
}
}
canvas.drawBitmap(mFrontBitmap, mSrcRect, getBounds(), mPaint);
|
protected void | finalize()
try {
mFrameSequenceState.destroy();
if (!mDestroyed) {
destroy();
}
} finally {
super.finalize();
}
|
public int | getIntrinsicHeight()
return mFrameSequence.getHeight();
|
public int | getIntrinsicWidth()
return mFrameSequence.getWidth();
|
public int | getOpacity()
return mFrameSequence.isOpaque() ? PixelFormat.OPAQUE : PixelFormat.TRANSPARENT;
|
private static void | initializeDecodingThread()
synchronized (sLock) {
if (sDecodingThread != null) return;
sDecodingThread = new HandlerThread("FrameSequence decoding thread",
Process.THREAD_PRIORITY_BACKGROUND);
sDecodingThread.start();
sDecodingThreadHandler = new Handler(sDecodingThread.getLooper());
}
|
public boolean | isDestroyed()
synchronized (mLock) {
return mDestroyed;
}
|
public boolean | isRunning()
synchronized (mLock) {
return mNextFrameToDecode > -1 && !mDestroyed;
}
|
public void | run()
// set ready to swap
synchronized (mLock) {
if (mState != STATE_WAITING_TO_SWAP || mNextFrameToDecode < 0) return;
mState = STATE_READY_TO_SWAP;
}
invalidateSelf();
|
private void | scheduleDecodeLocked()
mState = STATE_SCHEDULED;
mNextFrameToDecode = (mNextFrameToDecode + 1) % mFrameSequence.getFrameCount();
sDecodingThreadHandler.post(mDecodeRunnable);
|
public void | setAlpha(int alpha)
mPaint.setAlpha(alpha);
|
public void | setColorFilter(android.graphics.ColorFilter colorFilter)
mPaint.setColorFilter(colorFilter);
|
public void | setFilterBitmap(boolean filter)
mPaint.setFilterBitmap(filter);
|
public void | setLoopBehavior(int loopBehavior)Define looping behavior of frame sequence.
Must be one of LOOP_ONCE, LOOP_INF, or LOOP_DEFAULT
mLoopBehavior = loopBehavior;
|
public void | setOnFinishedListener(android.support.rastermill.FrameSequenceDrawable$OnFinishedListener onFinishedListener)Register a callback to be invoked when a FrameSequenceDrawable finishes looping.
mOnFinishedListener = onFinishedListener;
|
public boolean | setVisible(boolean visible, boolean restart)
boolean changed = super.setVisible(visible, restart);
if (!visible) {
stop();
} else if (restart || changed) {
stop();
start();
}
return changed;
|
public void | start()
if (!isRunning()) {
synchronized (mLock) {
checkDestroyedLocked();
if (mState == STATE_SCHEDULED) return; // already scheduled
mCurrentLoop = 0;
scheduleDecodeLocked();
}
}
|
public void | stop()
if (isRunning()) {
unscheduleSelf(this);
}
|
public void | unscheduleSelf(java.lang.Runnable what)
synchronized (mLock) {
mNextFrameToDecode = -1;
}
super.unscheduleSelf(what);
|