Why provide multiple drawables for various densities? - android

My app supports various screen densities from ldpi to xhdpi. It contains some drawables (images) that need to be displayed properly on all densities.
I'm wondering how many various resolutions of the drawables are actually necessary to provide, considering that Android runtime is scaling resources when it cannot find one for the appropriate density.
If you provide only mdpi drawables, they will be scaled up on hdpi/xhdpi (blurry) and scaled down on ldpi screen.
However, if you provide only xhdi resources, then they will be scaled down on all lower density screens. Scaling down doesn't make them blurry and they will look fine.
So why bother providing drawables for other densities than xhdpi?

There are several reasons to used prescaled drawables:
Scaling down to lower resolutions takes resources (CPU time, memory and it drains your battery faster), especially if you have a lot of images this might be undesired.
Scaling down a large image might result in worse quality than using a 'pre-tuned' smaller image. Especially if your images are based on a vector original. Or they contain very fine lines or details, which will get lost when due to the scaling.
For example:
becomes
See here for the source of these images and more information on scaling artifacts.
On lower resolutions the image pixels are larger (or the image gets smaller), so it might be required to remove certain aspects of the image to keep it clear/understandable.

Your assumption about scaling resources isn't correct. Some large images may still look nice when they are scaled down, but this isn't the general case. If the image has smooth transitions and no gradients, it will be nicely scaled down. Images that contain sharp transitions or contain text for example will look ugly in lower resolutions.

If you make the scaling down yourself the result might be better compared to auto scaling plus your app doesnt consume resources for scaling if the drawables are prepared.

Not necessary but for example, you can use different layouts with different drawables for different screen resolutions. You have the option to do it. If you and your users are satisfied with the result, then cool. If you need different behavior in different densities and resolutions, then this feature comes really useful.

Related

Clarifications about android drawable folders

Until now, I did not pay too much attention to how to store drawable resources.
I usually generate multiple versions of an icon and store them under the drawable-mdpi, drawable-hdpi, drawable-xhdpi, ... folders.
For other images for which I don't have multiple versions, I inconsistently store them under drawable-nodpi or drawable.
However, I recently encountered an issue related that bring all my attention to that. I stored a 100KB image under the drawable folder. However, my app was regularly crashing, stating it could not allocate 18MB!
After some searches, the reason was that the image was scaled to fit the screen resolution and it resulted in a way heavier image. The fix was to move it under the drawable-nodpi folder which prevents that scaling.
So now, I am trying to better understand where I should locate my images and how this scaling effect works to optimize my app on that part.
I have done plenty of searches, but resources are limited or unclear on that subject and the official documentation kinda really sucks.
I am aware of the official explanation for the drawable or drawable-nodpi, but it does not clarify everything.
Typically:
How does the scaling work? Let's say I have res/drawable-mdpi/image.png. Does that mean the image is scaled if I have a screen different from mdpi, or will it also be scaled on mdpi screens resulting in possibly heavier image size on every device?
Following previous question, if the image is not scaled for mdpi screens but scaled for any others, then does that mean that if I provide a version of this image for every screen density, the scale will never happen? Or at the opposite, if the image is scaled also on mdpi devices, then having different version of the same image for each screen density will still scale the image, but using the version of the image matching the screen density of the device?
How to deal with icons for which I do not have multiple versions? I am afraid that if I put this single version in mdpi, it just scales it and uses so much more memory than necessary. In such case, should I put any icon for which I do not have different versions under nodpi?
On the other hand, if whenever you put an image in mdpi, hdpi, ... it scales it even on devices matching the density, then should I just move everything under nodpi or some high resolution like xxxhdpi where it can only be downscaled?
Thanks
Let's say I have res/drawable-mdpi/image.png. Does that mean the image is scaled if I have a screen different from mdpi
Yes.
or will it also be scaled on mdpi screens resulting in possibly heavier image size on every device?
No.
if I provide a version of this image for every screen density, the scale will never happen?
Yes. Your APK will be larger, due to the 7 copies of the drawable.
How to deal with icons for which I do not have multiple versions?
Option #1: Decide what density that particular version belongs in, and put it there. Android will upsample or downsample the image for devices operating at other densities.
Option #2: Put it in -anydpi or -nodpi, in which case Android will not upsample or downsample it. However, in this case, you need to be controlling the size of the image yourself (e.g., in the ImageView).
Option #3: Replace the icon with one that either you have all relevant densities or one that is an SVG that works as a vector drawable when imported using the Vector Asset Wizard in Android Studio.
The decision-making needs to be based both on memory consumption and what the result looks like. A low-memory solution that looks awful is unlikely to be a good choice.
should I just move everything under nodpi
Probably not.
or some high resolution like xxxhdpi where it can only be downscaled?
Probably not. It is unlikely that a massively downscaled version of your icon will look very good.

why must provide different images for different screen densities?

Why developers must provide different images for different screen densities (ldpi, mdpi, hdpi, xhdpi) ?
Why don't we just provide one big image for big density (e.g. hdpi or xhdpi) ?
There are couple of reasons why you might want to do this:
Resizing on the fly takes resources (CPU/memory) and thus slows your app
You might not be satisfied with resizing algorithm or know how to resize your image to get better end result (think of the case when you have vectors for your image)
you might want to use different image for different densisies at all (think of the case when your image contain text. You might want to ommit text on low density image if there is not enough space to show it)
If you do provide big images android will have to scale them wish isn't really optimal because it will waste energy and slow your app.

