Rough and fast Android full screen adaptation scheme

1. Status Quo

Due to the serious fragmentation of Android, screen adaptation has always been a headache in development. In the face of various screen sizes and resolutions on the market, Android's adaptation scheme based on dp and res directory names can no longer meet the needs of writing full-screen adaptation at one time. In order to achieve the best visual effect, it is always necessary in the development process. It takes more resources to adapt. There are also developers who have given some solutions of their own. First, let's analyze the status quo of some common solutions:


1. Official adaptation plan  

  • dp. dp is a unit unique to Android development. Unlike px, dp is a unit based on screen pixel density. Maybe 1dp=1px on a low density screen, but 1dp=4px on a high density screen. When writing layout xml, if the length and width of a control are specified by dp, it can ensure that the absolute size of the control is roughly equivalent under screens of various sizes and resolutions. That is to say, whether under the pad or under the large and small screen mobile phone, the size of the control we actually see is similar:

  • The resource directory name. It can be seen from the above figure that although the use of dp ensures that the absolute size of the control is consistent in different screens. The advantage of this is that on screens of similar size, no matter how large the resolution is, it will not affect the layout; but when the screen sizes are quite different, just guaranteeing the absolute size of the control looks problematic. In the res directory, you can add suffixes such as '-1920x1080' to each resource directory to adapt to different screens. The specific rules can be found in the official website documentation. This can provide different layouts for different screens, and even provide two completely different layout styles for pads and mobile phones. But usually, designers do not provide different design drawings for different screens. Their demand is only that the relative size of the controls under different screens is consistent with the screen, so dp cannot meet this point, and it is suitable for various screens. Again and again, it is slightly cumbersome, and the modification is also more troublesome. Usually the adaptation we need is like this:




  • Percentage layout support library. Not used, but deprecated in API level 26.0.0-beta1.

  • ConstraintLayout. The recommended layout after the percentage support library has been deprecated seems a little more complicated.

2. Player adaptation plan

        The purpose of adaptation for the majority of players is very clear. The purpose is to ensure that the relative size of the controls on different screens is consistent and looks the same. Take the two adaptation schemes of a great player as an example:

  • Poor scalability. For each ViewGroup, it is necessary to write the corresponding AutoLayout for expansion, and for each attribute of each View that needs to be adapted, code must be written for adaptation and expansion;

  • Numerical calculations are performed in the onMeasure stage. It consumes performance, and this has many unreasonable properties for non-LayoutParams properties. For example, when the textSize of the TextView is converted and setTextSize during onMeasure, the textSize dynamically set by the player in the code will be invalid, because it will be reset and overwritten by AutoLayout every time onMesasure.

  • There are many issues and the author is no longer maintained.

  • Option One. Write a script to convert the length to the length of each resolution, the disadvantage is that it is difficult to cover all resolutions on the market.

  • Option II. AutoLayout support library. The idea of ​​this library is very good: compare the design drawing, use px to write the layout, without affecting the preview; in the drawing stage, the px value calculation of the corresponding design drawing is converted into the size suitable for the current screen; in order to simplify the access, the inflate automatically converts each The Layout is converted to the corresponding AutoLayout so that no changes in all the xml are required. But at the same time, the library also has the following problems:

2. Ideas

For screens with large differences in size, the same design scheme should not be used, otherwise the advantages of large screens will not be fully reflected, and the official adaptation scheme seems to express this meaning. However, in actual design and development, for an ordinary app, few projects have the will and energy to design and develop a set of design solutions for each screen to adapt to it.

Usually a simple adaptation requirement is: if the width of the design drawing is 200, and the length of a control marked on the design drawing is 3, then the length of the control is equivalent to 3/200 of the total width, then we hope that on any size screen The length of the control above is 3/200 of the screen width.

