* commit '8b455841d9da1ac5ddf1b88e03c0c94613fd4046': Scaling (Animated)VectorDrawable inside ImageView
This commit is contained in:
@ -200,6 +200,11 @@ public class VectorDrawable extends Drawable {
|
|||||||
private static final int LINEJOIN_ROUND = 1;
|
private static final int LINEJOIN_ROUND = 1;
|
||||||
private static final int LINEJOIN_BEVEL = 2;
|
private static final int LINEJOIN_BEVEL = 2;
|
||||||
|
|
||||||
|
// Cap the bitmap size, such that it won't hurt the performance too much
|
||||||
|
// and it won't crash due to a very large scale.
|
||||||
|
// The drawable will look blurry above this size.
|
||||||
|
private static final int MAX_CACHED_BITMAP_SIZE = 2048;
|
||||||
|
|
||||||
private static final boolean DBG_VECTOR_DRAWABLE = false;
|
private static final boolean DBG_VECTOR_DRAWABLE = false;
|
||||||
|
|
||||||
private VectorDrawableState mVectorState;
|
private VectorDrawableState mVectorState;
|
||||||
@ -219,6 +224,11 @@ public class VectorDrawable extends Drawable {
|
|||||||
private int mDpiScaledHeight = 0;
|
private int mDpiScaledHeight = 0;
|
||||||
private Insets mDpiScaleInsets = Insets.NONE;
|
private Insets mDpiScaleInsets = Insets.NONE;
|
||||||
|
|
||||||
|
// Temp variable, only for saving "new" operation at the draw() time.
|
||||||
|
private final float[] mTmpFloats = new float[9];
|
||||||
|
private final Matrix mTmpMatrix = new Matrix();
|
||||||
|
private final Rect mTmpBounds = new Rect();
|
||||||
|
|
||||||
public VectorDrawable() {
|
public VectorDrawable() {
|
||||||
this(null, null);
|
this(null, null);
|
||||||
}
|
}
|
||||||
@ -262,44 +272,59 @@ public class VectorDrawable extends Drawable {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void draw(Canvas canvas) {
|
public void draw(Canvas canvas) {
|
||||||
final Rect bounds = getBounds();
|
// We will offset the bounds for drawBitmap, so copyBounds() here instead
|
||||||
if (bounds.width() <= 0 || bounds.height() <= 0) {
|
// of getBounds().
|
||||||
|
copyBounds(mTmpBounds);
|
||||||
|
if (mTmpBounds.width() <= 0 || mTmpBounds.height() <= 0) {
|
||||||
// Nothing to draw
|
// Nothing to draw
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final int saveCount = canvas.save();
|
// Color filters always override tint filters.
|
||||||
final boolean needMirroring = needMirroring();
|
final ColorFilter colorFilter = (mColorFilter == null ? mTintFilter : mColorFilter);
|
||||||
|
|
||||||
canvas.translate(bounds.left, bounds.top);
|
// The imageView can scale the canvas in different ways, in order to
|
||||||
|
// avoid blurry scaling, we have to draw into a bitmap with exact pixel
|
||||||
|
// size first. This bitmap size is determined by the bounds and the
|
||||||
|
// canvas scale.
|
||||||
|
canvas.getMatrix(mTmpMatrix);
|
||||||
|
mTmpMatrix.getValues(mTmpFloats);
|
||||||
|
float canvasScaleX = Math.abs(mTmpFloats[Matrix.MSCALE_X]);
|
||||||
|
float canvasScaleY = Math.abs(mTmpFloats[Matrix.MSCALE_Y]);
|
||||||
|
int scaledWidth = (int) (mTmpBounds.width() * canvasScaleX);
|
||||||
|
int scaledHeight = (int) (mTmpBounds.height() * canvasScaleY);
|
||||||
|
scaledWidth = Math.min(MAX_CACHED_BITMAP_SIZE, scaledWidth);
|
||||||
|
scaledHeight = Math.min(MAX_CACHED_BITMAP_SIZE, scaledHeight);
|
||||||
|
|
||||||
|
if (scaledWidth <= 0 || scaledHeight <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final int saveCount = canvas.save();
|
||||||
|
canvas.translate(mTmpBounds.left, mTmpBounds.top);
|
||||||
|
|
||||||
|
// Handle RTL mirroring.
|
||||||
|
final boolean needMirroring = needMirroring();
|
||||||
if (needMirroring) {
|
if (needMirroring) {
|
||||||
canvas.translate(bounds.width(), 0);
|
canvas.translate(mTmpBounds.width(), 0);
|
||||||
canvas.scale(-1.0f, 1.0f);
|
canvas.scale(-1.0f, 1.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Color filters always override tint filters.
|
// At this point, canvas has been translated to the right position.
|
||||||
final ColorFilter colorFilter = mColorFilter == null ? mTintFilter : mColorFilter;
|
// And we use this bound for the destination rect for the drawBitmap, so
|
||||||
|
// we offset to (0, 0);
|
||||||
|
mTmpBounds.offsetTo(0, 0);
|
||||||
|
|
||||||
|
mVectorState.createCachedBitmapIfNeeded(scaledWidth, scaledHeight);
|
||||||
if (!mAllowCaching) {
|
if (!mAllowCaching) {
|
||||||
// AnimatedVectorDrawable
|
mVectorState.updateCachedBitmap(scaledWidth, scaledHeight);
|
||||||
if (!mVectorState.hasTranslucentRoot()) {
|
|
||||||
mVectorState.mVPathRenderer.draw(
|
|
||||||
canvas, bounds.width(), bounds.height(), colorFilter);
|
|
||||||
} else {
|
|
||||||
mVectorState.createCachedBitmapIfNeeded(bounds);
|
|
||||||
mVectorState.updateCachedBitmap(bounds);
|
|
||||||
mVectorState.drawCachedBitmapWithRootAlpha(canvas, colorFilter);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// Static Vector Drawable case.
|
|
||||||
mVectorState.createCachedBitmapIfNeeded(bounds);
|
|
||||||
if (!mVectorState.canReuseCache()) {
|
if (!mVectorState.canReuseCache()) {
|
||||||
mVectorState.updateCachedBitmap(bounds);
|
mVectorState.updateCachedBitmap(scaledWidth, scaledHeight);
|
||||||
mVectorState.updateCacheStates();
|
mVectorState.updateCacheStates();
|
||||||
}
|
}
|
||||||
mVectorState.drawCachedBitmapWithRootAlpha(canvas, colorFilter);
|
|
||||||
}
|
}
|
||||||
|
mVectorState.drawCachedBitmapWithRootAlpha(canvas, colorFilter, mTmpBounds);
|
||||||
canvas.restoreToCount(saveCount);
|
canvas.restoreToCount(saveCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -770,10 +795,11 @@ public class VectorDrawable extends Drawable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void drawCachedBitmapWithRootAlpha(Canvas canvas, ColorFilter filter) {
|
public void drawCachedBitmapWithRootAlpha(Canvas canvas, ColorFilter filter,
|
||||||
|
Rect originalBounds) {
|
||||||
// The bitmap's size is the same as the bounds.
|
// The bitmap's size is the same as the bounds.
|
||||||
final Paint p = getPaint(filter);
|
final Paint p = getPaint(filter);
|
||||||
canvas.drawBitmap(mCachedBitmap, 0, 0, p);
|
canvas.drawBitmap(mCachedBitmap, null, originalBounds, p);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean hasTranslucentRoot() {
|
public boolean hasTranslucentRoot() {
|
||||||
@ -797,16 +823,15 @@ public class VectorDrawable extends Drawable {
|
|||||||
return mTempPaint;
|
return mTempPaint;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updateCachedBitmap(Rect bounds) {
|
public void updateCachedBitmap(int width, int height) {
|
||||||
mCachedBitmap.eraseColor(Color.TRANSPARENT);
|
mCachedBitmap.eraseColor(Color.TRANSPARENT);
|
||||||
Canvas tmpCanvas = new Canvas(mCachedBitmap);
|
Canvas tmpCanvas = new Canvas(mCachedBitmap);
|
||||||
mVPathRenderer.draw(tmpCanvas, bounds.width(), bounds.height(), null);
|
mVPathRenderer.draw(tmpCanvas, width, height, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void createCachedBitmapIfNeeded(Rect bounds) {
|
public void createCachedBitmapIfNeeded(int width, int height) {
|
||||||
if (mCachedBitmap == null || !canReuseBitmap(bounds.width(),
|
if (mCachedBitmap == null || !canReuseBitmap(width, height)) {
|
||||||
bounds.height())) {
|
mCachedBitmap = Bitmap.createBitmap(width, height,
|
||||||
mCachedBitmap = Bitmap.createBitmap(bounds.width(), bounds.height(),
|
|
||||||
Bitmap.Config.ARGB_8888);
|
Bitmap.Config.ARGB_8888);
|
||||||
mCacheDirty = true;
|
mCacheDirty = true;
|
||||||
}
|
}
|
||||||
|
@ -37,8 +37,8 @@ public class ScaleDrawableTests extends Activity {
|
|||||||
};
|
};
|
||||||
|
|
||||||
protected int icon = R.drawable.bitmap_drawable01;
|
protected int icon = R.drawable.bitmap_drawable01;
|
||||||
|
|
||||||
protected int vector_icon = R.drawable.vector_drawable16;
|
protected int vector_icon = R.drawable.vector_drawable16;
|
||||||
|
protected int animated_vector_icon = R.drawable.ic_hourglass_animation;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
@ -46,12 +46,12 @@ public class ScaleDrawableTests extends Activity {
|
|||||||
ScrollView scrollView = new ScrollView(this);
|
ScrollView scrollView = new ScrollView(this);
|
||||||
GridLayout container = new GridLayout(this);
|
GridLayout container = new GridLayout(this);
|
||||||
scrollView.addView(container);
|
scrollView.addView(container);
|
||||||
container.setColumnCount(3);
|
container.setColumnCount(4);
|
||||||
container.setBackgroundColor(0xFF888888);
|
container.setBackgroundColor(0xFF888888);
|
||||||
|
|
||||||
LayoutParams params = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
|
LayoutParams params = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
|
||||||
params.width = 400;
|
params.width = 300;
|
||||||
params.height = 300;
|
params.height = 200;
|
||||||
|
|
||||||
for (int i = 0; i < scaleTypes.length; i++) {
|
for (int i = 0; i < scaleTypes.length; i++) {
|
||||||
TextView t = new TextView(this);
|
TextView t = new TextView(this);
|
||||||
@ -71,6 +71,13 @@ public class ScaleDrawableTests extends Activity {
|
|||||||
view.setScaleType(scaleType);
|
view.setScaleType(scaleType);
|
||||||
view.setImageResource(vector_icon);
|
view.setImageResource(vector_icon);
|
||||||
container.addView(view);
|
container.addView(view);
|
||||||
|
|
||||||
|
ImageView avd_view = new ImageView(this);
|
||||||
|
avd_view.setLayoutParams(params);
|
||||||
|
avd_view.setScaleType(scaleType);
|
||||||
|
avd_view.setImageResource(animated_vector_icon);
|
||||||
|
container.addView(avd_view);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setContentView(scrollView);
|
setContentView(scrollView);
|
||||||
|
Reference in New Issue
Block a user