原理
px值 = dp值 * metrics.density,这里的density是指的手机的屏幕密度,由系统提供,不同的手机的density可能不同,所以我们不能直接使用系统的density,通过修改density值,强行把所有不同尺寸分辨率的手机的宽度dp值改成一个统一的值,这样就解决了所有的适配问题。
比如,原设备分辨率为1280×800,其像素密度为320dpi,设计稿宽度就是是1280px,那么开发这边就会把目标dp值设为640dp,在不同的设备中,动态修改density值,从而保证(手机像素宽度)px/density这个值始终是640dp,这样的话,就能保证UI在不同的设备上表现一致了。
如果UI设计图是按屏幕宽度为640dp来设计的,那么在1024×600分辨率的设备上,其修改后的像素密度则为(1024/640)*160=256dpi,屏幕宽度经过计算则为1024/(256/160)=640dp,这就和我们呢UI设计图保持一致了,这样就实现了统一设计图,可以在不同分辨率的屏幕上适配了。
实现
①
public class DisplayUtil {
private static float sDensity;
private static float sScaledDensity;
public static void setCustomDensity(@NonNull Activity activity, @NonNull final Application application) {
DisplayMetrics appDisplayMetrics = new DisplayMetrics();
activity.getWindowManager().getDefaultDisplay().getRealMetrics(appDisplayMetrics);
if (sDensity == 0) {
sDensity = appDisplayMetrics.density;
sScaledDensity = appDisplayMetrics.scaledDensity;
// 防止系统切换后不起作用
application.registerComponentCallbacks(new ComponentCallbacks() {
@Override
public void onConfigurationChanged(Configuration newConfig) {
if (newConfig != null && newConfig.fontScale > 0) {
sScaledDensity = application.getResources().getDisplayMetrics().scaledDensity;
}
}
@Override
public void onLowMemory() {
}
});
}
float targetDensity = (float) appDisplayMetrics.widthPixels / (float) 640;
// 防止字体变小
float targetScaleDensity = targetDensity * (sScaledDensity / sDensity);
int targetDensityDpi = (int) (160 * targetDensity);
appDisplayMetrics.density = targetDensity;
appDisplayMetrics.scaledDensity = targetScaleDensity;
appDisplayMetrics.densityDpi = targetDensityDpi;
final DisplayMetrics activityDisplayMetrics = activity.getResources().getDisplayMetrics();
activityDisplayMetrics.density = targetDensity;
activityDisplayMetrics.scaledDensity = targetScaleDensity;
activityDisplayMetrics.densityDpi = targetDensityDpi;
}
}
该实现是基于设计图宽度为640dp进行适配的,假如UI设计图不一致,只需更改对应的值即可。除此之外,我们也可以高度进行适配,只需替换一行代码即可,例如以高度400dpj进行适配,如下
float targetDensity = (float) appDisplayMetrics.heightPixels / (float) 400;
其中需要注意的是,android 4.4(sdk 19)之后,获取屏幕高度需要考虑底部导航栏等decor,getWindowManager().getDefaultDisplay().getMetrics()等传统方法获取的宽高是不正确的,和adb shell wm size可以查询到设备的物理尺寸不一致,应使用getRealMetrics()方法获取屏幕实际的物理尺寸。
②
只需要在所需的Activity中,或创建一个公共baseActivity的onCreate中调用即可。
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
DisplayUtil.setCustomDensity(this, getApplication());
}