/* * Copyright (C) 2016 Google Inc. All Rights Reserved. * * 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.google.android.apps.santatracker.dasherdancer; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.widget.ImageView; public class FrameAnimationView extends ImageView { private Drawable[] mFrames; private int[] mFrameIndices; private int mFrameIndex; private final Paint mPaint = new Paint(); private InsetDrawableCompat mInsetDrawable; private BitmapDrawable mBitmapDrawable; public FrameAnimationView(Context context) { super(context); init(); } public FrameAnimationView(Context context, AttributeSet attrs) { super(context, attrs); init(); } public FrameAnimationView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } private void init() { mPaint.setAntiAlias(true); setImageDrawable(mInsetDrawable); } /** * Will attempt to recycle the old frames before setting the new frames. * * @param frames * @param frameIndices */ public void setFrames(Drawable[] frames, int[] frameIndices) { if (mFrames != null) { mFrames = null; } mFrames = frames; mFrameIndices = frameIndices; } public int getFrameIndex() { return mFrameIndex; } public void setFrameIndex(int frameIndex) { mFrameIndex = frameIndex; if (mFrames != null && mFrameIndex >= 0 && mFrameIndex < mFrameIndices.length && mFrames[mFrameIndices[mFrameIndex]] != null && !isBitmapRecycled(mFrames[mFrameIndices[mFrameIndex]])) { invalidate(); } } private boolean isBitmapRecycled(Drawable drawable) { if (drawable != null && drawable instanceof InsetDrawableCompat) { drawable = ((InsetDrawableCompat) drawable).getDrawable(); } if (drawable != null && drawable instanceof BitmapDrawable) { Bitmap bmp = ((BitmapDrawable) drawable).getBitmap(); if (bmp != null) { return bmp.isRecycled(); } } return true; } Matrix matrix = new Matrix(); @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); recalculateMatrix(w, h); } private void recalculateMatrix(int vwidth, int vheight) { if (mFrames != null && mFrameIndex >= 0 && mFrameIndex < mFrameIndices.length) { InsetDrawableCompat insetDrawableCompat = (InsetDrawableCompat) mFrames[mFrameIndices[mFrameIndex]]; if (isBitmapRecycled(insetDrawableCompat)) { return; } matrix.reset(); float scale; float dx = 0, dy = 0; int dwidth = insetDrawableCompat.getDrawable().getIntrinsicWidth() + insetDrawableCompat.getLeft() + insetDrawableCompat.getRight(); int dheight = insetDrawableCompat.getDrawable().getIntrinsicHeight() + insetDrawableCompat.getTop() + insetDrawableCompat.getBottom(); if (dwidth * vheight > vwidth * dheight) { scale = (float) vheight / (float) dheight; dx = (vwidth - dwidth * scale) * 0.5f; } else { scale = (float) vwidth / (float) dwidth; dy = (vheight - dheight * scale) * 0.5f; } matrix.setTranslate(insetDrawableCompat.getLeft(), insetDrawableCompat.getTop()); matrix.postScale(scale, scale); matrix.postTranslate(Math.round(dx), Math.round(dy)); setImageMatrix(matrix); } } public void onDraw(Canvas c) { if (mFrames != null && mFrameIndex >= 0 && mFrameIndex < mFrameIndices.length) { //the line below should work with InsetDrawable with CENTER_CROP, //but it doesn't work on older APIs (JB) beacause of different handling of insets: //setImageDrawable(mFrames[mFrameIndices[mFrameIndex]]); //code below fixes the bug in InsetDrawable and works on all API levels: //instead of setting the InsetDrawable on FrameAnimationView, //we set the Bitmap directly and use the insets to calculate the correct matrix InsetDrawableCompat insetDrawableCompat = (InsetDrawableCompat) mFrames[mFrameIndices[mFrameIndex]]; if (isBitmapRecycled(insetDrawableCompat)) { return; } Bitmap newBitmap = ((BitmapDrawable) insetDrawableCompat.getDrawable()).getBitmap(); Drawable current = getDrawable(); if (current == null || (current instanceof BitmapDrawable && !newBitmap.equals(((BitmapDrawable) current).getBitmap()))) { setImageBitmap(newBitmap); recalculateMatrix(getWidth(), getHeight()); } } if (isBitmapRecycled(getDrawable())) { return; } super.onDraw(c); } }