Mobile Apps

How to Use Density Specific Images

Mobile phones and tablets come in all shapes and sizes. This week we’ll look at how to prepare your app with images that match different screen densities. In a following posts, we will go through screen size specific images and resizable images. The more your images match a device’s screen, the better they look.

Example App

This blog post comes with an example app that we will be expanding in the next posts on images. It demonstrates all we’ll go through in this post.

screenshots

Screen density or DPI

Two screens of identical size can have different densities. This is known as DPI, which stands for Dots Per Inch. Devices can have any DPI, but both iOS and Android have defined generalized density categories.

DPI LDF iOS Android
~120 0.75 ldpi / low
~160 1 Default mdpi / medium
~240 1.5 hdpi / high
~320 2 Retina xhdpi
~480 3 HD Retina xxhdpi
~640 4 xxxhdpi

More abbreviations: D(I)P and LDF

In your app you will want to use the default D(I)P or Density-independent Pixel to set the exact size of all your views, be it ImageView or other. This basically means you layout for 160dpi, and let the operating system scale up/down based on the LDF or Logical Density Factor, which is DPI / 160.

NOTE: If you don’t set a width or height on your ImageView, it will display the actual size, which won’t be consistent across devices that don’t exactly match a generalized category.

Getting the screen density

In Titanium you can get all this information about the device via:

  • Ti.Platform.displayCaps.dpi
  • Ti.Platform.displayCaps.logicalDensityFactor
  • Ti.Platform.displayCaps.density

NOTE: The last property will not get you the same categories as in our table. On iOS you will get medium, high or xhigh, while on Android you get low, medium, high, xhigh or xxhigh.

Providing density specific images on iOS

iOS uses filename suffixes to differentiate between images for specific density categories. Use /images/image.png in an ImageView and Titanium will consider the following paths, given the device falls in that or a higher category.

Filename Category Devices
/images/image@3x.png HD Retina iPhone 6 Plus
/images/image@2x.png Retina iPhone >= 5
iPad >= 3
iPad Mini >= 2
iPad Air
/images/image.png Default Older

Providing density specific images on Android

Android uses folder names with so-called configuration qualifier names. As you can read in the Android API Guides there are many qualifiers, ranging from screen and input features to even day/night time.

You could have a folder as complex as:

/images/res-mcc310-mnc004-en-rUS-ldrtl-sw320dp-w720dp-h720dp-large-long-port-car-night-xxhdpi-finger-keyssoft-qwerty-navexposed-trackball-v7/image.png

In reality, you will just use a few. We’ll be looking at some of them in later posts, but when it comes to selection based on density categories you will use:

/images/res-[category]/image.png

Android will use the image in the closest matching category. This might also mean it will use a higher category image instead of only falling back to a lower category. The last fallback is always /images/res-mdpi/image.png and ultimately /images/image.png.

NOTE: Another difference with iOS is that if you don’t set the size of your ImageView and no perfect match is found for the device DPI, then the image will not show in the size you might expect. This is why you should always set at least one of width or height. See the example app for a demo.

Using subfolders

Because Android uses folder names, it handles subfolders in the images folder a bit different from iOS:

  • iOS: /images/<subfolders>/image@2x.png
  • Android: /images/res-hdpi/<subfolders>/image.png

Using remote images

Only on iOS can you also use remote density specific images. You do have to build the URL to the right image yourself using Ti.Platform.displayCaps.logicalDensityFactor and set the hires property of the ImageView to true.

NOTE: Remote @3x images are not yet supported (TIMOB-18769). Use @2x instead.

Sharing (only) default images between platforms

One of Titanium’s great features is cross-platform support from a single codebase. However, you will want to keep the packaged apps as small as you can. For both images and other resources, you can use platform-specific folders. When Titanium packages your app it will leave out folders named after other platforms and move the resources in the target platform’s folders one level up.

Your assets folder

/images/android/android-only.png
/images/iphone/ios-only.png
/images/both.png

Result when packaged for iOS

/images/ios-only.png
/images/both.png

Best practice folder structure

Since default images on iOS have no suffix the above leads to the following best practice to organize your image assets. For Alloy these paths would sit under /app/assets and for classic under /Resources.

Path Example size
/images/image.png 100×100
/images/iphone/image@2x.png 200×200
/images/iphone/image@3x.png 300×300
/images/android/res-hdpi/image.png 150×150
/images/android/res-xhdpi/image.png 200×200
/images/android/res-xxhdpi/image.png 300×300

As you see iOS and Android share the same default (160dpi) image. The images for xhdpi and xxhdpi can be copies of the (HD) Retina image, but then without the suffix. You can also safely ignore ldpi and xxxhdpi and save some bits there.

For additional information about how this works, make sure you check out this AppU Video.

Final notes

For Android image paths have to begin with /. This is not required but also doesn’t hurt on iOS, so please do.

Related links