适配缘起

由于Android各种机型屏幕尺寸众多及屏幕分辨率各异的原因,很容易出现同一元素在不同手机上显示效果不同的问题。所以为了让用户获得一致的用户体验效果,使得某一元素在Android不同尺寸、不同分辨率的、不同系统的手机上保持界面上的效果一致,我们需要对各种手机屏幕进行适配!

基本概念

1
2
3
4
5
6
px = dp * density
density = DPI / 160

此方案需要水平和竖直方向分别适配:
density = 当前设备屏幕总宽度(单位为像素)/ 设计图总宽度(单位为 dp)
density = 当前设备屏幕总高度(单位为像素)/ 设计图总高度(单位为 dp)

PS:和 density 相关的还有 densityDpi、scaledDensity,我们根据 density 等比修改 densityDpi、scaledDensity

具体使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
@Override 
public void initData(@Nullable Bundle bundle) {
if (ScreenUtils.isPortrait()) {
ScreenUtils.adaptScreen4VerticalSlide(this, 360);
} else {
ScreenUtils.adaptScreen4HorizontalSlide(this, 360);
}
}


/**
* 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;
}

ScreenUtils.java

屏幕适配问题汇总及解决

webview 加载后发现 density 复原?

由于 WebView 初始化的时候会还原 density 的值导致适配失效,继承 WebView,重写如下方法:

1
2
3
4
5
@Override
public void setOverScrollMode(int mode) {
super.setOverScrollMode(mode);
ScreenUtils.restoreAdaptScreen();
}

如何让系统 View 尺寸正常?

后面提到的 Dialog 和 Toast 其实都可以用用这种解决方案,就是在 inflate 相关 View 之前调用 ScreenUtils#cancelAdaptScreen,show 之后调用 ScreenUtils#restoreAdaptScreen 即可,这样就可以让系统 View 显示正常尺寸。