When reading questions on SO and "supporting multiple Android screens", it always says to place your images in 4 drawables. But wouldn't it work if you place the image in the highest resolution (say xxxhdpi) and let Android scales it down? . Scaling down will barely be visible ( at least from what I have seen) and saves the app a ton of redundant space
Thank you
You can place all your images to only xxxhdpi drawable folder. It works but on the lower dpi devices, it may cause memory/performance issues. And also bad scaling image quality at some cases.
Assuming you have all images into drawable-xxhdpi folder; if you run your app on a lower dpi device, scaling is inevitable and it it costs much more memory usage.
Scaling is proceed at runtime. But if you resize them before runtime, there is no need to scale them again and again. It makes your app faster and smoother, use less memory.
If you want more performance and no memory issues, resize your drawables and put other drawable folders.
Related
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.
I don't get why it's recommended to have a drawable (image) for all densities (ldpi, ..., xxhdpi).
Would it not be better to add one file with the highest needed density (xxxhdpi) and than programmatically scale that image down (e.g. for tab icons I could just set a explicit dp size)?
Therefore I would only have to manage one file and the APK file size would be smaller. I think the performance disadvantage should not be significant.
Update 1:
To be more specific: I never noticed any quality loss when using a high density image (PNG file) which was scaled down to a explicit dp value on my mdpi device.
So I was considering if the disadvantages of managing multiple image files as a coder without a designer (and the higher APK file size) might outweigh the advantages. Especially if I'm going to target newer devices (API >= 17).
Update 2:
In my case I'm more a coder than a designer. In the microphone example image of #mes I could just use a high density version of the left microphone and scale it down with no significant disadvantages?
There's no significant advantage, when designer or you simply reduce image size and put inside the ldpi, actual meaning is to paint another image for small size, because there are many cases, when simply reducing an image size produces inacurate and low quality image, and a good designer will draw another image, with few details in it, this is an example, only in this case worth to put different images in different folders
Therefore I would only have to manage one file and the APK file size would be smaller. I think the performance disadvantage should not be significant.
Yes, you are right, there no significant performance improvements, and also apk size is increases, that's why not worth to downscale the same image.
There are two reasons for this:
Typically, devices with lower density screens also tend to have less powerful hardware for image & graphics processing, along with lower memory.
Its not just the density, it is also the file size and image dimensions that are scaled accordingly.
The Android way of having different drawables for different screen form factors is thus appropriate from a technical standpoint.
First advantage is what mes mentioned: different images for different sizes.
Second advantage is memory/code efficiency: a 1000x1000 pixels image on android will take 1000x1000x4 ~= 3.8 mb of memory which is really wasted on smaller screens which can have 16mb available memory per app. You could try loading the bitmaps efficiently but that's a lot of code to put/maintain for each image and also will have performance issues on some cases.
A lot of blogs and best practices for Android Development says
"You don't need to supply bitmaps for every possible density, Android will scale your bitmaps (typically when they are loaded) to match the current density."
Reference Link : https://plus.google.com/105051985738280261832/posts/6eWwQvFGLV8
I tried it works well.
But i couldn't understand one thing.
For instance i created a sample app with 5 full screen images of around 1.5 MB each in a view pager.
I created heavy images for 7 inch MDPI devices like Galaxy Tab 2 and put them in "drawable-large-mdpi" folder. It was bit jerky but didn't crash and let me scroll through all the images.
Now i tried to use the app on my Nexus 7 which is almost a large HDPI device. The app crashed with "OOM error" while decoding the bitmap.
*If i move the images from large MDPI to large HDPI, it works well on both the devices with out any crash. *
So i have two questions.
Does this result incline towards, we can only put the graphics assets in the highest density drawable folder and let it scale down automatically in its range ?
What happens internally why it crashed for the first time ?
That would be one way to do it so long as the drawables look fine when scaled. Scaling down in size usually is better than scaling up. That said, there is this line:
Even so, when bitmaps are scaled to a density there isn't a design
for, you may get artifacts such as softening of the edges.
The reason scaling from HDPI to TVDPI works fine is because the two densities are so close. Problems with some images will start showing up between the major densities. For example, jumping from HDPI to LDPI will sure result in many artifacts as the image was simply not designed for low-resolutions.
It's most likely crashing with an OOM in the Nexus 7 because it's taking your large images that the device thinks are MDPI images and scaling them up to TVDPI settings. This would result in an even bigger image.
When you put them in the HDPI folder, you're telling it to scale down to TVDPI so the resulting image is a less memory footprint.
There's also the overhead that comes in to play in the actual scaling.
The same thing will probably happen when you scale up to an XHDPI device.
No, you can put graphic assets into any drawable folder. If you only put resource in one density folder, the system will scale up and down to the target device densities.
You wanted to use a lot of memory with using those large images at the same time. Even if you use compressed image formats, when the system draws it on the screen, it will be in the memory as a bitmap. So if you keep 4-5 1200x800 pixel image in the memory at the same time it will use up that amount of memory in bitmap. That crashed your app because it ran out of the granted memory (which is very low by default android only ensure 16 mb which is usually broadened by manufacturers).
When you put the images in the hdpi, system scaled down the images, thats why there could be (not guaranteed) enough memory for the smaller images.
Solution: don't use that big images as backgrounds. Try nicely scalable images, and check out the 9-patch format: link
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.
I'm quite new to Android development. My understanding is that you can create several versions of the same image with different sizes and put them into the folders drawable-ldpi, drawable-mdpi, drawable-hdpi.
It seems obvious to me that you can handle this problem "the lazy way" by just resizing one image depending on the device's pixel density. For this I programmatically find out what density the device has, like ldpi. The implementation itself is not the problem. I'm just afraid of any drawbacks (that prevent me later from running the app on different devices).
So, are there any (major) drawbacks of scaling images automatically ?
In which of the three folders do I put the image so that the compiler can find it?
You would put the image in your regular drawable folder. That way any phone can find it.
While you can programatically shrink images, shrinking usually has the effect of reducing image detail and causing jaggies.
Adding in smaller assets will also reduce memory usage on smaller phones. Keep in mind that some Android phones are notoriously bad with memory (see: HTC Status), so any and all savings help.
I would recommend just photoshop scaling images down large images yourself. For smaller images, it is not as big a deal.
Android does auto-scale and it works fine in some cases, but it doesn't work for many, notably small images with important details like text. Scaling a larger image down blurs those details. Scaling a smaller image up is worse. This is why icons files have been multi-resolution since the very early days of GUIs. To wit, text scaling is hugely complicated. Ask any font designer, and note Adobe built a company on algorithms to do it automatically.
it's on mdpi folder. It'll change the size automatically. But is not recommended since the image quality drops.