Full guide to Android screen adaptation (Part 2) - Changing screen without pressure, this is the ultimate solution for Android screen adaptation


In the previous article, we introduced the basic methods of Android screen adaptation, such as using qualifier resources, image adaptation, vector graphics, etc.

For interested friends, please go to: Full introduction to Android screen adaptation (middle) - from the nine grid to the vector map, revealing the correct opening method of Android multi screen adaptation .


However, with the continuous innovation of smart phone screen form, these basic practices are not enough. Developers must further master more professional adaptation skills to meet the challenges of changing screens. This article will focus on font scaling adaptation Android 9.0 new screen support, special-shaped full screen and other contents share the ultimate solution of Android screen adaptation for you.


1、 Font zoom fit


The Android system allows users to freely adjust the font size of the system in order to bring them an excellent accessibility experience. Therefore, as a developer, we must ensure that the application can be perfectly compatible with various font scaling scenarios, which can meet user needs and will not damage the interface layout.


1. Dynamically calculate and set font size in code to adapt to different screen density and size


 public  class  MainActivity  extends  AppCompatActivity  {
     private  static  final  float  BASE_FONT_SIZE  =  16f ;  //Base font size, in SP
     private  static  final  float  BASE_SCREEN_WIDTH  =  360f ;  //Base screen width, in DP

     @Override
     protected  void  onCreate ( Bundle savedInstanceState )  {
         super . onCreate ( savedInstanceState ) ;
         setContentView ( R . layout . activity_main ) ;

         //Get screen width
         int screenWidth =  getScreenWidth ( this ) ;

         //Calculate dynamic font size
         float dynamicFontSize =  calculateDynamicFontSize ( screenWidth ) ;

         //Set the font size of TextView
         TextView textView =  findViewById ( R . id . text_view ) ; textView . setTextSize ( TypedValue . COMPLEX_UNIT_SP , dynamicFontSize ) ;
     }

     /** *Get screen width (unit: DP) */
     private  static  int  getScreenWidth ( Context context )  {
         DisplayMetrics displayMetrics = context . getResources ( ) . getDisplayMetrics ( ) ;
         return  ( int )  ( displayMetrics . widthPixels / displayMetrics . density ) ;
     }

     /** *Calculate dynamic font size (unit: SP) */
     private  static  float  calculateDynamicFontSize ( float screenWidth )  {
         return  BASE_FONT_SIZE  *  ( screenWidth /  BASE_SCREEN_WIDTH ) ;
     }
 }

In this example:

  • We have defined the base font size BASE_FONT_SIZE 16sp, and the reference screen width BASE_SCREEN_WIDTH 360 dp.
  • stay onCreate Method, we first obtain the width of the current screen (in dp), and then call calculateDynamicFontSize Method to calculate the dynamic font size (unit: sp).
  • Finally, we set the calculated dynamic font size to TextView On.

calculateDynamicFontSize The implementation principle of the method is as follows:

  • We assume that the base font size 16sp corresponds to the base screen width 360dp.
  • When the screen width changes, we can calculate the new font size according to the proportion of the screen width.
  • The specific calculation formula is: dynamicFontSize = BASE_FONT_SIZE * (screenWidth / BASE_SCREEN_WIDTH) , where screenWidth Is the width of the current screen (in dp).

In this way, we can automatically adjust the font size dynamically according to the screen size, so as to ensure that the text display effect on different devices can be more reasonable.


2. Scale text in code


It mainly involves the following aspects:

  • Using sp as the font unit Android recommends using sp (scale independent pixels) as the font unit, because sp will scale according to the user's font size settings to ensure that the text size is reasonable on different devices.

  • Inherit the Application class and override the attachBaseContext method. When the application starts, you can override the attachBaseContext Method, the font size of the entire application is scaled and adapted here.

  • The TextUtil tool class Android provides TextUtil Tool class, which can scale text content. Call the TextUtil.scale(CharSequence source, float proportion) Method.


