Key Points of Android APP Full Screen Adaptation Technology

Posted by Jebs on Sat, 11 May 2019 11:39:04 +0200

The Concept of Full Screen

Why explain the full screen first, because the word is now a false proposition. Full screen literally means that the front of the mobile phone is all screen, 100% of the screen. But nowadays, none of the manufacturers that launch so-called "full screen" mobile phones can achieve full scale.

So let's talk about the understanding and definition of the full screen in Android development.

The screen aspect ratio of general mobile phones is 16:9, such as 1080x1920, 1440x2560, etc. Its ratio is 1.77. Before the appearance of full-screen mobile phones, the default maximum aspect ratio of Android is 1.86, which is compatible with 16:9 screen.

In the pursuit of larger screen space and more extreme user experience, some mobile phone manufacturers have improved the aspect ratio of the screen. 17:9, 19:10, 18:9 and 18.5:9 mobile phones have entered the market. The aspect ratio of these mobile phones is much higher than 1.86. These mobile phones are called full-screen mobile phones.

Why do you need to adapt

We change the value of targetSdkVersion to less than or equal to 23. When we run the program, we will find a black bar at the bottom of the screen.

image

How to fit

Target SdkVersion <=23, larger aspect ratio of screen

After the release of Galaxy S8, Android officially provided an adaptation solution, that is, to increase the aspect ratio of the maximum screen supported by App. The implementation is simple. The following configuration can be made in Android Manifest. xml:

<meta-data android:name="android.max_aspect"
    android:value="ratio_float"/>

Among them, ratio_float is a floating point number, and the official recommendation is 2.1 or larger, because 18.5:9 = 2.0555555... If there are more vertical and horizontal mobile phones in the future, the value will be larger.

<meta-data android:name="android.max_aspect" 
    android:value="2.1" />

The max_aspect value can also be set dynamically in Java code by the following methods:

public void setMaxAspect() {
        ApplicationInfo applicationInfo = null;
        try {
            applicationInfo = getPackageManager().getApplicationInfo(getPackageName(), 
            PackageManager.GET_META_DATA);
        } catch (PackageManager.NameNotFoundException e) {
            e.printStackTrace();
        }
        if(applicationInfo == null){
            throw new IllegalArgumentException(" get application info = null ");
        }
        applicationInfo.metaData.putString("android.max_aspect", "2.1");
    }

If the value of targetSdkVersion is greater than 23, then max_aspect should not be set.

View the adapted screenshots:

image

https://android-developers.googleblog.com/2017/03/update-your-app-to-take-advantage-of.html

Image resource adaptation

Let's take a look at the startup page. The images that fit on the 16:9 screen will be stretched on the 18:9 screen.

16:9 screen display 18:9 screen display
image
image

There are only two ways to solve this problem: changing pictures or changing layout.

Change pictures

You can't rely on a single vendor's solution, you can only start with Android system attributes. Considering that most of the full-screen mobile phones are only stretched in height, mostly about 6.0 inches, and the pixel density is not much different from xxhdpi, we can add a group of resources drawable-xhdpi-2160x1080, drawable-long to solve the problem of image stretching. Of course, such a method is certainly not very good, which will increase the capacity of app. I won't show you here.

Optimal layout

Of course, the best way is to use XML for relative layout, or the solution of. 9 graph.

What I have concluded is a small amount of multi-cut, minimizing the impact of size on the layout. Here, for example, using a square cut, let him center the display, regardless of how the screen is vertical and horizontal, will not stretch the picture, stretching only the background.

image
<ImageView
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:scaleType="fitCenter"
        android:src="@drawable/bz002"/>
Before adaptation After adaptation
image
image

Full Screen Height Problem Adaptation

First, explain the concepts of window, decorview, rootview.

image

Windows Official Document: Window

public abstract class Window. Abstract base class for a top-level window look and behavior policy. An instance of this class should be used as the top-level view added to the window manager. It provides standard UI policies such as a background, title area, default key processing, etc.

The only existing implementation of this abstract class is android.view.PhoneWindow, which you should instantiate when needing a Window.

