107

The images (icons) come in roughly the same size, but I need to resize them in order for the buttons to remain the same height.

How do I do this?

Button button = new Button(this);
button.setText(apiEventObject.getTitle());
button.setOnClickListener(listener);

/*
 * set clickable id of button to actual event id
 */
int id = Integer.parseInt(apiEventObject.getId());
button.setId(id);

button.setLayoutParams(new LayoutParams(
        android.view.ViewGroup.LayoutParams.FILL_PARENT,
        android.view.ViewGroup.LayoutParams.WRAP_CONTENT));

Drawable drawable = LoadImageFromWebOperations(apiSizeObject.getSmall());
//?resize drawable here? drawable.setBounds(50, 50, 50, 50);
button.setCompoundDrawablesWithIntrinsicBounds(drawable, null, null, null);
weston
  • 54,145
  • 21
  • 145
  • 203
  • Have you found the way how to resize drawable (Bitmap)? – Zelimir Feb 01 '11 at 20:42
  • 2
    Way late, but wondering why you weren't calling `setCompoundDrawables()`? Intrinsic refers to the original image size in other places within Android, e.g. `Drawable.getIntrinsicHeight()`. – William T. Mallard Nov 22 '13 at 09:14

16 Answers16

177

The setBounds() method doesn't work for every type of container (did work for some of my ImageView's, however).

Try the method below to scale the drawable itself:

// Read your drawable from somewhere
Drawable dr = getResources().getDrawable(R.drawable.somedrawable);
Bitmap bitmap = ((BitmapDrawable) dr).getBitmap();
// Scale it to 50 x 50
Drawable d = new BitmapDrawable(getResources(), Bitmap.createScaledBitmap(bitmap, 50, 50, true));
// Set your new, scaled drawable "d"
Andy
  • 455
  • 6
  • 12
Eric Chen
  • 3,562
  • 7
  • 39
  • 58
  • for me the problem here, is that it draws a white rectangle around the drawable image – noloman May 25 '12 at 13:31
  • 8
    The BitmapDrawable(Bitmap) constructor is deprecated. Use: Drawable d = new BitmapDrawable(getResources(), Bitmap.createScaledBitmap(bitmap, 50, 50, true)); – Andy Oct 10 '12 at 16:56
  • 2
    This will create pixelation when scaling up drawables. Even if they are vector drawables. – Sanket Berde Mar 16 '17 at 18:11
  • 1
    probably want to use `ContextCompat.getDrawable(context, resourceid)` – Pierre May 24 '18 at 10:27
  • As a side note, you can create `StateListDrawable` programmitically and use `addState` method with that "converted drawable" to make it works for `selector's item` size used in `setPasswordVisibilityToggleDrawable`. – 林果皞 Jan 09 '19 at 20:12
  • 1
    casting the Drawable to a BitmapDrawable causes cast exception on Android 10, 9 & 8. It doesn't cause the exception on Android 5. I didn't try on 6 & 7. – Ashraf Alshahawy Jan 13 '20 at 12:35
34

Specify the size with setBounds(), ie for a 50x50 size use

drawable.setBounds(0, 0, 50, 50);

public void setBounds (int left, int top, int right, int bottom)

CSchulz
  • 10,882
  • 11
  • 60
  • 114
jkhouw1
  • 7,320
  • 3
  • 32
  • 24
  • 2
    After SetBounds size remains the same. May be some invalidate needed? – Kostadin Feb 15 '12 at 10:46
  • 11
    Actually, setBounds works for GradientDrawables. It just doesn't work for Image Drawables. – gregm Jul 27 '12 at 15:21
  • It worked for me when I was putting the image in a button, but not when I put it in an ImageView. The OP was using a button, but also calling the intrinsic flavor of the `setCompoundDrawables()` function. – William T. Mallard Nov 22 '13 at 09:17
  • @gregm Interestingly, you can also set the size for a GradientDrawable using setSize(). – 6rchid Feb 05 '20 at 18:56
15

i didn't have time to dig why the setBounds() method is not working on bitmap drawable as expected but i have little tweaked @androbean-studio solution to do what setBounds should do...

/**
 * Created by ceph3us on 23.05.17.
 * file belong to pl.ceph3us.base.android.drawables
 * this class wraps drawable and forwards draw canvas
 * on it wrapped instance by using its defined bounds
 */
public class WrappedDrawable extends Drawable {

    private final Drawable _drawable;
    protected Drawable getDrawable() {
        return _drawable;
    }

    public WrappedDrawable(Drawable drawable) {
        super();
        _drawable = drawable;
    }

    @Override
    public void setBounds(int left, int top, int right, int bottom) {
        //update bounds to get correctly
        super.setBounds(left, top, right, bottom);
        Drawable drawable = getDrawable();
        if (drawable != null) {
            drawable.setBounds(left, top, right, bottom);
        }
    }

    @Override
    public void setAlpha(int alpha) {
        Drawable drawable = getDrawable();
        if (drawable != null) {
            drawable.setAlpha(alpha);
        }
    }

