Customizing Flex applicationDPI for a multiple screen size application

Posted on October 17, 2011 | 5 comments

Building a Flex mobile application that works across smartphones and tablets takes an understanding of screen sizes, screen dots per inch (DPI) and screen resolutions. In the current landscape of devices typically you consider a tablet something bigger then a 5-6 inch physical screen. Also in the current device landscape screen resolutions are for tablets are typically greater then 1024×600 (original Galaxy Tab 7′ and PlayBook), while smartphones are typically less 1024×600. This of course will change as screens get better and have high resolutions.

How Flex works with helping you develop and design for multiple screens is provide a concept of applicationDPI. This applicationDPI is baked into the spark mobile skins, available in css media queries, and available to you any where in your code. Flex also provides a mechanism to override the applicationDPI for your needs, I will get to this in a bit. First for an example of an application making heavy use of the applicationDPI check out Caltrain Times. It is currently running across Android, iOS, and BlackBerry Tablet OS (PlayBook).

I have also personally tested it across these devices:

Device NameScreen SizeScreen ResolutionDPI
Motorola Atrix4540 x 960275
Google Nexus 13.7480 x 800254
iPhone 3GS3.5320 x 480163
iPod Retina Display3.5640 x 960326
iPad9.71024 x 768132
PlayBook71024 x 600168
Galaxy Tab 7′71024 x 600168
Motorola Xoom10.11280 x 800150
Nook Color71024 x 600168

What is Flex’s Application DPI really?

Lets start with what its not. Its not a reflection of what the device’s actual DPI, for example the DPI values in the above chart. What Flex does for developers is provide 3 classifications you build your application against. They values are 160, 240, and 320, but you can think of them as small, medium, and large. Its also important to think of the Flex three DPI classification approach as your way to define what your application looks like and how it acts not relative it directly to device DPI. I explain two examples of why this is important with my Caltrain Times application. First is how to handle incorrect DPI values from the OS level. Second is applying your look to a set of devices by customizing the DPI classification in Flex. I’ll get to the detail of those two examples later in the article, first lets explain application DPI.

The definition, Flex’s applicationDPI takes on two meanings. If you explicitly set the value your are telling Flex that this application is skin to applicationDPI=XXX (160,240, or 320) and for the application to automatically scale the SWF and contents to the ratio of XXX/runtimeDPI. The applicationDPI classification strings can be found in mx.core.DPIClassification. For more control of how your application looks for the different DPI’s do not set this value. Then the value of applicationDPI is just a classification of 160, 240, or 320 (or some arbitrary value if you wanted to your own classification). By default Flex considers any device DPI of < 200 = 160, > 200 = 240 = < 280, and > 280 = 320. Now you can override the class mx.core.RuntimeDPIProvider, in the first case this controls the scaling, the second case it sets the applicationDPI value.

The rest of the post will make use of extending mx.core.RuntimeDPIProvider to handle incorrect DPIs from the OS and control the look between phones and tables.

Handling Incorrect DPIs

It just happens that on the Android devices depending on the OS and manufacturers there is a chance of the device reporting their DPI incorrectly. I address this problem in Caltrain Times application, specifically on the Motorola Atrix, in my custom extended mx.core.RuntimeDPIProvider class. The basis of my thoughts go back to the current landscape screen resolution and screen size values. I check the resolution threshold of 1024×600=614400 and screen size to figure out case that just don’t make sense. Here is the code for screenSize and pixel checking:

var screenX:Number = Capabilities.screenResolutionX;
var screenY:Number = Capabilities.screenResolutionY;
var pixelCheck:Number = screenX * screenY;
var pixels:Number = (screenX*screenX) + (screenY*screenY);
var screenSize:Number = Math.sqrt(pixels)/Capabilities.screenDPI;

In the case of a high resolution 4 inch Android device with incorrect DPI of < 220 you'll find that the screenSize is getting larger then 5 inches. In my application I just looked for any device that was larger then 4.3 screen size (might have to change that as phones get larger displays) and its screen resolution < 610000 with a report DPI of < 220. If all those criteria are found I force it to return a value of 240 which the correct value I want.