Every activity has a Windows object, but Windows is an abstract class, where Android provides Windows with the only implementation class, PhoneWindow. That is to say, the Windows instance in Activity is a PhoneWindow object.

But Phone Windows is Windows after all, and it doesn't have much View-related capabilities. But Phone Windows has a very important view object in Android, DecorView.

Now the relationship is very clear, each activity holds a PhoneWindow object, and a PhoneWindow object holds an instance of DecorView, so most of the View-related operations in Activity are actually done through DecorView.

DecorView can be understood as the inside screen of the mobile phone, which is the glass that can emit light.

Here, through the code, we print out the height of the various data in our page.

int decorviewHeight = decorView.getHeight();
int screenHeight = FullScreenManager.getScreenHeight();
int nativeBarHeight = FullScreenManager.getNativeBarHeight();
int contentViewHeight = rootView.getHeight();
int navigationBarHeight1 = FullScreenManager.getNavigationBarHeight();

Log.d("shijiacheng","=======================================");
Log.d("shijiacheng","DecorView height: " + decorviewHeight + " px");
Log.d("shijiacheng","Screen height: " + screenHeight + " px");
Log.d("shijiacheng","NativeBar height: " + nativeBarHeight + " px");
Log.d("shijiacheng","ContentView height: " + contentViewHeight + " px");
Log.d("shijiacheng","NavigationBar height: " + navigationBarHeight + " px");
Log.d("shijiacheng","---------------------------------------");

Get the height of decorView

final View decorView = getWindow().getDecorView();
int decorviewHeight = decorView.getHeight();

Get screen height

/**
 * Get screen height
 * @return
 */
public static int getScreenHeight() {
    Resources resource = AppContext.getInstance().getResources();
    DisplayMetrics displayMetrics = resource.getDisplayMetrics();
    return displayMetrics.heightPixels;
}

Get the height of the status bar

/**
 * Get the height of the status bar
 *
 * @return
 */
public static int getNativeBarHeight() {
    Resources resource = AppContext.getInstance().getResources();
    int result = 0;
    int resourceId = resource.getIdentifier("status_bar_height", 
            "dimen", "android");
    if (resourceId > 0) {
        result = resource.getDimensionPixelSize(resourceId);
    }
    return result;
}

Get the height of contentView

LinearLayout contentView = findViewById(R.id.root);
int contentViewHeight = contentView.getHeight();

Get the height of Navigation Bar

public static int getNavigationBarHeight() {
    Resources resources =  AppContext.getInstance().getResources();
    int resourceId = resources.getIdentifier("navigation_bar_height","dimen", "android");
    int height = resources.getDimensionPixelSize(resourceId);
    return height;
}

In order to display the data more intuitively, we use the layout method to display the data. The layout code is relatively simple, but it is not shown here.

image

Let's start by showing the normal height of the screen.

10-08 09:52:03.636 23818-23818/? D/shijiacheng: =========================
10-08 09:52:03.637 23818-23818/? D/shijiacheng: DecorView height: 1280 px
10-08 09:52:03.637 23818-23818/? D/shijiacheng: Screen height: 1280 px
10-08 09:52:03.637 23818-23818/? D/shijiacheng: NativeBar height: 50 px
10-08 09:52:03.637 23818-23818/? D/shijiacheng: ContentView height: 1230 px
10-08 09:52:03.637 23818-23818/? D/shijiacheng: NavigationBar height: 96 px
10-08 09:52:03.637 23818-23818/? D/shijiacheng: -------------------------
image

DecorView = Screen height = NativeBar height + ContentView height

Look at the full screen of millet mix

2018-10-08 09:54:15.640 /? D/shijiacheng: =========================
2018-10-08 09:54:15.640 /? D/shijiacheng: DecorView height: 2160 px
2018-10-08 09:54:15.641 /? D/shijiacheng: RootView height: 2094 px
2018-10-08 09:54:15.641 /? D/shijiacheng: Screen height: 2030 px
2018-10-08 09:54:15.641 /? D/shijiacheng: NativeBar height: 66 px
2018-10-08 09:54:15.641 /? D/shijiacheng: ContentView height: 2094 px
2018-10-08 09:54:15.641 /? D/shijiacheng: NavigationBar height: 130 px
2018-10-08 09:54:15.641 /? D/shijiacheng: -------------------------
image

