Further down in this post are two approaches I've taken to develop android UIs to cater for various screen sizes. Before you ask to what extent I've read/researched the issue of catering for various screen sizes I've been through the topics covered here and here plus many more articles regarding supporting screen sizes. In addition I've been through several posts which touch on both approaches.
With both approaches I've divided my screen layout categories into 4 i.e. small, normal, large and x-large as seen here.
My first and past approach of creating a screen was to create an XML and corresponding activity belonging to small, normal, large and x-large categories and subsequently inflating and customising the views in each XML in the respective activity. In my welcome screen I used the code below to determine which activity would be displayed based on the device's resolution and screen size in inches.
//!< splashScreenAnim() - Splash screen animation
public boolean splashScreenAnim() {
// For tracking purposes record in logcat SplashScreenThread() method has been called
Log.i(TAG + "onCreate()", "SplashScreenThread(); HAS STARTED");
// Thread for the display of the SplashScreen
splashScreenAnimThread = new Thread() {
@Override
public void run() {
try { //
int waited = 0;
while(_active && (waited < splashScreenAnimDelay)) {
sleep(100);
if(_active) {
waited += 100;
}
}
} catch(InterruptedException e) { //
Log.e(TAG + "SplashScreenThread()", "SplashScreenThread() ERROR IS AS FOLLOWS: " + e.toString());
} finally {
//
finish();
//
Log.w(TAG, "CHECK IF android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.HONEYCOMB");
//
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.HONEYCOMB) {
Log.i(TAG, "CHECK IF android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.HONEYCOMB");
// Obtain the normal device display area
Display display = getWindowManager().getDefaultDisplay();
// Obtain the normal device display metrics
DisplayMetrics outMetrics = new DisplayMetrics();
Log.i(TAG, "outMetrics is " + outMetrics);
//
display.getMetrics(outMetrics);
// Obtain the logical density of the display
float density = getResources().getDisplayMetrics().density;
Log.i(TAG, "Logical density of the display is " + density);
// Obtain screen size in inches
ScreenInches screenInches = new ScreenInches(outMetrics, density);
double screenSizeInInches = screenInches.screenSizeInInches();
// Call the layout based on the screen dimensions
if(((int) outMetrics.widthPixels < 800 && (int) outMetrics.heightPixels < 480)) {
Intent underDevltScreenNormal800by480 = new Intent(SplashScreen.this, TempScreenNormal800by480.class);
startActivity(underDevltScreenNormal800by480);
Log.e(TAG, "UnderDevltScreenNormal800by480.java called.");
} else if(((int) outMetrics.widthPixels == 1440 && (int) outMetrics.heightPixels == 900) | (int) outMetrics.widthPixels == 1440) {
Intent tempScnXLarge = new Intent(SplashScreen.this, TempScnXLarge.class);
startActivity(tempScnXLarge);
Log.i(TAG, "tempScnXLarge.java called.");
} else if( (screenSizeInInches >= 3.00 && screenSizeInInches <= 5.50)
&& ((int) outMetrics.widthPixels >= 800 && (int)outMetrics.widthPixels <= 960)
&& ((int) outMetrics.heightPixels >= 480) && (int) outMetrics.heightPixels <= 540) {
Intent loginScreen = new Intent(SplashScreen.this, ScnNormal800by480.class);
startActivity(loginScreen);
Log.i(TAG, "scnNormal800by480.java called.");
} else if( (screenSizeInInches >= 3.00 && screenSizeInInches < 5.50)
&& ((int) outMetrics.widthPixels > 1024 && (int)outMetrics.widthPixels <= 1280)
&& ((int) outMetrics.heightPixels > 600) && (int) outMetrics.heightPixels <= 800) {
Intent loginScreen = new Intent(SplashScreen.this, ScnNormal.class);
startActivity(loginScreen);
Log.i(TAG, "scnNormal.java called.");
} else if( (screenSizeInInches >= 3.00 && screenSizeInInches < 5.50)
&& ((int) outMetrics.widthPixels > 1280 && (int)outMetrics.widthPixels <= 1920)
&& ((int) outMetrics.heightPixels > 768) && (int) outMetrics.heightPixels <= 1080) {
Intent loginScreen = new Intent(SplashScreen.this, ScnNormal1920by1080.class);
startActivity(loginScreen);
Log.i(TAG, "scnNormal1920by1080.java called.");
} else if( (screenSizeInInches >= 5.00 && screenSizeInInches < 10.1)
&& ((int) outMetrics.widthPixels > 960 && (int)outMetrics.widthPixels <= 1024)
&& ((int) outMetrics.heightPixels > 540) && (int) outMetrics.heightPixels <= 768) {
Intent loginScreen = new Intent(SplashScreen.this, ScnLarge.class);
startActivity(loginScreen);
Log.i(TAG, "scnLarge.java called.");
} else if( (screenSizeInInches >= 6.0 && screenSizeInInches < 8.0)
&& ((int) outMetrics.widthPixels > 1024 && (int)outMetrics.widthPixels <= 1280)
&& ((int) outMetrics.heightPixels > 768) && (int) outMetrics.heightPixels <= 800) {
Intent loginScreen = new Intent(SplashScreen.this, ScnLarge1280by800.class);
startActivity(loginScreen);
Log.i(TAG, " scnLarge1280by800.java called.");
} else if( (screenSizeInInches >= 8.0 && screenSizeInInches < 10.5)
&& ((int) outMetrics.widthPixels > 1024 && (int)outMetrics.widthPixels <= 1280)
&& ((int) outMetrics.heightPixels > 600) && (int) outMetrics.heightPixels <= 800) {
Intent loginScreen = new Intent(SplashScreen.this, ScnXLarge.class);
startActivity(loginScreen);
Log.i(TAG, "scnXLarge.java called.");
} else if( (screenSizeInInches >= 7.0 && screenSizeInInches < 10.5)
&& ((int) outMetrics.widthPixels > 1280 && (int)outMetrics.widthPixels <= 2560)
&& ((int) outMetrics.heightPixels > 800) && (int) outMetrics.heightPixels <= 1600) {
Intent tempScnXLarge = new Intent(SplashScreen.this, TempScnXLarge.class);
startActivity(tempScnXLarge);
Log.i(TAG, "tempScnXLarge.java called.");
}
androidOSStatus = true;
} else {
androidOSStatus = false;
}
}
}
};
splashScreenAnimThread.start();
return androidOSStatus;
}
This 1st approach worked in terms of selecting the correct activity but the elected activites didnt there was no scaling assuming the screen resolution was not an exact match.
In my 2nd approach I took the approach of programtically building the UI in the activity to be called as opposed to the regular approach of inflating XML views. Using this approach I've been able to scale each view as I've done for the imageview and the textview in the code below:
package com.example.test;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.sql.Timestamp;
import java.util.Calendar;
import java.util.Date;
import android.os.Bundle;
import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.graphics.Typeface;
import android.text.InputType;
import android.text.format.Time;
import android.text.method.PasswordTransformationMethod;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.Display;
import android.view.Menu;
import android.view.ViewGroup.LayoutParams;
import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.annotation.SuppressLint;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.database.Cursor;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.Build;
import android.view.ActionMode;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnKeyListener;
import android.view.View.OnTouchListener;
import android.view.View.OnFocusChangeListener;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.view.inputmethod.EditorInfo;
public class ScnNormal800by480 extends Activity {
/** Pubic vars */
public String TAG = "ScnNormal800by480";
public TextView tvDealerSupport;
public InputMethodManager tmpImm;
public double normal800by480WScale, normal800by480HScale;
public Typeface officialBoldFont, officialRegularFont;
public ImageView ivLogo;
@SuppressLint("NewApi")
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Obtain the normal device display area
Display display = getWindowManager().getDefaultDisplay();
// Obtain the normal device display metrics
DisplayMetrics outMetrics = new DisplayMetrics();
Log.w(TAG, "outMetrics is " + outMetrics);
display.getMetrics(outMetrics);
// Obtain the logical density of the display
float density = getResources().getDisplayMetrics().density;
Log.w(TAG, "Logical density of the display is " + density);
// Fetch W and H scaling factors
NormalScreenScaling800by480 normalScreenScaling800by480 = new NormalScreenScaling800by480(outMetrics, density);
normal800by480WScale = normalScreenScaling800by480.normalScaleW();
normal800by480HScale = normalScreenScaling800by480.normalScaleH();
Log.w(TAG + " - normal800by480WScale", "normal800by480WScale: " + normal800by480WScale);
Log.w(TAG + " - normal800by480HScale", "normal800by480HScale: " + normal800by480HScale);
//
ScreenInches screenInches = new ScreenInches(outMetrics, density);
float getHeightDpi = screenInches.getHeightDpi();
float getWidthDpi = screenInches.getWidthDpi();
double screenSizeInInches = screenInches.screenSizeInInches();
// Set official font
officialRegularFont = Typeface.createFromAsset(getAssets(), "square721extendedreg.ttf");
officialBoldFont = Typeface.createFromAsset(getAssets(), "square721extendedbold.ttf");
// Set imm for all the edittexts
tmpImm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
Log.w(TAG, "- - - Main RL - - -");
// Create main RL params
RelativeLayout.LayoutParams rlMainlayoutParams
= new RelativeLayout.LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT);
// Create main relative layout
RelativeLayout rlMain = new RelativeLayout(this);
rlMain.setLayoutParams(rlMainlayoutParams);
//rlMain.setBackgroundResource(R.drawable.bgndlogin);
rlMain.setBackgroundColor(Color.BLACK);
// Create layout paramaters
RelativeLayout.LayoutParams ivLogoParams =
new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
ivLogoParams.width = (int) Math.round(208 * normal800by480WScale);
ivLogoParams.height = (int) Math.round(60 * normal800by480HScale);
ivLogoParams.topMargin = (int) Math.round(17 * normal800by480HScale);
ivLogoParams.leftMargin = (int) Math.round(16 * normal800by480WScale);
// Create the and customise the ImageView
ivLogo = new ImageView(this);
ivLogo.setLayoutParams(ivLogoParams);
ivLogo.setBackgroundResource(R.drawable.bgelogo);
// Set layout parameters
RelativeLayout.LayoutParams tvDealerSupportParams =
new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
tvDealerSupportParams.topMargin = (int) Math.round(33 * normal800by480HScale);
tvDealerSupportParams.leftMargin = (int) Math.round(225 * normal800by480WScale);
tvDealerSupportParams.width = (int) Math.round(960 * normal800by480WScale);
// Create and customise the textview
tvDealerSupport = new TextView(this);
tvDealerSupport.setLayoutParams(tvDealerSupportParams);
tvDealerSupport.setText("DEALER SUPPORT ");
tvDealerSupport.setTextColor(Color.BLACK);
tvDealerSupport.setTypeface(officialBoldFont);
tvDealerSupport.setBackgroundColor(Color.parseColor("#FFCC00"));
tvDealerSupport.setGravity(Gravity.RIGHT);
// Set the text size based on the screen dimensions
if((int) outMetrics.widthPixels <= 854 && outMetrics.heightPixels <= 480) {
tvDealerSupport.setTextSize(9.7f);
} else if( ((int) outMetrics.widthPixels > 854 && (int)outMetrics.widthPixels <= 960)
&& ((int) outMetrics.heightPixels > 480) && (int) outMetrics.heightPixels <= 540) {
tvDealerSupport.setTextSize(11.7f);
}
// Add all view to the main relative layout
Log.w(TAG, "- - - Addition to main RL - - -");
rlMain.addView(ivLogo);
rlMain.addView(tvDealerSupport);
// Set the activity content to an explicit view.
setContentView(rlMain);
}
public class NormalScreenScaling800by480 {
/** Private vars */
private DisplayMetrics _outMetrics;
private float _density;
private String TAG = "NormalScreenScaling800by480";
/** Normal i.e. 800 by 480 screen scaling constructor
*/
public NormalScreenScaling800by480(DisplayMetrics outMetrics, float density) {
_outMetrics = outMetrics;
_density = density;
}
/** Returns width scaling factor for normal screen sizes i.e. 800 by 480
*/
public double normalScaleW() {
double dpWidth = _outMetrics.widthPixels / 1.5;
Log.i(TAG, "For NORMAL 800 by 480 screen scaling _outMetrics.widthPixels is: " + _outMetrics.widthPixels);
Log.i(TAG, "For NORMAL 800 by 480 screen scaling _density is: " + _density);
Log.i(TAG, "For NORMAL 800 by 480 screen scaling width dp is: " + dpWidth);
double scaleW = dpWidth/800;
Log.i(TAG, "Width scaling factor NORMAL 800 by 480 screens is: " + scaleW);
return scaleW;
}
/** Returns height scaling factor for normal screen sizes i.e. 800 by 480
*/
public double normalScaleH() {
double dpHeight = _outMetrics.heightPixels / 1.5;
Log.i(TAG, "For NORMAL 800 by 480 screen scaling _outMetrics.heightPixels is: " + _outMetrics.heightPixels);
Log.i(TAG, "For NORMAL 800 by 480 screen scaling _density is: " + _density);
Log.i(TAG, "For NORMAL 800 by 480 screen scaling height dp is: " + dpHeight);
double scaleH = dpHeight/480;
Log.i(TAG, "Height scaling factor NORMAL 800 by 480 screens is: " + scaleH);
return scaleH;
}
}
/*! Screen sizes in inches */
public class ScreenInches {
/** Private vars */
private DisplayMetrics _outMetrics;
private float _density;
private String TAG = "ScreenInches";
/** Screen inches calculation constructor
*/
public ScreenInches(DisplayMetrics outMetrics, float density) {
_outMetrics = outMetrics;
_density = density;
}
/** Returns screen inches
*/
public double screenSizeInInches() {
//
float widthDpi = _outMetrics.xdpi;
Log.i(TAG, "widthDpi = _outMetrics.xdpi; is: " + widthDpi);
float heightDpi = _outMetrics.ydpi;
Log.i(TAG, "heightDpi = _outMetrics.ydpi; is: " + heightDpi);
//
float widthPixels = _outMetrics.widthPixels;
Log.i(TAG, "_outMetrics.widthPixels is: " + _outMetrics.widthPixels);
float heightPixels = _outMetrics.heightPixels ;
Log.i(TAG, "_outMetrics.heightPixels is: " + _outMetrics.heightPixels);
//
float widthInches = widthPixels / widthDpi;
Log.i(TAG, "widthInches = widthPixels / widthDpi is: " + widthInches);
float heightInches = heightPixels / heightDpi;
Log.i(TAG, "heightInches = heightPixels / heightDpi is: " + heightInches);
// The size of the diagonal in inches is equal to the square root of the height in inches squared plus the width in inches squared.
double diagonalInches = Math.sqrt(
(widthInches * widthInches)
+ (heightInches * heightInches));
Log.i(TAG, " diagonalInches is: " + diagonalInches);
//
double diagonalInches2 = Math.sqrt((widthPixels * widthPixels) + (heightPixels * heightPixels));
double inchDiag = diagonalInches2/_density;
//
return diagonalInches;
//return inchDiag;
}
//
public float getWidthDpi() {
//
float widthDpi = _outMetrics.xdpi;
Log.i(TAG, "widthDpi = _outMetrics.xdpi; is: " + widthDpi);
return widthDpi;
}
//
public float getHeightDpi() {
float heightDpi = _outMetrics.ydpi;
Log.i(TAG, "heightDpi = _outMetrics.ydpi; is: " + heightDpi);
return heightDpi;
}
}
}
Based on your experience with regards to catering for various screen sizes i.e. small, normal, large and x-large and the various screen resolutions under each size which approach how could I improve my second approach further? I ask as I intend on using the programmatic approach in UI design of fragments.
Thanks.