Here is the code (with some checks for the my desktop case in there too):

if (screenSize > 4.3 && pixelCheck > 510000 && pixelCheck < 610000 &&
    Capabilities.screenDPI < 240 && pixelCheck != 1296000)
{
    //trace("Force 240");
    return DPIClassification.DPI_240;
}

Note: The source code can be found at https://github.com/renaun/CaltrainTimes/blob/master/src/com/renaun/mobile/dpi/CustomDPIProvider.as.

Applying your look to a set of devices by customizing the DPI classification in Flex

In this example I use similar calculations to figure out if the device is of a tablet size. The reasoning for my Caltrain Times application is that I wanted to overrider the typical DPI classification of 160 for tablets to be rendered at the DPI classification of 240. For my application with the larger look on the bigger tablet screens it feels the screen out nicely and works well. Again I go into the custom runtime DPI class to do this. Here is the code:

else if (screenSize > 6.9 && screenSize < 11 && pixelCheck > 610000 && pixelCheck < 1920000 && pixelCheck != 1296000)
{
    //trace("Force 240 Tablet");
    return DPIClassification.DPI_240;  
}

The “pixelCheck < 1920000 && pixelCheck != 1296000" part is to handle running it through adl on my desktop. The rest probably makes sense, in the current device landscape tablets are typically larger then 7 inches and smaller then 11 inches with a resolution greater then 1024x600. If these values show up then I apply the DPI classification of 240.

Summary

The main take away is don't take the devices DPI at face value. If you want more control of your application across various devices make use of some classification constraints and check screen size, resolution, and DPI. It might seem like a bunch of work but this class can be used across applications and projects with minor tweaks depending on content requirements. Its also a good way to figure out if its a tablet or smartphone and outside of Flex DPI classification. Enjoy.

Check out my MAX 2011 talk where I go through Caltrain Times in more detail, as well as the source code available on github.com/renaun/CaltrainTimes.

  • http://twitter.com/jozefchutka Jozef Chutka

    here is a SystemScreen class to convert inches to pixels, screen size to centimeters etc. http://wonderfl.net/c/dzqE

  • Pingback: Cool Stuff with the Flash Platform - 10/24/2011 | Remote Synthesis

  • http://evonyhookups.info John snow

    Thanks Renaun! This incorrect DPI stuff is driving me insane…

  • http://www.facebook.com/locusta Locusta Liquirizia

    Help. I’m using flashdevelop. Once I’ve created the new class that overrides runtimedpiprovider, how i tell my application that the default class is changed?

    • renaun

      Not sure how FlashDevelop does class path ordering. You want to try and get it to compile any monkey patched classes first.

  • Ra

    This is one good thing also

    By this you scale your app according to devise screen size

    private var originalW:Number=1024.0; // this is my app width

    private var originalH:Number=600.0; // this is my app height

    private function init():void{

    navigator.firstView = homeView;

    var deff:Number = Math.abs(( originalW – this.width ) ) * 100 / originalW ;

    deff = ( 1 * deff ) / 100 ;

    if ( deff != 0 && deff != 1 ) {

    if ( originalW < this.width ) {

    scaleX = 1 + deff;

    trace ( "scaling width Bigger= " + scaleX );

    }

    else {

    scaleX = 1 – deff;

    trace ( "scaling width smaller = " + scaleX );

    }

    }

    //——————-

    deff = Math.abs(( originalH – this.height ) ) * 100 / originalH ;

    deff = ( 1 * deff ) / 100 ; // get scaling percent on the respect on 1

    if ( deff != 0 && deff != 1 ) {

    if ( originalH < this.height ) {

    scaleY = 1 + deff;

    trace ( "scaling height Bigger= " + scaleY );

    }

    else {

    scaleY = 1 – deff;

    trace ( "scaling height smaller = " + scaleY );

    }

    }

    }

    Rama