0

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.

TokTok123
  • 753
  • 3
  • 11
  • 27
  • `programmatic approach in UI design` ... why do you want to **overcomplicate** your life (and your code)? – Phantômaxx Jul 28 '14 at 08:55
  • Android is already doing this classification of small,large,xlarge etc for us then why don't you use the approach that is standard and prescribed i.e. Using Values folder for hdpi/mdpi/ldpi and the layout folder see this http://stackoverflow.com/questions/21280277/different-values-folders-in-android – MOSO Jul 28 '14 at 09:11
  • @MOSO: Looking at the link in yourcoments: 1st: Just to confirm this means for each of the UI categories i.e. small, normal, large and X-large I will create my UI and 2nd point: If I were to follow this approach how do I then refer to the various dimensions in the values folders? 3rd: does having these various values folders aid in scaling? thanks – TokTok123 Jul 28 '14 at 10:19
  • for 1st and 2nd go through first: http://android4beginners.com/2013/07/appendix-c-everything-about-sizes-and-dimensions-in-android/ and http://www.techrepublic.com/blog/software-engineer/support-different-android-device-configurations-with-dimension-resources/ – MOSO Jul 28 '14 at 11:05
  • @TokTok123 else for the third one - I would say yes. The perfect example is the launcher icon ic_launcher in the android project. When you run the app you may run in any phone it will never be distorted. On the contrary this is just the magic of values and other drawable folders. – MOSO Jul 28 '14 at 11:11

0 Answers0