    @Override
    public void setColorFilter(ColorFilter colorFilter) {
        Drawable drawable = getDrawable();
        if (drawable != null) {
            drawable.setColorFilter(colorFilter);
        }
    }

    @Override
    public int getOpacity() {
        Drawable drawable = getDrawable();
        return drawable != null
                ? drawable.getOpacity()
                : PixelFormat.UNKNOWN;
    }

    @Override
    public void draw(Canvas canvas) {
        Drawable drawable = getDrawable();
        if (drawable != null) {
            drawable.draw(canvas);
        }
    }

    @Override
    public int getIntrinsicWidth() {
        Drawable drawable = getDrawable();
        return drawable != null
                ? drawable.getBounds().width()
                : 0;
    }

    @Override
    public int getIntrinsicHeight() {
        Drawable drawable = getDrawable();
        return drawable != null ?
                drawable.getBounds().height()
                : 0;
    }
}

usage:

// get huge drawable 
final Drawable drawable = resources.getDrawable(R.drawable.g_logo);
// create our wrapper           
WrappedDrawable wrappedDrawable = new WrappedDrawable(drawable);
// set bounds on wrapper 
wrappedDrawable.setBounds(0,0,32,32); 
// use wrapped drawable 
Button.setCompoundDrawablesWithIntrinsicBounds(wrappedDrawable ,null, null, null);

results

before: enter image description here after: enter image description here

Dr.jacky
  • 3,341
  • 6
  • 53
  • 91
ceph3us
  • 7,326
  • 3
  • 36
  • 43
  • how to add padding left? – reegan29 Jan 07 '20 at 09:21
  • 1
    I don't know why it works, but it works. This is the only solution that worked for me. – arenaq Apr 29 '21 at 11:46
  • 1
    This should be the accepted answer. Cheers – Sahil Garg Jun 01 '21 at 09:27
  • 1
    @arenaq it works due to overriding the intrinsic width and height. That's what's missing from the other setBounds solutions - if bounds are set without overriding width and height it'll still be dropped. – 0101100101 Aug 07 '23 at 00:58
  • If you do this, please use a subclass of `androidx.appcompat.graphics.drawable.DrawableWrapperCompat` and override whichever methods you need. The rest will be nicely delegated for you. – TWiStErRob Aug 27 '23 at 09:16
14

Before apply .setBounds(..) try to convert current Drawable into ScaleDrawable

drawable = new ScaleDrawable(drawable, 0, width, height).getDrawable();

after that

drawable.setBounds(0, 0, width, height);

will work

iBog
  • 2,245
  • 1
  • 20
  • 15
9

To use

textView.setCompoundDrawablesWithIntrinsicBounds()

Your minSdkVersion should be 17 in build.gradle

    defaultConfig {
    applicationId "com.example..."
    minSdkVersion 17
    targetSdkVersion 25
    versionCode 1
    versionName "1.0"
}

To change drawable size:

    TextView v = (TextView)findViewById(email);
    Drawable dr = getResources().getDrawable(R.drawable.signup_mail);
    Bitmap bitmap = ((BitmapDrawable) dr).getBitmap();
    Drawable d = new BitmapDrawable(getResources(), Bitmap.createScaledBitmap(bitmap, 80, 80, true));

    //setCompoundDrawablesWithIntrinsicBounds (image to left, top, right, bottom)
    v.setCompoundDrawablesWithIntrinsicBounds(d,null,null,null);
Semih Fatih
  • 91
  • 1
  • 5
4

Got this working using LayerDrawable:

fun getResizedDrawable(drawable: Drawable, scale: Float) =
    LayerDrawable(arrayOf(drawable)).also { it.setLayerSize(0, (drawable.intrinsicWidth * scale).toInt(), (drawable.intrinsicHeight * scale).toInt()) }

fun getResizedDrawable(drawable: Drawable, scalex: Float, scaleY: Float) =
    LayerDrawable(arrayOf(drawable)).also { it.setLayerSize(0, (drawable.intrinsicWidth * scalex).toInt(), (drawable.intrinsicHeight * scaleY).toInt()) }

fun getResizedDrawableUsingSpecificSize(drawable: Drawable, newWidth: Int, newHeight: Int) =
    LayerDrawable(arrayOf(drawable)).also { it.setLayerSize(0, newWidth, newHeight) }

Example:

val drawable = AppCompatResources.getDrawable(this, android.R.drawable.sym_def_app_icon)!!
val resizedDrawable = getResizedDrawable(drawable, 3f)
textView.setCompoundDrawablesWithIntrinsicBounds(resizedDrawable, null, null, null)
imageView.setImageDrawable(resizedDrawable)
android developer
  • 114,585
  • 152
  • 739
  • 1,270
3

Use the post method to achieve the desired effect:

{your view}.post(new Runnable()
    {
        @Override
        public void run()
        {
            Drawable image = context.getResources().getDrawable({drawable image resource id});
            image.setBounds(0, 0, {width amount in pixels}, {height amount in pixels});
            {your view}.setCompoundDrawables(image, null, null, null);
        }
    });
OneEyeQuestion
  • 742
  • 7
  • 22
3