The following is a complete Java code example:

 public  class  MyApplication  extends  Application  {
     private  static  final  float  DEFAULT_FONT_SCALE  =  1.0f ;

     @Override
     protected  void  attachBaseContext ( Context base )  {
         super . attachBaseContext ( base ) ;
         //Get the font scaling of the current system
         float fontScale = base . getResources ( ) . getConfiguration ( ) . fontScale ;
         //Calculate the scale to be scaled
         float scale = fontScale /  DEFAULT_FONT_SCALE ;
         //Scales the font of the entire application
         ResourcesCompat . getFont ( base ) . setCompatibilityScaling ( scale ) ;
     }
 }

 public  class  MainActivity  extends  AppCompatActivity  {
     @Override
     protected  void  onCreate ( Bundle savedInstanceState )  {
         super . onCreate ( savedInstanceState ) ;
         setContentView ( R . layout . activity_main ) ;

         TextView textView =  findViewById ( R . id . text_view ) ;
         //Scale the text of a single TextView textView . setText ( TextUtils . scale ( textView . getText ( ) ,  1.2f ) ) ;
     }
 }


In this example:

  • Custom MyApplication Class overrides attachBaseContext Method to obtain the font scaling ratio of the current system, and scale and adapt the font of the entire application.

  • stay MainActivity , we use TextUtils.scale() Method vs. single TextView The text of is scaled 1.2 times.


3. Set the autoSizeTextType property of the layout root element

 <LinearLayout ... app:autoSizeTextType="uniform"> <TextView android:textSize="16sp" ... /> </LinearLayout>

AutoSizeTextType enables all text in the layout to be automatically resized to fully fit the height and width of the layout.


II New screen support from Android 9.0


1. Support irregular full screen

After the official launch of Android 9.0 (Pie), Google has made a comprehensive upgrade in screen display, the most important of which is the support for heterogeneous full screen.

Heteromorphic full screen refers to the screen design with grooves or bulges around the screen, which is common in iPhone X, Samsung Galaxy S9 and other mobile phones. Android 9.0 provides a series of APIs and features to support this screen form, including:

  • DisplayCutout API : This API allows developers to obtain information about the "groove" area on the screen, including location, size, etc., so that it can better adapt to the application interface.

  • Window Insets : Android 9.0 also provides the Window Insets mechanism, which allows developers to know the area where the current window is blocked by the system UI, so as to adjust the interface layout.

  • Note friendly navigation bar: The navigation bar of Android 9.0 can automatically adapt to the special-shaped screen, and will automatically avoid when there are grooves.


(1) . Use DisplayCutout API to adapt to a full screen example

 public  class  MainActivity  extends  AppCompatActivity  {
     @Override
     protected  void  onCreate ( Bundle savedInstanceState )  {
         super . onCreate ( savedInstanceState ) ;
         setContentView ( R . layout . activity_main ) ;

         //Get DecorView of Window
         final  View decorView =  getWindow ( ) . getDecorView ( ) ;

         //Set OnApplyWindowInsets listener decorView . setOnApplyWindowInsetsListener ( new  View . OnApplyWindowInsetsListener ( )  {
             @Override
             public  WindowInsets  onApplyWindowInsets ( View v ,  WindowInsets insets )  {
                 //Get the "notch" area around the screen
                 DisplayCutout displayCutout = insets . getDisplayCutout ( ) ;
                 if  ( displayCutout !=  null )  {
                     //Get the location and size of the "groove"
                     Rect safeInsets = displayCutout . getSafeInsetLeft ( ) , safeInsets = displayCutout . getSafeInsetTop ( ) , safeInsets = displayCutout . getSafeInsetRight ( ) , safeInsets = displayCutout . getSafeInsetBottom ( ) ;

                     //Adjust the interface layout according to the "groove" information
                     adjustLayoutForCutout ( decorView , safeInsets ) ;
                 }

                 //Return the insets information for the system to continue processing
                 return insets ;
             }
         } ) ;
     }

     private  void  adjustLayoutForCutout ( View view ,  Rect safeInsets )  {
         //Adjust the inner margin of the view according to the "Groove" information view . setPadding ( safeInsets . left , safeInsets . top , safeInsets . right , safeInsets . bottom ) ;

         //If the view is a ViewGroup, adjust the child view recursively
         if  ( view instanceof  ViewGroup )  {
             ViewGroup viewGroup =  ( ViewGroup ) view ;
             for  ( int i =  zero ; i < viewGroup . getChildCount ( ) ; i ++ )  {
                 adjustLayoutForCutout ( viewGroup . getChildAt ( i ) , safeInsets ) ;
             }
         }
     }
 }

