preface
Due to the adaptation of the article before more attention, and previous schemes are more pit, but this has been my ponder this week to find the optimal solution, worry you still stay in the previous way of adaptation, so in the Denver nuggets can only rely on Shared links to remind you to check the latest updated version, adaptation apologize for before still immature, Please read the following again.
A month ago, I saw toutiao’s new screen adaptation, portal, and I was amazed. I wanted to incorporate it into my utility class with a single line of code. AndroidUtilCode 1.19.0 has its latest adaptation. The related functions are in ScreenUtils, and the related API is as follows:
Vertical sliding screen adaptScreen4HorizontalSlide adaptScreen4VerticalSlide: adaptation: adaptation level sliding screen cancelAdaptScreen: cancel the adapter screen isAdaptScreen: Screen fitCopy the code
The effect
The ScreenAdaptActivity in UtilApk ADAPTS the design drawing to 360DP width. We set two views to 180DP width as follows:
public class ScreenAdaptActivity extends BaseActivity { private TextView tvUp; private TextView tvDown; public static void start(Context context) { Intent starter = new Intent(context, ScreenAdaptActivity.class); context.startActivity(starter); } @Override public void initData(@Nullable Bundle bundle) { if (ScreenUtils.isPortrait()) { ScreenUtils.adaptScreen4VerticalSlide(this, 360); } else { ScreenUtils.adaptScreen4HorizontalSlide(this, 360); } } @Override public int bindLayout() { return R.layout.activity_screen_adapt; } @Override public void initView(Bundle savedInstanceState, View contentView) { } @Override public void doBusiness() { } @Override public void onWidgetClick(View view) { } public void toggleFullScreen(View view) { ScreenUtils.toggleFullScreen(this); } @Override protected void onDestroy() { ScreenUtils.cancelAdaptScreen(this); super.onDestroy(); }}Copy the code
Its effect at 1080×1920 420dpi(xxhdPI) is shown below:
Its effect at 768×1280 320dpi(XHDPI) is shown below:
Its effect at 480×800 240dpi(HDPI) is shown below:
Its effect at 320×480 160dpi(MDPI) is shown as follows:
This is the effect of 360dp width for portrait screen and 360dp height for widescreen screen.
The principle of
If you’ve seen the adaptation of today’s headline above, you probably already know how it works, but if you don’t, you can continue to read my explanation: We know px = DP * density, we need to make sure that DP does not change the density, and android default density = Dpi / 160, which means the number of px in 1DP, that is, the pixel density, we developed according to a design draft. Is there any way to make density relate to the size of the design? Suppose the width of our design is 1080px and the resources are placed in xxhdPI, then we convert the width to DP to 1080/3 = 360DP, and the width should be 360DP on different devices. DensityDpi, scaledDensity = screenWidthPx / 360 DensityDpi and scaledDensity can be modified according to density equal ratio.
Since API 26 and above Activity#getResources()#getDisplayMetrics() and Application#getResources()#getDisplayMetrics() are different references, So adapting to API 26 and above has no effect, But below API 26 Activity#getResources()#getDisplayMetrics() and Application#getResources()#getDisplayMetrics() are the same references, Thanks to @mirkowu for asking the question, there will be a solution later.
However, since the dp size of the design drawing was passed in before, such as 360DP of XXHDPI, if the width of a button you wrote is 36DP, it needs to be changed to 36/3 = 12DP for adaptation, and the font size also needs to be changed to the size divided by 3. In this way, one-step access cannot be achieved, so I changed to direct input and take MDPI as a special case for adaptation, for example, 360DP of XXHDPI mentioned above, then 360 * 3 = 1080DP under MDPI, so we will create an MDPI device with a width of 1080px. Then switch to the device to preview the layout, which perfectly solves the above problems. When we write the layout, the design drawing is 36px, so we can directly write 36DP, and the font of the design drawing is 24px, we can directly write 24SP, so as to achieve the same effect as the design drawing.
Not only that, if the third-party SDK is connected with an interface or View, the size of the interface will be completely wrong, because the width of the interface is only 360DP after our adaptation, and the layout in the third-party SDK may be larger than 360DP, which will cause new problems. The change I made above solves this problem incidentally. In addition, the image resources can be placed under the highest DPI that needs to be adjusted, such as drawable-xxhdpi or drawable-xxxhdpi, which will not cause distortion on the HD screen.
However, this will cause problems in obtaining the height of the status bar and navigation bar. The code for obtaining the height of the status bar is as follows:
public static int getStatusBarHeight() {
Resources resources = Utils.getApp().getResources();
int resourceId = resources.getIdentifier("status_bar_height", "dimen", "android");
return resources.getDimensionPixelSize(resourceId);
}Copy the code
Since Application#getResources is used, this results in the final calculation of the status bar height using the modified density, GetSystem () gets the status bar height of the correct height, as shown in the following code:
public static int getStatusBarHeight() {
Resources resources = Resources.getSystem();
int resourceId = resources.getIdentifier("status_bar_height", "dimen", "android");
return resources.getDimensionPixelSize(resourceId);
}Copy the code
The same is true for the height of the navigation bar.
Given resources.getSystem (), wouldn’t it be easier to adapt Activity#getResources()#getDisplayMetrics() and Application#getResources()#getDisplayMetrics(), Density, densityDpi, scaledDensity, densityDpi, scaledDensity, densityDpi, scaledDensity, densityDpi, scaledDensity, densityDpi, scaledDensity And at the time of modified system font, Resources# getSystem () # getDisplayMetrics () will change accordingly, so also do not need to register registerComponentCallbacks to monitor the change of the system font, So the final source code is very simple, but the problem is very complicated, between light tools I will update these days a lot of version to solve the problem, from 1.18.0 to 1.18.7, there are six versions are related to the adaptation, but eventually find a solution perfectly, also want to thank you for your help, its ultimate source as shown below:
/** * Adapt the screen for vertical slide. * * @param activity The activity. * @param designWidthInPx The size of design diagram's width, in pixel. */ public static void adaptScreen4VerticalSlide(final Activity activity, final int designWidthInPx) { adaptScreen(activity, designWidthInPx, true); } /** * Adapt the screen for horizontal slide. * * @param activity The activity. * @param designHeightInPx The size of design diagram's height, in pixel. */ public static void adaptScreen4HorizontalSlide(final Activity activity, final int designHeightInPx) { adaptScreen(activity, designHeightInPx, false); } /** * Reference from: https://mp.weixin.qq.com/s/d9QCoBP6kV9VSWvVldVVwA */ private static void adaptScreen(final Activity activity, final int sizeInPx, final boolean isVerticalSlide) { final DisplayMetrics systemDm = Resources.getSystem().getDisplayMetrics(); final DisplayMetrics appDm = Utils.getApp().getResources().getDisplayMetrics(); final DisplayMetrics activityDm = activity.getResources().getDisplayMetrics(); if (isVerticalSlide) { activityDm.density = activityDm.widthPixels / (float) sizeInPx; } else { activityDm.density = activityDm.heightPixels / (float) sizeInPx; } activityDm.scaledDensity = activityDm.density * (systemDm.scaledDensity / systemDm.dens activityDm.densityDpi = (int) (160 * activityDm.density); appDm.density = activityDm.density; appDm.scaledDensity = activityDm.scaledDensity; appDm.densityDpi = activityDm.densityDpi; } /** * Cancel adapt the screen. * * @param activity The activity. */ public static void cancelAdaptScreen(final Activity activity) { final DisplayMetrics systemDm = Resources.getSystem().getDisplayMetrics(); final DisplayMetrics appDm = Utils.getApp().getResources().getDisplayMetrics(); final DisplayMetrics activityDm = activity.getResources().getDisplayMetrics(); activityDm.density = systemDm.density; activityDm.scaledDensity = systemDm.scaledDensity; activityDm.densityDpi = systemDm.densityDpi; appDm.density = systemDm.density; appDm.scaledDensity = systemDm.scaledDensity; appDm.densityDpi = systemDm.densityDpi; } /** * Return whether adapt screen. * * @return {@code true}: yes<br>{@code false}: no */ public static boolean isAdaptScreen() { final DisplayMetrics systemDm = Resources.getSystem().getDisplayMetrics(); final DisplayMetrics appDm = Utils.getApp().getResources().getDisplayMetrics(); return systemDm.density ! = appDm.density; }Copy the code
Pit point
It’s all said in principle.
advice
This scheme can be used for both old and new projects. If a new Activity is added to an old project, you can use this scheme to adapt it, and then cancelAdaptScreen when starting other old activities. For the new project, I suggest using the use in my tool class, which can make you feel great to the extreme. Just call the adaptation code before setContentView(xx) in BaseActivity. Remember that the second parameter must be passed in the actual pixel size of the design drawing, no longer the previous DP size.
With a fixed size, then our percentage is not very good to achieve, directly write XXDP after calculation, so that on all devices are also a certain proportion, where also need to do what percentage layout? Is not so easy, more coquettish operation can wait for you to unlock.
conclusion
If my utility class is bothering you, go to AndroidUtilCode issue, and thank you for Toutiao’s solution to wear 13 on the shoulders of giants for once.
The last
Be sure to use version 1.19.0 or later for screen adaptation
Be sure to use version 1.19.0 or later for screen adaptation
Be sure to use version 1.19.0 or later for screen adaptation
Sorry for the trouble.