9

As the parameterless BitmapDrawableconstructor is deprecated, we have to give resource id to the constructor.

BitmapDrawable bitmapDrawable = new BitmapDrawable(res, bmap);

Where res is generally getResources().

Why does the constructor need it and how can we define that value if we are using a general image cache?

azizbekian
  • 60,783
  • 13
  • 169
  • 249
frankish
  • 6,738
  • 9
  • 49
  • 100
  • as far as I understand from the API: "Also see the Bitmap class, which handles the management and transformation of raw bitmap graphics, and should be used when drawing to a Canvas.", You should use Bitmap instead of BitmapDrawable if You have a image not defined in Resources. – Opiatefuchs Feb 17 '15 at 08:16
  • It seems like even `setImageBitmap` uses `BitmapDrawable` internally. (`ImageView.class`) – frankish Feb 17 '15 at 08:20

3 Answers3

6

Why does the constructor need it?

From the docs of BitmapDrawable(Resources res, Bitmap bitmap):

Create drawable from a bitmap, setting initial target density based on the display metrics of the resources.

So, that is needed in order to set an initial target density using the display metrics, and the DisplayMetrics would be fetched from the Resources that you are providing as a parameter to BitmapDrawable.

How can we define that value if we are using a general image cache?

Sorry, cannot understand the question. Can you rephrase?

If I have already scaled the Bitmap myself, why does BitmapDrawable need resources?

How can BitmapDrawable know, that you have already scaled the Bitmap? Normally, if you are creating a BitmapDrawable you shouldn't take care of scaling it on your own, that's why the API is designed that way.

It seems like the only way to avoid scaling when converting a Bitmap to a Drawable is to use the deprecated constructor?

Although it sounds bad, but you can see how classes from transition framework use that constructor, e.g. ChangeBounds, Crossfade.

I've dug sources a bit and found out this chunk of code in Bitmap class:

static public int scaleFromDensity(int size, int sdensity, int tdensity) {
    if (sdensity == DENSITY_NONE || tdensity == DENSITY_NONE || sdensity == tdensity) {
        return size;
    }

    // Scale by tdensity / sdensity, rounding up.
    return ((size * tdensity) + (sdensity >> 1)) / sdensity;
}

This is being called in following chain:

* BitmapDrawable#constructor
* BitmapDrawable#updateLocalState()
* BitmapDrawable#computeBitmapSize()
* Bitmap#getScaledWidth()
* scaleFromDensity(getWidth(), mDensity, targetDensity)

What if you explicitly set the density of your Bitmap to DENSITY_NONE? Then the if check will evaluate to true and no scaling would be performed.

Bitmap bitmap = ...
bitmap.setDensity(Bitmap.DENSITY_NONE);

Haven't tested, just making assumptions based on sources.

azizbekian
  • 60,783
  • 13
  • 169
  • 249
  • Thanks for this information. I completely missed the fact that Bitmaps have a density property. GetDensity for a bitmap by default "is the same density as the current display" https://developer.android.com/reference/android/graphics/Bitmap.html#getDensity() so it appears that no scaling would be performed on the BitmapDrawable unless you explicitly set a density on the Bitmap. That makes sense to me now! – Nate Cook Jul 31 '17 at 18:49
3

In this constructor getResources() is needed "to set initial target density based on the display metrics of the resource". I understand it this way - you take the display metrics, that contain information about size, density, font scaling, and pass this info to constructor. Probably if you will have in your res/ folder image in different sizes - in appropriate sub folders, like drawable-ldpi, drawable-hdpi - you will get different information.

To use it in image cache, you need to pass a Context to it, and then invoke like this: Context ctx; ctx.getResources(); Hope that helps.

0

Regarding Constructor deprecated and new constructor

BitmapDrawable(Resources res) /* deprecated constructor */

BitmapDrawable(android.content.res.Resources, android.graphics.Bitmap) /* new constructor they introduced to replace above deprecated constructor */

The deprecated constructor creates an empty drawable, not dealing with density. But, the new constructor creates drawable from a bitmap, setting initial target density based on the display metrics of the resources. Using the new constructor gives you all the power that you can have with Bitmap (final class). https://developer.android.com/reference/android/graphics/Bitmap.html#pubmethods

What happens if you don't pass Bitmap variable via constructor? When you pass a variable from the parameter, you don't need to create new bitmap variable there where you passed. You created another variable which just does reference (static reference) of your Bitmap. Since bitmaps are large size files, when you pass reference you save a lot of space and processing power that you had to use loading bitmaps inside your ImageView. so it helps you don't get OutOfMemory Exception.

Eg,

public void passA(String); /* method signature*/
String s = "dfsfsdf";
passA(s); /* method calling */

Another side:

public void passA(String newVar) {
   /* newVar simply points (references) to the memory of "dfsfsdf". */
   /* passing references */ /* for Bitmap creating new copy is expensive 
    operations */
/*Note: Java does manipulate objects by reference, and all object variables are references */
}

Another idea they passed Bitmap variable (android.graphics.Bitmap ) is because Bitmap is Parcelable (if implements Parcelable). Parcelable objects can be contained in Bundle. And Bundles are the core things for IPCS (Interprocess communication), and communication via various Android Components).

Regarding the general Image Cache You setup all the necessary density and other things from the Bitmap passed a variable via new constructor. Then you can create a HashMap in Least Recently Used (Lru) cache where you define your String key to point out configured Bitmap variable. For eg, LruCache. Now, in the Least Recently Cache of your processor, you have configured Bitmap. This saves your a lot of processing power.

https://developer.android.com/reference/android/util/LruCache.html

Hope, my answer helps you!

Uddhav P. Gautam
  • 7,362
  • 3
  • 47
  • 64