A Practice of Screen Adaptation
Android’s screen fragmentation problem has a long history. Generally, we use the same physical size on different devices. Such a solution does not completely restore the design draft, and the situation of individual adaptation will appear more frequently as the size of the project increases.
Solution
In May 2018, the ByteDance technical team released an article “一种极低成本的Android屏幕适配方式(A very low-cost Android screen adaptation solution)”. This solution proposes to modify the density for screen adaptation. Recently, our project has been modified and adapted regarding this solution, and it is currently operating well.
Principle
As it is describe in the article:
px = density * dp;
density = dpi / 160;
px = dp * (dpi / 160);
Generally, the design draft will fix a width value, and it may even be a design draft shared with iOS, and then the unit conversion is performed according to the platform.
如果设计图宽为360dp,想要保证在所有设备计算得出的px值都正好是屏幕宽度的话,我们只能修改 density 的值(If the design width is 360dp and we want to ensure that the px value calculated on all devices is exactly the screen width, we can only modify the density value.).
Implementation
So we can write utils file for encapsulation, as the code provided by the article, we can write a function about changing density
.
1 | private static final int DEFAULT_WIDTH = 375; |
We should run this function before setContentView()
method within Activity
‘s OnCreated()
method.
Problem
Question 1:
The system will reset the density value in some situations as WebView disappear.
Solution:
Change the density value again after the reset problem happened. For example, we can override setOverScrollMode()
method to solve the WebView problem.
Question 2:
Some components, like Bitmap, using the raw density
to calculate but not the density
we changed.
Solution:
Read the source code of Bitmap
, we can find mDensity
is the variable that using in the calculation.
1 | /** @hide */ |
And the process of initialization is as below.
1 | static int getDefaultDensity() { |
The sDefaultDensity
initialized as -1
.
1 | private static volatile int sDefaultDensity = -1; |
It can be seen that when the value of density is not passed in, the value calculated by Bitmap is DisplayMetrics.DENSITY_DEVICE
, andmDensity
itself is a variable marked with @ hide
, which means that in newer versions We can’t get and modify it through reflection. Although sDefaultDensity
is not marked, it is also on the official hidden API
list.
Therefore, the solution is as follows: On the old version of the system, we modify the value of sDefaultDensity
by reflection. On the new version, we avoid using Bitmap
directly but pass in the Density
through BitmapFactory
to generate.
Question 3:
How are third-party libraries compatible if they are not designed to that size?
Solution:
The best solution is to fork the third-party library and modify it by yourself. If this method is difficult to complete, you can consider other screen adaptation solutions, such as inserting stubs before and after calling the code of the third-party library to restore and modify the density.
Question 4:
How compatible is the solution for horizontal and vertical screen adaptation?
Solution:
Out project only supports the use of vertical screens, so the requirement for vertical screens is mandatory for all pages. This is also the task of this adaptation. Pay attention to the crash of transparent pages compatible with the Android8.0 system version. For scenes with horizontal and vertical screen requirements, you can make relevant judgments in the tool code above.