目录:
1. 屏幕运算公式
2. 为什么不同的手机需要不同的适配
3.屏幕适配方案,头条的方案原理 今日头条的-------autosize
- 头条适配方案改进版本
5 .图片如何适配
1. 屏幕运算公式
1.1 屏幕指标
屏幕尺寸: 屏幕尺寸指屏幕的对角线的长度,单位是英寸,1英寸=2.54厘米
屏幕分辨率是指在横纵向上的像素点数,单位是px,1px=1个像素点。一般以纵向像素横向像素,如19601080。
屏幕像素密度是指每英寸上的像素点数,单位是dpi,即“dot per inch”的缩写。屏幕像素密度与屏幕尺寸和屏幕分辨率有关
1.2 px和dp的关系
1). dpi是像素密度,即每英寸所打印的点数。
dpi的运算方式是: dpi= √(长度像素数² + 宽度像素数²) / 屏幕对角线英寸数
例如现在有一台 “宽2英寸,长3英寸” 的设备:
- 当该设备分辨率为 320*480,则dpi值为160
- 当该设备分辨率为 640*960,则dpi值为320
所以 dpi 值越高也代表屏幕显示的画面越精细
2). dp 是一个虚拟像素单位,1 dp 约等于中密度屏幕(160dpi;“基准”密度)上的 1 像素。对于其他每个密度,Android 会将此值转换为相应的实际像素数。
所以相同dp值在不同分辨率的手机上展示的大小就基本一致
android中的dp在渲染前会将dp转为px,计算公式:
- px = density * dp;
- density = dpi / 160;
结合上面得到: px = dp * (dpi / 160)
2. 为什么不同的手机需要不同的适配
当设备的物理尺寸存在差异的时候(相同分辨率不同尺寸的平板),dp就显得无能为力了。为4.3寸屏幕准备的UI,运行在5.0寸的屏幕上,很可能在右侧和下侧存在大量的空白。而5.0寸的UI运行到4.3寸的设备上,很可能显示不下。
3.屏幕适配方案,头条的方案原理 今日头条的-------autosize
但是通常下,我们只需要以宽或高一个维度去适配,
布局中的dp值最终会转换成px,都是调用 TypedValue 的 applyDimension方法:
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:
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;
}
可以看到重点在于修改系统的 density 的值。density 代表 1dp 占当前设备多少像素,即 density = dpi / 160,它在每个设备上都是固定的。
该方案的核心在于,将不同尺寸分辨率手机的宽度dp值改成一个统一的值,从而解决屏幕适配的问题。
公式: px=dp∗density
今日头条屏幕适配方案的核心原理在于,修改 density 计算公式:
density = 当前设备屏幕总宽度(单位为像素)/ 设计图总宽度(单位为 dp)
下面假设设计图宽度是360dp,以宽维度来适配。
那么适配后 自定义density = 设备真实宽(单位px) / 360,
这样就可以保证不同分辨率的设备,宽度的dp值是一样的,然后直接按照设计图尺寸进行开发,不需要再做任何其他的适配。
public static void setCustomDensity(Application application) {
DisplayMetrics appDisplayMetrics = application.getResources().getDisplayMetrics();
//计算density,360是设计图的总宽度
float targetDensity = appDisplayMetrics.widthPixels / 360;
//计算dpi
int targetDensityDpi = (int) (160 * targetDensity);
//替换系统的 density 和 dpi
appDisplayMetrics.density = appDisplayMetrics.scaledDensity = targetDensity;
appDisplayMetrics.densityDpi = targetDensityDpi;
DisplayMetrics activityDisplayMerics = activity.getResources().getDisplayMetrics();
activityDisplayMerics.density = targetDensity;
activityDisplayMerics.densityDpi = targetDensityDpi;
}
4. 头条适配方案改进版本
大致思路:在头条适配方案的基础上,通过重写Activity的getResources(),重写冷门单位pt作为基准单位,它是Android 中的一个长度单位:表示一个点,是屏幕的物理尺寸,其大小为 1 英寸的 1 / 72,也就是 72pt 等于 1 英寸
public final class AdaptScreenUtils {
private static List<Field> sMetricsFields;
private AdaptScreenUtils() {
throw new UnsupportedOperationException("u can't instantiate me...");
}
/**
* Adapt for the horizontal screen, and call it in {@link android.app.Activity#getResources()}.
*/
public static Resources adaptWidth(final Resources resources, final int designWidth) {
float newXdpi = (resources.getDisplayMetrics().widthPixels * 72f) / designWidth;
applyDisplayMetrics(resources, newXdpi);
return resources;
}
/**
* Adapt for the vertical screen, and call it in {@link android.app.Activity#getResources()}.
*/
public static Resources adaptHeight(final Resources resources, final int designHeight) {
return adaptHeight(resources, designHeight, false);
}
/**
* Adapt for the vertical screen, and call it in {@link android.app.Activity#getResources()}.
*/
public static Resources adaptHeight(final Resources resources, final int designHeight, final boolean includeNavBar) {
float screenHeight = (resources.getDisplayMetrics().heightPixels
+ (includeNavBar ? getNavBarHeight(resources) : 0)) * 72f;
float newXdpi = screenHeight / designHeight;
applyDisplayMetrics(resources, newXdpi);
return resources;
}
private static int getNavBarHeight(final Resources resources) {
int resourceId = resources.getIdentifier("navigation_bar_height", "dimen", "android");
if (resourceId != 0) {
return resources.getDimensionPixelSize(resourceId);
} else {
return 0;
}
}
/**
* @param resources The resources.
* @return the resource
*/
public static Resources closeAdapt(final Resources resources) {
float newXdpi = Resources.getSystem().getDisplayMetrics().density * 72f;
applyDisplayMetrics(resources, newXdpi);
return resources;
}
/**
* Value of pt to value of px.
*
* @param ptValue The value of pt.
* @return value of px
*/
public static int pt2Px(final float ptValue) {
DisplayMetrics metrics = FWAdSDK.sContext.getResources().getDisplayMetrics();
return (int) (ptValue * metrics.xdpi / 72f + 0.5);
}
/**
* Value of px to value of pt.
*
* @param pxValue The value of px.
* @return value of pt
*/
public static int px2Pt(final float pxValue) {
DisplayMetrics metrics = FWAdSDK.sContext.getResources().getDisplayMetrics();
return (int) (pxValue * 72 / metrics.xdpi + 0.5);
}
private static void applyDisplayMetrics(final Resources resources, final float newXdpi) {
resources.getDisplayMetrics().xdpi = newXdpi;
FWAdSDK.sContext.getResources().getDisplayMetrics().xdpi = newXdpi;
applyOtherDisplayMetrics(resources, newXdpi);
}
static void preLoad() {
applyDisplayMetrics(Resources.getSystem(), Resources.getSystem().getDisplayMetrics().xdpi);
}
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;
}
}
}
resCls = resCls.getSuperclass();
if (resCls != null) {
declaredFields = resCls.getDeclaredFields();
} else {
break;
}
}
} else {
applyMetricsFields(resources, newXdpi);
}
}
private static void applyMetricsFields(final Resources resources, final float newXdpi) {
for (Field metricsField : sMetricsFields) {
try {
DisplayMetrics dm = (DisplayMetrics) metricsField.get(resources);
if (dm != null) dm.xdpi = newXdpi;
} catch (Exception e) {
Log.e("AdaptScreenUtils", "applyMetricsFields: " + e);
}
}
}
private static DisplayMetrics getMetricsFromField(final Resources resources, final Field field) {
try {
return (DisplayMetrics) field.get(resources);
} catch (Exception e) {
Log.e("AdaptScreenUtils", "getMetricsFromField: " + e);
return null;
}
}
}
5 .图片如何适配
通过.9