I have an ImageView
, full width, and width:height = 3:1, let's call it 1200 x 400. I want to show two Drawable
s in the ImageView
, both of which are only known at runtime. One of the drawables should be placed into the ImageView
according to "center-crop" (assuming the drawable has width:height > 3, that means making the width fit exactly and cropping top and bottom), the other one should be centered and scaled by a custom factor.
I have some code that does this, but it seems unnecessarily complicated to me; I only could get it to work as wanted when I create a new Bitmap
from the LayerDrawable
, then a BitmapDrawable
from that, and set the desired bounds again on the BitmapDrawable
, although I had already set the bounds on the LayerDrawable
- but those bounds are just ignored if I don't perform the extra step. I'd much prefer to generate the LayerDrawable
in such a way that I can use it as it is in .setImageDrawable()
, but I don't know how. What Android does if I try does not make any sense to me. What am I doing wrong?
This is how I make the LayerDrawable
double divide(int k, int n) {
return ((double)k)/n;
}
int verticalPaddingForHorizontalFit(int viewWidth, int viewHeight, int drawableWidth, int drawableHeight){
// you want to draw a drawable into a view, with an exact match in the width. Then either [1] or [2]
// [1] the view is taller than the drawable (i.e., height/width bigger for the view)
// --> method result is positive,
// and gives the amount of padding top and bottom
// [2] the drawable is taller
// --> method result is negative,
// and gives (minus) the amount that needs to be clipped from top and bottom
// such that the drawable is vertically centered
double viewAspect = divide(viewHeight, viewWidth );
double drawableAspect = divide(drawableHeight, drawableWidth);
return (int)Math.round(0.5 * viewWidth * (viewAspect - drawableAspect));
}
int[] paddingWhenCenteredAt(int viewWidth, int viewHeight, int drawableWidth, int drawableHeight, double drawableScale, int centerX, int centerY){
// scale the drawable with drawableScale, and put it into the view
// such that the center of the drawable has coordinates (centerX, centerY)
// return the padding needed as array of left, top, right, bottom, in that order
// negative values indicating clipping instead of padding
double w = drawableScale * drawableWidth;
double h = drawableScale * drawableHeight;
double left = centerX - 0.5*w;
double right = viewWidth - (centerX + 0.5*w);
double top = centerY - 0.5*h;
double bottom = viewHeight - (centerY + 0.5*h);
return new int[]{(int)Math.round(left), (int)Math.round(top), (int)Math.round(right), (int)Math.round(bottom)};
}
LayerDrawable makeLayerDrawable(Resources r, int outputWidth, int outputHeight, Bitmap bm1, Bitmap bm2){
Drawable[] layers = new Drawable[2];
BitmapDrawable bmd1 = new BitmapDrawable(r, bm1);
int width1 = bmd1.getIntrinsicWidth();
int height1 = bmd1.getIntrinsicHeight();
layers[0] = bmd1;
BitmapDrawable bmd2 = new BitmapDrawable(r, bm2);
int width2 = bmd2.getIntrinsicWidth();
int height2 = bmd2.getIntrinsicHeight();
layers[1] = bmd2;
LayerDrawable result = new LayerDrawable(layers);
int vPad = verticalPaddingForHorizontalFit(outputWidth, outputHeight, width1, height1);
result.setLayerInset(0, 0, vPad, 0, vPad);
int[] ltrb = paddingWhenCenteredAt(outputWidth, outputHeight, width2, height2, 0.5, outputWidth/2, outputHeight/2);
result.setLayerInset(1, ltrb[0], ltrb[1], ltrb[2], ltrb[3]);
result.setBounds(0, 0, outputWidth, outputHeight);
return result;
}
(I tested with Bitmap bm1 2400 x 1200 pixels and Bitmap bm2 800 x 300 pixels.)
Now, if I just use that LayerDrawable
, like so
myImageView.setImageDrawable(layd);
the ImageView
will not have the desired size (height changes.) If I set the layout again with LayoutParameters
, I can prevent that, but then, the drawables are not correctly shown. If instead, I do this
LayerDrawable layd = makeLayerDrawable(r, outputWidth, outputHeight, bm1, bm2);
Bitmap b = Bitmap.createBitmap(outputWidth, outputHeight, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(b);
layd.draw(canvas);
BitmapDrawable result = new BitmapDrawable(getResources(), b);
result.setBounds(0, 0, outputWidth, outputHeight);
myImageView.setImageDrawable(result);
it works.
Here is the complete code on github