Saturday, February 11, 2012

Resizing Android View for games

Those who have developed games for Android are aware of how senseless the View dimensions you have to specify position and size are.
Beginners are advised to read this: http://developer.android.com/guide/topics/resources/more-resources.html
Possible values are:

  • px (pixels): rarely useful, only e.g. if you want an exactly 1 pixel wide line (and even then it isn’t really useful, but that’s a question of conviction so let’s move on)
  • dp or dip (density-independent pixels): this is the generally recommended unit, perfectly useful for standard applications, because the application will be readable on any device
  • sp (scaled pixels based on preferred font size): if your application contains a lot of text and the user has adjusted font size, then your application becomes easier to read (e.g. because they have worse eyesight), but too large settings may cause your interface to break.
  • in (inches): this corresponds to the physical size; honestly I don’t see much use for it: you may be able to develop easier this way (“make that about 3 inches”), but it’s guaranteed to break on 99% of the devices...
  • pt (1/72 inches): this is essentially a duplicate of the above
  • mm (millimeters): at least this one is a standard unit, but equally useless.

This basically boils it down to one (dp), or at most two (sp) useful units.
When you’re working on a game, you design the interface by placing things on the entire screen. The emphasis is on the word “entire”, meaning “how many dp/px/in/mm wide is the screen?” We don’t know that, only that it’s at least 320dip wide... If you’re interested, you can browse a set of resolutions here: http://developer.android.com/resources/dashboard/screens.html.
You’ll want to specify things as percentages (“I want this icon to be 10% wide”), and not in irrelevant units...
This makes applications developed on small phones to look the same on tablets, instead of being crammed into the top 80% of the screen (this is a textbook error: games get designed for 480x800 and then just get ported identically to 480x854 or 600x800).
I personally design everything to the smallest screen size, and I also take 4/3 screen aspect ratio into account. The code I use for this looks like this:


public class Util
{
  public static int gDisplayPlayFieldSize;
  public static float gDisplayPlayFieldDensity;

  static public void init()
  {
    DisplayMetrics dm=MainApp.gApp.getResources().getDisplayMetrics();
    gDisplayPlayFieldSize=(dm.heightPixels<dm.widthPixels ? dm.heightPixels : dm.widthPixels);
    gDisplayPlayFieldDensity=gDisplayPlayFieldSize/320.f; 

  }

  static public float getDimension(TypedArray arr, int idx, float defValue)
  {
    if (!arr.getValue(idx, mTypedValue)||mTypedValue.type!=TypedValue.TYPE_DIMENSION)
      return defValue;

    int unit=(mTypedValue.data>>TypedValue.COMPLEX_UNIT_SHIFT)&TypedValue.COMPLEX_UNIT_MASK;

    if (unit==TypedValue.COMPLEX_UNIT_MM)
      return TypedValue.complexToFloat(mTypedValue.data)*Util.gDisplayPlayFieldDensity;
    return arr.getDimension(idx, defValue);
   }

  static public boolean setPercentDimension(View view)
  {
    if (view==null)
      return false;
    LayoutParams lp=view.getLayoutParams();
    if (lp==null)
      return false;

    boolean rl=false;
    if (lp.width>0) {
      lp.width=(lp.width*Util.gDisplayPlayFieldSize+50)/100;
      rl=true;
    }

    if (lp.height>0) {
      lp.height=(lp.height*Util.gDisplayPlayFieldSize+50)/100;
      rl=true;
    }

    return rl;
  }
}


What the getDimension method does is that it treats mm values as percentages (because it’s useless anyway) and works as default otherwise. I’m using this in my own View class, because a lot of the settings are set up via Style-s.
The setPercentDimension method essentially “reinterprets” the size of a View, it’s usually used inside an Activity or a Dialog class.
It’s not a perfect method, because it requires additional code for resizing, but at least you don’t have to worry about how the application will look on a 720x1280 screen.
Copyright © Blind Logic's Blog