Probably a little late. But here is the solution that finally worked for me in every situation.

The idea is to create a custom drawable with fixed intrinic size and pass the drawing job on to the original drawable.

Drawable icon = new ColorDrawable(){
        Drawable iconOrig = resolveInfo.loadIcon(packageManager);

        @Override
        public void setBounds(int left, int top, int right, int bottom){
            super.setBounds(left, top, right, bottom);//This is needed so that getBounds on this class would work correctly.
            iconOrig.setBounds(left, top, right, bottom);
        }

        @Override
        public void draw(Canvas canvas){
            iconOrig.draw(canvas);
        }

        @Override
        public int getIntrinsicWidth(){
            return  mPlatform.dp2px(30);
        }

        @Override
        public int getIntrinsicHeight(){
            return  mPlatform.dp2px(30);
        }
    };
Androbean Studio
  • 382
  • 2
  • 11
3

It's been a while since the question was asked
but is still unclear for many how to do this simple thing.

It's pretty simple in that case when you use a Drawable as a compound drawable on a TextView (Button).

So 2 things you have to do:

1.Set bounds:

drawable.setBounds(left, top, right, bottom)

2.Set the drawable appropriately (without using of intrinsic bounds):

button.setCompoundDrawablesRelative(drawable, null, null, null)
  • No need to use Bitmaps
  • No workarounds such as ScaleDrawable ColorDrawable or LayerDrawable (what are definitely created for other purposes)
  • No need in custom drawables!
  • No workarounds with the post
  • It's a native and simple solution, just how Android expects you to do.
Leo DroidCoder
  • 14,527
  • 4
  • 62
  • 54
  • How can I scale the drawable to match the text size of the TextView? What should be the bounds to do that? – Ashwin May 31 '23 at 11:50
2

jkhouw1 answer is correct one, but it lacks some details, see below:

It is much easier for at least API > 21. Assume that we have VectorDrawable from resources (example code to retrieve it):

val iconResource = context.resources.getIdentifier(name, "drawable", context.packageName)
val drawable = context.resources.getDrawable(iconResource, null)

For that VectorDrawable just set desired size:

drawable.setBounds(0, 0, size, size)

And show drawable in button:

button.setCompoundDrawables(null, drawable, null, null)

That's it. But note to use setCompoundDrawables (not Intrinsic version)!

2

You can create an extension for that if you're using Kotlin

fun Drawable.resizeTo(context: Context, size: Int) =
    BitmapDrawable(context.resources, toBitmap(size, size))
Tidder Jail
  • 472
  • 7
  • 11
0

You can try button.requestLayout(). When the background size is changed, it needs to remeasure and layout, but it won't do it

Xiaomei
  • 1
  • 1
0

Example of how to scale Drawable by percentage:

// If used for TextView, must be set by setCompoundDrawable(...) without intrinsic bounds
// because already have bounds.
fun Drawable.setScaledBoundsBy(percent: Float) = apply {
    setBounds(0, 0, intrinsicWidth.times(percent).toInt(), intrinsicHeight.times(percent).toInt())
}
Krzysiulele
  • 346
  • 5
  • 11
0

You can create a subclass of the view type, and override the onSizeChanged method.

I wanted to have scaling compound drawables on my text views that didn't require me to mess around with defining bitmap drawables in xml, etc. and did it this way:

public class StatIcon extends TextView {

    private Bitmap mIcon;

    public void setIcon(int drawableId) {
    mIcon = BitmapFactory.decodeResource(RIApplication.appResources,
            drawableId);
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        if ((w > 0) && (mIcon != null))
            this.setCompoundDrawablesWithIntrinsicBounds(
                null,
                new BitmapDrawable(Bitmap.createScaledBitmap(mIcon, w, w,
                        true)), null, null);

        super.onSizeChanged(w, h, oldw, oldh);
    }

}

(Note that I used w twice, not h, as in this case I was putting the icon above the text, and thus the icon shouldn't have the same height as the text view)

This can be applied to background drawables, or anything else you want to resize relative to your view size. onSizeChanged() is called the first time the View is made, so you don't need any special cases for initialising the size.

Zulaxia
  • 2,702
  • 2
  • 22
  • 23
-1

You can use LayerDrawable from only one layer and setLayerInset method:

Drawable[] layers = new Drawable[1];
layers[0] = application.getResources().getDrawable(R.drawable.you_drawable);

LayerDrawable layerDrawable = new LayerDrawable(layers);
layerDrawable.setLayerInset(0, 10, 10, 10, 10);
Valery Miller
  • 804
  • 1
  • 9
  • 13
-47
Button button = new Button(this);
Button = (Button) findViewById(R.id.button01);

Use Button.setHeight() or Button.setWeight() and set a value.

CSchulz
  • 10,882
  • 11
  • 60
  • 114
user564612
  • 45
  • 1
  • 8
  • 9
    that just sets the button height, not the height of the drawable. i want to set the width/height of the drawable (especially if it's larger than the set button height). –  Jan 05 '11 at 22:03
  • 26
    You know you can delete the answer, don't you? – Iharob Al Asimi Aug 16 '15 at 15:58