The problem arises. We can find that the height of contentView is higher than that of screen screen. We can't help but wonder that our method of getting the height of screen is wrong under the full screen.

Question 1: Inaccurate calculation of screen height acquisition method

We always use the following methods to measure the height of the screen:

public static int getScreenHeight() {
    Resources resource = AppContext.getInstance().getResources();
    DisplayMetrics displayMetrics = resource.getDisplayMetrics();
    return displayMetrics.heightPixels;
}

But this method is a very old one, not keeping pace with the times. Although this method is not a problem on the ordinary screen, it is not effective on the full screen mobile phone.

Now let's look at the evolution of the method of getting screen size.

Get screen width and height

Getting the screen width is a problem we often encounter in our development, and I believe that everyone is very familiar with it. The most commonly used are the following two kinds:

public static int getScreenHeight1(Activity activity) {
    return activity.getWindowManager().getDefaultDisplay().getHeight();
}
public static int getScreenHeight2(Activity activity) {
    DisplayMetrics displayMetrics = new DisplayMetrics();
    activity.getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
    return displayMetrics.heightPixels;
}

In fact, the above two ways are the same, but the second way is to encapsulate information into DesplayMetrics and get data from DesplayMetrics.

After Android 3.2(Api 13), the following method is provided to encapsulate data into Point and return width and height information.

@TargetApi(Build.VERSION_CODES.HONEYCOMB_MR2)
public static int getScreenHeight3(Activity activity) {
    Point point = new Point();
    activity.getWindowManager().getDefaultDisplay().getSize(point);
    return point.y;
}

After Android 4.2(Api17), the following method is provided. Similar to the third method, the data is encapsulated in a Point and the height information of the payment is returned.

@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
public static int getScreenHeight4(Activity activity) {
    Point realSize = new Point();
    activity.getWindowManager().getDefaultDisplay().getRealSize(realSize);
    return realSize.y;
}

In fact, getRealSize was added when Android Api15, but it was hidden, we can see by looking at the source code.

image

The getRealSize() method in the Android Api15 Display.java source code is marked as @hide

image

Therefore, we can rewrite the method of Height Acquisition and adapt it to all models and systems.

A Method of Getting Screen Size by Adapting All Screens

public static int[] getScreenSize(Context context) {
        int[] size = new int[2];

        WindowManager w = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
        Display d = w.getDefaultDisplay();
        DisplayMetrics metrics = new DisplayMetrics();
        d.getMetrics(metrics);
        // since SDK_INT = 1;
        int widthPixels = metrics.widthPixels;
        int heightPixels = metrics.heightPixels;

        // includes window decorations (statusbar bar/menu bar)
        if (Build.VERSION.SDK_INT >= 14 && Build.VERSION.SDK_INT < 17)
            try {
                widthPixels = (Integer) Display.class.getMethod("getRawWidth").invoke(d);
                heightPixels = (Integer) Display.class.getMethod("getRawHeight").invoke(d);
            } catch (Exception ignored) {
            }
        // includes window decorations (statusbar bar/menu bar)
        if (Build.VERSION.SDK_INT >= 17)
            try {
                Point realSize = new Point();
                Display.class.getMethod("getRealSize", Point.class).invoke(d, realSize);
                widthPixels = realSize.x;
                heightPixels = realSize.y;
            } catch (Exception ignored) {
            }
        size[0] = widthPixels;
        size[1] = heightPixels;
        return size;
    }

By using the new method of Height Acquisition and re-running the program, the running results have been displayed normally.