Drawbacks of keeping images into a single drawable folder (hdpi, mdpi or default)

I looked into several questions and answers but I still don't know exactly what could be the consequences of what I am doing on my app:
I setup a background image on my main activity, and I added this line to be sure the image will be displayed over the whole screen: setScaleType(ScaleType.FIT_XY) (my images can be a little stretched without any problem, it cannot be seen thanks to the textures used).
For now I only added the images in the xhdpi folder, since this density is the highest I plan to handle. Considering the images are automatically resized according to the screen, what is the point of having a specific image in every density folder?
An image in every folder makes the app size much bigger and it takes of course much more time to prepare.
What are the risks of not handling all the densities from xhdpi to lpdi?
EDIT:
Example below of an image I use as a pattern. FYI, I use this code to multiply the pattern:
<?xml version="1.0" encoding="utf-8"?>
<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
android:src="#drawable/butterfly"
android:tileMode="repeat" />
One downside of your approach is that sometimes larger icons need more details than smaller icons do. By using only one image for all densities you would also have the same amount of details in each case. Often, a lager and more detailed icon does not look good when scaled down (and vice-versa).
Nice article to read regarding this issue: http://www.pushing-pixels.org/2011/11/04/about-those-vector-icons.html
The devices with ldpi / mdpi are essentially low profile devices which have severely constrained resources like memory. If you are using large images for such devices, the issues you may face are:
An app that is slow to execute as large images have to be decompressed by the relatively less powerful CPUs
OutOfMemory errors due to the images not fitting the heap size of these devices.
Edit:
This answer assumes that the OP understands the reason for Providing Alternate Bitmaps and is only interested in knowing the risks in not following the Best Practices.
From here: http://developer.android.com/training/multiscreen/screendensities.html
Since Android runs in devices with a wide variety of screen densities, you should always provide your bitmap resources tailored to each of the generalized density buckets: low, medium, high and extra-high density. This will help you achieve good graphical quality and performance on all screen densities.

Drawable image scaling

If an asset is present in the drawable-xhdpi directory, do its equivalents need to be present in the drawable-hdpi/mdpi/ldpi directories as well in order to support those devices? Or, will Android downscale the xhdpi asset? If Android does downscale the asset, is there any benefit to using the lower dpi folders?
One benefit to using the lower folder is that you get control over how the image is downscaled. High resolution images may not necessarily look the way you want when they are reduced, it depends on the filtering algorithm used and the content of the image. If the high precision image has too many small details it may look odd or grainy at small resolutions.
By placing your own image in the ldpi folder, you get artistic control over exactly how the image looks. You may wish to use a simpler image with less high frequency content in the smaller image, so that it is clearer and easier to see and understand.
In addition to my comment on your question, I have a few reasons why you may want to include drawables in all folders:
If you have a drawable in xhdpi that's very high-res and has a lot of "activity" going on in it, it may look terrible and clustered when scaled down. Thus, you could create smaller, similar versions of that icon (named similarly across these density folders) that contain less "cluster" as you move down in size.
Different size layouts may want different size icons, and the scaling may not occur as you personally want it, so making your own scaled drawables would (mostly) prevent this.
Lint will yell at you if you don't put drawables in all the density folders.
If I think of any more reasons I'll add them.

Which screen size / density combinations should I support for bitmap background image?

I'm writing an application where I will need a number of full screen bitmap backgrounds. Based on my naive reading of Supporting Multiple Screens in the Android documentation, to cover all my bases I should probably have 16 versions of each bitmap: all pairs of [ small, normal, large, xlarge ] and [ ldpi, mdpi, hdpi, xhdpi ]. This should reduce the work the CPU has to do for scaling the images, but will come at great storage cost.
However, this seems wildly inefficient for two reasons:
Not all of these combinations are found in practice.
As I'm just rendering vector art for each physical size (without respect for DPI), pairs like large/mdpi and normal/hdpi (which are both ~ 480x854 pixels) are duplicate files.
So, should I just provide really large images and let the system scale them down? Bite the bullet and provide a lot of duplicate images? Avoid the issue altogether and cobble some code solution with raw resources? Any other ideas? Thanks.
EDIT: Apparently you can create XML bitmap drawables which alias an actual bitmap. That solves the second inefficiency argument. Still, I wonder, what combinations of these do others provide in practice?
You would only provide images at different resolutions if you want that piece of art to be a constant size, regardless of the screen dpi it is displayed on. An icon or a button would be an example of this.
For the background images described here, you don't care what the image dpi is -- you only care what the x * y dimensions are. So you don't have to produce the cross-product of sizes vs. dpi. You only need consider the 4 screen size categories.
And within the 4 screen size categories, you only need store one of the larger sizes (xlarge or large) and let the framework scale that up or down as needed. You can also do the scaling programmatically, to ensure that you don't change the aspect ratio of your background, and you don't crop it.
See also Android game working on all screen sizes which will hopefully attract a better answer.
I would advise providing images for ldpi, mdpi, hdpi and optionally xhdpi (depending on your target users). That will allow you to cover the most commonly used resolutions just fine.
If you end up with the feeling that your application is getting too large (either by deciding to add all possible image sizes or other reasons), you can also look into allowing your application to be moved to an SD card. That way, storage won't be much of an issue anymore. (http://developer.android.com/guide/appendix/install-location.html)

Categories

Resources