Suppose there is a huge bitmap, huge_bitmap
, in the drawable-nodpi
folder. I want to create a custom view with huge_bitmap
as its background:
public class CustomView extends View {
private Bitmap mBackground;
public CustomView(Context context) {
super(context);
}
public CustomView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CustomView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
mBackground = decodeAndResizeFromResource(getResources(),
R.drawable.huge_bitmap, getMeasuredWidth(), getMeasuredHeight());
}
@Override
protected void onDraw(Canvas canvas) {
canvas.drawBitmap(mBackground, 0, 0, mPaint);
super.onDraw(canvas);
}
}
where decodeAndResizeFromResource
method subsamples the image and loads a smaller, scaled version into memory.
This works fine. However, onMeasure
will often be invoked more than once. Lint warns about this:
Avoid object allocations during draw/layout operations (preallocate and reuse instead)
Allocating objects in methods such as onMeasure
, onLayout
, onSizeChanged
and onDraw
should therefore be avoided.
How do I correctly fix the example above?
I obviously don’t want to preallocate the bitmap (assume it will throw an OutOfMemoryError
). Since I need the view’s measured width and height in order to subsample/scale accordingly, I need to load the bitmap after measuring.
Here are two possible workarounds:
@Override
protected void onDraw(Canvas canvas) {
if (mBackground == null) {
mBackground = decodeAndResizeFromResource(getResources(),
R.drawable.huge_bitmap, getMeasuredWidth(), getMeasuredHeight());
}
canvas.drawBitmap(mBackground, 0, 0, mPaint);
super.onDraw(canvas);
}
or maybe
private int mWidth, mHeight;
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
final int w = getMeasuredWidth();
final int h = getMeasuredHeight();
if (mWidth != w || mHeight != h) {
mBackground = decodeAndResizeFromResource(getResources(),
R.drawable.huge_bitmap, w, h);
mWidth = w;
mHeight = h;
}
}
but both feel clumsy. What would you recommend?
view.setBackgroundResource(R.drawable.huge_bitmap);
in the Activity'sonResume
method, doesn't Android take care of the scaling itself? \$\endgroup\$OutOfMemoryError
. \$\endgroup\$