2018-10-08 13:19:32.389 /? D/shijiacheng: ==========================
2018-10-08 13:19:32.390 /? D/shijiacheng: DecorView height: 2160 px
2018-10-08 13:19:32.390 /? D/shijiacheng: Screen height: 2160 px
2018-10-08 13:19:32.390 /? D/shijiacheng: NativeBar height: 66 px
2018-10-08 13:19:32.390 /? D/shijiacheng: ContentView height: 2094 px
2018-10-08 13:19:32.390 /? D/shijiacheng: NavigationBar height: 130 px
2018-10-08 13:19:32.390 /? D/shijiacheng: --------------------------
image

Question 2: Computation of Millet mix Cut into Classical Navigation Key Mode

In MIUI settings, we changed the full-screen navigation style to the "classic navigation key" style.

image

Rerun the program and the results are as follows:

image

You can see another problem, DecorView = Screen height > NativeBar height + ContentView height

It's not hard to find that Screen height also counts the height of the virtual navigation bar at the bottom.

In many cases, we use the following methods to get the height of the navigation bar:

public static int getNavigationBarHeight() {
        Resources resources =  AppContext.getInstance().getResources();
        int resourceId = resources.getIdentifier("navigation_bar_height",
            "dimen", "android");
        int height = resources.getDimensionPixelSize(resourceId);
        return height;
    }

The height of the navigation bar obtained by this method is not a problem, but the height of the navigation bar can be obtained even if the navigation bar is hidden on the full screen mobile phone. From the logcat log above, you can see that even without the navigation bar, the height calculation of the navigation bar is valuable.

Adapt the Millet mix Virtual Navigation Bar

In the millet mix model, we can "force_fsg_nav_bar" to determine whether the millet mobile phone has opened the full screen gesture.

public static int getHeightOfNavigationBar(Context context) {
        //Return to 0 if the Millet mobile phone opens a full screen gesture and hides the navigation bar.
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
            if (Settings.Global.getInt(context.getContentResolver(), 
                "force_fsg_nav_bar", 0) != 0) {
                return 0;
            }
        }
        int realHeight = getScreenSize(context)[1];

        Display d = ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE))
        .getDefaultDisplay();
        DisplayMetrics displayMetrics = new DisplayMetrics();
        d.getMetrics(displayMetrics);

        int displayHeight = displayMetrics.heightPixels;

        return realHeight - displayHeight;
    }

Therefore, this method can be used to determine whether the bottom navigation bar is displayed, and the height of the navigation bar can be calculated.

int navigationBarHeight = FullScreenManager.getHeightOfNavigationBar(MainActivity.this);
if (navigationBarHeight > 0){
    container_navigationview.setVisibility(View.VISIBLE);
}else {
    container_navigationview.setVisibility(View.GONE);
}

The normal display effect is as follows:

Virtual navigation bar No virtual navigation bar
image
image

No Virtual Navigation Bar Log

2018-10-08 13:19:32.389 /? D/shijiacheng: ==========================
2018-10-08 13:19:32.390 /? D/shijiacheng: DecorView height: 2160 px
2018-10-08 13:19:32.390 /? D/shijiacheng: Screen height: 2160 px
2018-10-08 13:19:32.390 /? D/shijiacheng: NativeBar height: 66 px
2018-10-08 13:19:32.390 /? D/shijiacheng: ContentView height: 2094 px
2018-10-08 13:19:32.390 /? D/shijiacheng: NavigationBar height: 0 px
2018-10-08 13:19:32.390 /? D/shijiacheng: --------------------------

Log with Virtual Navigation Bar

2018-10-08 13:38:03.229 /? D/shijiacheng: ==========================
2018-10-08 13:38:03.230 /? D/shijiacheng: DecorView height: 2160 px
2018-10-08 13:38:03.230 /? D/shijiacheng: Screen height: 2160 px
2018-10-08 13:38:03.230 /? D/shijiacheng: NativeBar height: 66 px
2018-10-08 13:38:03.230 /? D/shijiacheng: ContentView height: 1964 px
2018-10-08 13:38:03.230 /? D/shijiacheng: NavigationBar height: 130 px
2018-10-08 13:38:03.230 /? D/shijiacheng: --------------------------

Topics: Android Mobile Windows xml