In this example:

  • We are Window Of DecorView Set OnApplyWindowInsetsListener Listener, in which you can get the information about the "notch" of the screen.
  • We passed DisplayCutout Obtain the position and size of the "groove" with Rect And pass these information to adjustLayoutForCutout method.
  • adjustLayoutForCutout Method will traverse DecorView And all its children View`, And adjust their inner margin according to the "groove" information, so as to avoid the content being blocked by the "groove".

In this way, we can ensure that the application interface can perfectly adapt to the heterogeneous full screen, and the content will not be blocked by "grooves".


(2) An example of using Window Insets to fit a heterogeneous full screen

 public  class  MainActivity  extends  AppCompatActivity  {
     private  static  final  String  TAG  =  "MainActivity" ;

     @Override
     protected  void  onCreate ( Bundle savedInstanceState )  {
         super . onCreate ( savedInstanceState ) ;
         setContentView ( R . layout . activity_main ) ;

         //Get DecorView of Window
         final  View decorView =  getWindow ( ) . getDecorView ( ) ;

         //Set OnApplyWindowInsets listener decorView . setOnApplyWindowInsetsListener ( new  View . OnApplyWindowInsetsListener ( )  {
             @Override
             public  WindowInsets  onApplyWindowInsets ( View v ,  WindowInsets insets )  {
                 //Get the area blocked by the system UI
                 Rect systemWindowInsets =  new  Rect ( insets . getSystemWindowInsetLeft ( ) , insets . getSystemWindowInsetTop ( ) , insets . getSystemWindowInsetRight ( ) , insets . getSystemWindowInsetBottom ( )
                 ) ;

                 //Get the "safe area" (i.e., the area not covered)
                 Rect displayCutoutSafeInsets = insets . getDisplayCutout ( )  !=  null
                         ? insets . getDisplayCutout ( ) . getSafeInsets ( )
                         :  new  Rect ( ) ;

                 //Print debugging information
                 Log . d ( TAG ,  "System UI insets: "  + systemWindowInsets ) ;
                 Log . d ( TAG ,  "Display cutout safe insets: "  + displayCutoutSafeInsets ) ;

                 //Adjust the interface layout according to the insets information
                 adjustLayoutForInsets ( decorView , systemWindowInsets , displayCutoutSafeInsets ) ;

                 //Return the insets information for the system to continue processing
                 return insets ;
             }
         } ) ;
     }

     private  void  adjustLayoutForInsets ( View view ,  Rect systemWindowInsets ,  Rect displayCutoutSafeInsets )  {
         //Adjust the inner margin of the view according to the blocked area of the system UI view . setPadding ( systemWindowInsets . left + displayCutoutSafeInsets . left , systemWindowInsets . top + displayCutoutSafeInsets . top , systemWindowInsets . right + displayCutoutSafeInsets . right , systemWindowInsets . bottom + displayCutoutSafeInsets . bottom ) ;

         //If the view is a ViewGroup, adjust the child view recursively
         if  ( view instanceof  ViewGroup )  {
             ViewGroup viewGroup =  ( ViewGroup ) view ;
             for  ( int i =  zero ; i < viewGroup . getChildCount ( ) ; i ++ )  {
                 adjustLayoutForInsets ( viewGroup . getChildAt ( i ) , systemWindowInsets , displayCutoutSafeInsets ) ;
             }
         }
     }
 }

In this example:

  • We are Window Of DecorView Set OnApplyWindowInsetsListener Listener, in which you can obtain the area blocked by the system UI and the "safe area" (i.e., the area not blocked).

  • We passed WindowInsets Object's getSystemWindowInsetXXX() Method to obtain the area blocked by the system UI, and use the getDisplayCutout().getSafeInsets() Method to get the Security Zone.

  • We will transfer the information of these two regions to adjustLayoutForInsets Method, which will traverse DecorView And all its children View , and adjust their inner margin according to the occluded region information to avoid the content being occluded.

In this way, we can ensure that the application interface can perfectly adapt to the heterogeneous full screen, and the content will not be blocked by the system UI.


(3)、 Example of note friendly navigation bar adapting to heterogeneous full screen


Android 9.0 (Pie) has introduced a "notch friendly" navigation bar, which can automatically adapt to the heterogeneous full screen. Developers can set WindowManager.LayoutParams To let the application's navigation bar automatically fit into the screen Notch.

 public  class  MainActivity  extends  AppCompatActivity  {
     @Override
     protected  void  onCreate ( Bundle savedInstanceState )  {
         super . onCreate ( savedInstanceState ) ;
         setContentView ( R . layout . activity_main ) ;

         //Get LayoutParams of Window
         WindowManager . LayoutParams layoutParams =  getWindow ( ) . getAttributes ( ) ;

         //Set layoutParams to FLAG_LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES
         //This allows the navigation bar to automatically fit into the screen "notch" layoutParams . layoutInDisplayCutoutMode =  WindowManager . LayoutParams . LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES ;
         getWindow ( ) . setAttributes ( layoutParams ) ;
     }
 }

In this example, we have mainly done the following steps:

  • Get Current Window Of LayoutParams
  • take layoutInDisplayCutoutMode Property set to LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES This value indicates that the navigation bar of the application will automatically adapt to the "groove" of the screen to avoid the content being blocked.
  • The modified LayoutParams apply to Window On.

In this way, we can ensure that the navigation bar of the application can automatically adapt to the heterogeneous full screen, and the content will not be blocked by "grooves".


It is worth noting that in addition to setting layoutInDisplayCutoutMode Attribute, Android 9.0 also provides several other adaptation modes, which developers can choose according to their needs:

  • LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT : Default mode, the system will automatically fit the "groove".
  • LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER : Never let content enter the "groove" area.
  • LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES : Fits the "notch", but only takes effect when the "notch" appears on the short side (top or bottom) of the screen.
  • LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS : Always let content enter the "groove" area.

Choosing the appropriate mode according to specific needs can make the application interface better adapt to the heterogeneous full screen.


III Android 10 supports collapsible device multi window mode


Android 10 and above has introduced multi window mode support for foldable devices. Developers can use these features to provide users with a better experience.

The following code is an example of folding device multi window mode adaptation:

 public  class  MainActivity  extends  AppCompatActivity  {
     private  static  final  String  TAG  =  "MainActivity" ;

     @Override
     protected  void  onCreate ( Bundle savedInstanceState )  {
         super . onCreate ( savedInstanceState ) ;
         setContentView ( R . layout . activity_main ) ;

         //Get DecorView of Window
         final  View decorView =  getWindow ( ) . getDecorView ( ) ;

         //Set OnApplyWindowInsets listener decorView . setOnApplyWindowInsetsListener ( new  View . OnApplyWindowInsetsListener ( )  {
             @Override
             public  WindowInsets  onApplyWindowInsets ( View v, WindowInsets insets )  {
                 //Get window mode information
                 int windowingMode = insets . getWindowingMode ( ) ;

                 //Adjust the interface layout according to the window mode
                 adjustLayoutForWindowingMode ( decorView, windowingMode ) ;

                 //Return the insets information for the system to continue processing
                 return insets ;
             }
         } ) ;
     }

     private  void  adjustLayoutForWindowingMode ( View view, int windowingMode )  {
         switch  ( windowingMode )  {
             case  WINDOWING_MODE_MULTI_WINDOW :
                 //Layout adjustment in multi window mode
                 handleMultiWindowMode ( view ) ;
                 break ;
             case  WINDOWING_MODE_FREEFORM :
                 //Layout adjustment in free mode
                 handleFreeformMode ( view ) ;
                 break ;
             default :
                 //Layout adjustment in full screen mode
                 handleFullscreenMode ( view ) ;
                 break ;
         }

         //If the view is a ViewGroup, adjust the child view recursively
         if  ( view instanceof  ViewGroup )  {
             ViewGroup viewGroup =  ( ViewGroup ) view ;
             for  ( int i =  zero ; i < viewGroup . getChildCount ( ) ; i ++ )  {
                 adjustLayoutForWindowingMode ( viewGroup . getChildAt ( i )  windowingMode ) ;
             }
         }
     }

     private  void  handleMultiWindowMode ( View view )  {
         //Layout adjustment logic in multi window mode, such as:
         //- Resize font
         //- Resize controls
         //- Adjust control spacing
         // - ...
     }

     private  void  handleFreeformMode ( View view )  {
         //Layout adjustment logic in free mode, such as:
         //- Resize font
         //- Resize controls
         //- Adjust control spacing
         // - ...
     }

     private  void  handleFullscreenMode ( View view )  {
         //Layout adjustment logic in full screen mode, such as:
         //- Restore font size
         //- Restore control size
         //- Restore control spacing
         // - ...
     }
 }

In this example:

  • We are Window Of DecorView Set OnApplyWindowInsetsListener Listener, in which the current window mode is obtained.
  • We passed WindowInsets Object's getWindowingMode() Method to get the current window mode and pass it to the adjustLayoutForWindowingMode method.
  • adjustLayoutForWindowingMode Method will call the corresponding processing method according to the current window mode (multi window, free mode or full screen mode)( handleMultiWindowMode handleFreeformMode or handleFullscreenMode )。
  • In these processing methods, we can adjust the interface layout according to different window modes, such as adjusting font size, control size, control spacing, etc.

In this way, we can ensure that the application interface can provide a good user experience in different window modes.

It should be noted that different foldable devices may have different window mode support, and developers need to adapt and adjust according to specific devices and scenarios. At the same time, you can use the device simulator of Android Studio to test and debug.


4、 Best practices for screen adaptation

In addition to the above key points, we also need to pay attention to the following screen adaptation best practices:

  • Try to use constrained layout ConstraintLayout to reduce nesting levels

  • Properly use hard coded values to avoid misuse of resource files

  • Use absolute coordinates carefully, and pay attention to horizontal and vertical screen switching

  • Reduce overuse of wrap_content

  • Use the tools namespace tag to simulate data for easy preview and adjustment

  • Keep up with new Android features and screen innovation


Conclusion:

With the emergence of new forms such as folding mobile phones and ring screens, Android screen adaptation will also face increasing challenges. As developers, we need to constantly learn and innovate to keep up with the pace of screen innovation in order to present an unparalleled experience for users. What are your expectations for Android's future screen support? Let's wait and see, and work together to create the best screen adaptation practice!


  • twenty-one
    give the thumbs-up
  • step on
  • eleven
    Collection
    Think it's good? One click collection
  •  Reward
    Reward
  • zero
    comment

Is "relevant recommendation" helpful to you?

  • Very unhelpful
  • No help
  • commonly
  • to be helpful to
  • Very helpful
Submit
comment
Add Red Packet

Please fill in the red envelope greeting or title

individual

The minimum number of red packets is 10

element

The minimum amount of red packet is 5 yuan

Current balance three point four three element Go to recharge>
To be paid: ten element
Achieve 100 million technicians!
After receiving, you will automatically become a fan of the blogger and the red envelope owner rule
hope_wisdom
Red packet sent

Reward the author

W rain or shine w

Your encouragement will be the greatest impetus for my creation

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
Scan code for payment: ¥1
Obtaining
Scan code for payment

Your balance is insufficient, please change the scanning code to pay or Recharge

Reward the author

Paid in element
Payment with balance
Click to retrieve
Scan code for payment
Wallet balance zero

Deduction description:

1. The balance is the virtual currency of wallet recharge, and the payment amount is deducted at a ratio of 1:1.
2. The balance cannot be directly purchased and downloaded, but VIP, paid columns and courses can be purchased.

Balance recharge