屏幕适配原理简单说下
- 尺寸单位介绍
dpi
:屏幕密度,屏幕上每英寸上有多少像素点.屏幕对角线上的像素点总数除以屏幕对角线长度得到.dip
:也就是最常用的dp
单位,屏幕上每英寸有160个点,每个点如果代表1像素,那么1dp就等于1像素.
- 问题来了,假如屏幕上每英寸有320个像素,屏幕宽是1080像素,此时该屏幕宽是多少dp?
1. 屏幕每英寸320像素,如果用160个点去覆盖这些像素,每个点可以代表2个像素,也就是2个像素代表1dp,屏幕此时有1080像素,就需要用540个点覆盖,屏幕大小就是540dp.density
:每1英寸像素点除160,也就是每英寸有160个点,每个点能覆盖多少个像素.
- 解释原理
- UI图尺寸与像素都是固定的.
- 如果设计图宽为360dp,不同设备屏幕像素不同,要想360dp乘以density计算出的像素值都等于各个设备屏幕像素宽度,那么可以更改当前Activity中的density来达到目的.
看看AdaptScreenUtils
它是如何做的适配
- 它使用起来非常的简单,只要重写
Activity
中的getResources()
方法,如下:
@Override
public Resources getResources() {
// 这里是以高度为基准来适配的
return AdaptScreenUtils.adaptHeight(super.getResources(), "UI高度像素值");
}
- 看看源码
public final class AdaptScreenUtils {
public static Resources adaptHeight(final Resources resources, final int designHeight) {
// designHeight代表UI设计稿中的高度像素值
return adaptHeight(resources, designHeight, false);
}
public static Resources adaptHeight(final Resources resources, final int designHeight, final boolean includeNavBar) {
// 获取屏幕高度像素值
float screenHeight = (resources.getDisplayMetrics().heightPixels + (includeNavBar ? getNavBarHeight(resources) : 0)) * 72f;
// 以屏幕高度像素值除以设计稿中高度像素值,传入applyDisplayMetrics()方法中
float newXdpi = screenHeight / designHeight;
applyDisplayMetrics(resources, newXdpi);
return resources;
}
private static void applyDisplayMetrics(final Resources resources, final float newXdpi) {
// 将上面得到值赋值给DisplayMetrics中的xdpi变量
resources.getDisplayMetrics().xdpi = newXdpi;
Utils.getApp().getResources().getDisplayMetrics().xdpi = newXdpi;
applyOtherDisplayMetrics(resources, newXdpi);
}
// 该方法是通过反射为将resources对象中的xdpi变量改为newXdpi值.
private static void applyOtherDisplayMetrics(final Resources resources, final float newXdpi) {
if (sMetricsFields == null) {
sMetricsFields = new ArrayList<>();
Class resCls = resources.getClass();
Field[] declaredFields = resCls.getDeclaredFields();
while (declaredFields != null && declaredFields.length > 0) {
for (Field field : declaredFields) {
if (field.getType().isAssignableFrom(DisplayMetrics.class)) {
field.setAccessible(true);
DisplayMetrics tmpDm = getMetricsFromField(resources, field);
if (tmpDm != null) {
sMetricsFields.add(field);
tmpDm.xdpi = newXdpi;
}
}
}
// 包括resources父类对象中的xdpi值都要改为newXdpi值
resCls = resCls.getSuperclass();
if (resCls != null) {
declaredFields = resCls.getDeclaredFields();
} else {
break;
}
}
} else {
applyMetricsFields(resources, newXdpi);
}
}
}
xDpi
在哪儿用的上?
public class TypedValue {
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:
// 假如使用的是pt作为单位,xdpi为屏幕像素比设计稿像素,再乘以当前value值,最终的结果就是value值在屏幕中所占像素值.后面的我不太懂0.0.
// 使用pt作为单位可以避开dp单位,这样对原有的项目影响最小.
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;
}
}
注意
对于TextView
中文字大小,建议使用sp
作为单位,这样文字大小可以自动适配.然后千万不要写死TextView
尺寸大小,不然很容易出现文字比TextView
尺寸还大导致显示不全的现象.建议使用wrap_content
.