Personally, I think the design idea of ​​AutoLayout is very good, but the scheme of using LayoutParams and attributes as the entry point to perform conversion calculation in the mesure process has problems in terms of efficiency and scalability. So where is the end for Android to calculate the length, and can it be converted when Android calculates the length? If it can be converted when Android calculates the length, then there is no need for a series of redundant calculations and adaptations, and all problems will be solved.

After some searching, I found that the system's length calculation function is TypedValuein the middle, and the applyDimensionunit and value are passed in to calculate it as the corresponding px value.

 
 

public static float applyDimension(int unit, float value,                                       DisplayMetrics metrics)    {
       switch (unit) {
       case COMPLEX_UNIT_PX:
             return value;
       case COMPLEX_UNIT_DIP:
             return value * metrics.density;        
       case COMPLEX_UNIT_SP:            
             return value * metrics.scaledDensity;        
       case COMPLEX_UNIT_PT:            
             return value * metrics.xdpi * (1.0f/72);        
       case COMPLEX_UNIT_IN:            
             return value * metrics.xdpi;        
       case COMPLEX_UNIT_MM:            
             return value * metrics.xdpi * (1.0f/25.4f);        }        
       return 0;    }

  • 可以看见换算方法非常简单,而DisplayMetrics的所有属性都是public的,不用反射就能修改;

  • pt的原意是长度单位磅,根据当前屏幕与设计图尺寸将metrics.xdpi进行修改就可以实现将pt这个单位重定义成我们所需要的相对长度单位,使修改之后计算出的1pt实际对应的px/屏幕宽度px=1px/设计图宽度px

  • 而这个DisplayMetrics从哪来?从源码中可以看出一般为mContext.getResources().getDisplayMetrics(),这个mContext即为所在Activity;

  • 横竖屏切换等Configuration的变化会导致DisplayMetrics的重新计算还原;

  • px,dp与sp都是平时常用的单位,而pt,in与mm几乎没有看见过,从这些不常见的单位下手正好可以不影响其他常用的单位。

基于以上几点,便有了以下方案。

三、方案

本适配方案的目标是:完全按照设计图上标注的尺寸来编写页面,所编写的页面在所有大小与分辨率的屏幕上都表现一致,即控件在所有屏幕上相对于整个屏幕的相对大小都一致(看起来只是将设计图等比缩放至屏幕宽度大小)。

  • 核心。使用冷门的pt作为长度单位,按照上述想法将其重定义为与屏幕大小相关的相对单位,不会对dp等常用单位的使用造成影响。

  • 绘制。编写xml时完全对照设计稿上的尺寸来编写,只不过单位换为pt。假如设计图宽度为200,一个控件在设计图上标注的长度为3,只需要在初始化时定义宽度为200,绘制该控件时长度写为3pt,那么在任何大小的屏幕上该控件所表现的长度都为屏幕宽度的3/200。如果需要在代码中动态转换成px的话,使用TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PT, value, metrics)

  • 预览。实时预览时绘制页面是很重要的一个环节。以1334x750的设计图为例,为了实现于正常绘制时一样的预览功能,创建一个长为1334磅,宽为750磅的设备作为预览,经换算约为21.5英寸((sqrt(1334^2+750^2))/72)。预览时选择这个设备即可。


  • 代码处理。在activityonCreate时修改DisplayMetrics即可,推荐写在基类或ActivityLifecycleCallbacks中,参考github demo。

      Point size = new Point();  activity.getWindowManager().getDefaultDisplay().getSize(size);  context.getResources().getDisplayMetrics().xdpi = size.x / designWidth * 72f;

这样绘制出来的页面就跟设计图几乎完全一样,无论大小屏上看起来就只是将设计图缩放之后的结果。

适配前(左图API19 400x800, 右图API24 1440x2560):


适配后(左图API19 400x800, 右图API24 1440x2560):

虽然方案比较简单,但是为了方便使用也整理成了一个library,代码及demo见https://github.com/Firedamp/Rudeness

欢迎大家一起讨论完善,如有问题请统一提github issue方便汇总与处理。

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325652193&siteId=291194637