当前位置:   article > 正文

摩客怎么设置安卓的dp_Android屏幕适配——使用 dp 实现完美适配

qemu.sf.lcd_density=280

我们一直知道 Android 可以使用 dp、sp 完成简单的适配,那你真的理解了么?先来看几个问题:

dp 是如何进行适配的?

dp 和 px 是如何换算的?

sp 和 dp 的区别?

dp 适配为什么会有偏差?

如何解决 dp 适配的偏差,达到完美适配?

下面我们就来看下源码,解决这些问题。

概述

在 android.util 包下,有个重要的类就是 DisplayMetrics,它主要是记录显示县官的一些信息,比如大小,密度,缩放系数等等。

如果要想获取到其中的信息很简单,可以通过上下文去拿,例如:

DisplayMetrics dm = context.getResources().getDisplayMetrics();

或者按照源码注释上的示例:

DisplayMetrics metrics = new DisplayMetrics();

getWindowManager().getDefaultDisplay().getMetrics(metrics);

这个类有一大坨常量:

public static final int DENSITY_LOW = 120;

public static final int DENSITY_MEDIUM = 160;

public static final int DENSITY_TV = 213;

public static final int DENSITY_HIGH = 240;

public static final int DENSITY_260 = 260;

public static final int DENSITY_280 = 280;

public static final int DENSITY_300 = 300;

public static final int DENSITY_XHIGH = 320;

public static final int DENSITY_340 = 340;

public static final int DENSITY_360 = 360;

public static final int DENSITY_400 = 400;

public static final int DENSITY_420 = 420;

public static final int DENSITY_440 = 440;

public static final int DENSITY_XXHIGH = 480;

public static final int DENSITY_560 = 560;

public static final int DENSITY_XXXHIGH = 640;

其实最基本的只有DENSITY_LOW、DENSITY_MEDIUM、DENSITY_HIGH、DENSITY_XHIGH、DENSITY_XXHIGH、DENSITY_XXXHIGH,这些都是标准的、主要DPI,其它的都是为了适配引申出来的次要的、介于两者之间的DPI。

DPI 就是 dots-per-inch,每一英寸的像素点。

还有个概念是 PPI,全称是 pixel-per-inch,DPI 和 PPI 是两个不同的概念,即便它们的值有时是一样的,区别嘛...很难描述,大家自己找下资料,这里提到的目的是,有些博客把这两个概念混为一谈是不正确的。

这里主要为大家解读三个参数:

densityDpi:设备的DPI,也就是上面那些常量值,是由手机厂商设置的。

density:缩放系数,这个下面会详细讲解。

scaledDensity:字体缩放系数,和density一样,不过可以受用户设置的字体大小的偏好进行调整。

基础知识

我们先来看下这几个值的赋值,我们发现一个方法,看名字应该是设置默认值。

public void setToDefaults() {

...

density = DENSITY_DEVICE / (float) DENSITY_DEFAULT;

densityDpi = DENSITY_DEVICE;

scaledDensity = density;

...

}

1. densityDpi

先从最简单的下手:

public static final int DENSITY_DEFAULT = DENSITY_MEDIUM;

public static int DENSITY_DEVICE = getDeviceDensity();

private static int getDeviceDensity() {

return SystemProperties.getInt("qemu.sf.lcd_density",

SystemProperties.getInt("ro.sf.lcd_density", DENSITY_DEFAULT));

}

上面代码很好理解,densityDpi 就是从系统属性取值,如果qemu.sf.lcd_density 的值取不到,就取 ro.sf.lcd_density的,还取不到,就使用默认值,也就是 160 了。

qemu.sf.lcd_density 和 ro.sf.lcd_density,手机厂商会写到系统属性中,这样一来,开发者就可以很方便的获取到设备真是的 DPI 了。

2. DENSITY_DEFAULT

这个还是有必要提一句,不要被名字蒙蔽了,它不仅仅是默认值这么简单!

/**

* The reference density used throughout the system.

*/

public static final int DENSITY_DEFAULT = DENSITY_MEDIUM;

注释很重要!!!

注释很重要!!!

注释很重要!!!

整个系统使用的参考密度,这个可以理解为基准,后面计算 density 的缩放系数,都是以它为基准的。

3. density

用 DPI 除以基准 DPI,得到的就是density(缩放系数)。

density = DENSITY_DEVICE / (float) DENSITY_DEFAULT;

那么应该如何理解呢?

如果一个 160dpi 的设备,那么 density 就是 1。

如果一个 120dpi 的设备,那么 density 就是 0.75。

如果一个 240dpi 的设备,那么 density 就是 1.5。

还不理解?

在 1 英寸内有 160 像素点的设备上,缩放系数就是 1。

在 1 英寸内有 120 像素点的设备上,缩放系数就是 0.75。

在 1 英寸内有 240 像素点的设备上,缩放系数就是 1.5。

4. scaledDensity

与 density 一样的,也是缩放系数,默认情况和 density 的值是相等的。

scaledDensity = density;

与 density 的区别是,scaledDensity 会受用户设置显示字体的大小进行缩放。

上面几个关键概念理解清楚了,下面就要说下,如何使用dp和sp完成屏幕的适配了。

dp 和 sp 是如何适配的

1. dp

dp 是 Android 中的一个适配的单位,用来表示大小。

如果设置控件的高度为 20dp,那么会发生什么?如何在不同设备上进行适配的呢?下面会解答这些问题。

上面讲到的 density(缩放系数) 就派上用场了,通过 dp 值乘以 density 就得到了最终的像素值。

这也就是网上经常流传的换算比例:

在 240 * 320 分辨率,DPI为 120,density 是 0.75,1dp=0.75px。

在 320 * 480 分辨率,DPI为 160,density 是 1,1dp=1px。

在 480 * 800 分辨率,DPI为 240,density 是 1.5,1dp=1.5px。

...

以此类推看下图:

图片来自网络

那么这个DPI是如何计算的?

分辨率为 480*800 的设备举例,在 3.8 寸的屏幕下,DPI 应该约等于 240。

计算公式:

通过公式很容易发现一个问题,DPI 受到分辨率和屏幕尺寸两个值影响的,因为它的定义是,每一英寸的像素点,所以相同分辨率下,不同的屏幕尺寸的设备的 DPI 也是有可能不同的。

这也就说明,Android中我们经常提到的 ldpi,mdpi,hdpi 等等这些规格只能与 DPI 挂钩,并不能由设备分辨率进行区分 ldpi,mdpi,hdpi 这些规格,只能说由于手机尺寸比较接近,大多数的情况下,都符合在上面表格。

知道上面的概念了,这就能理解,在不同DPI设备上 20dp 如何进行适配了。

在 120dpi 的设备上,会乘以 0.75 的缩放系数转换为 15px 展示到界面上。

在 160dpi 的设备上,会乘以 1 的缩放系数转换为 20px 展示到界面上。

在 240dpi 的设备上,会乘以 1.5 的缩放系数转换为 30px 展示到界面上。

...

以此类推,实现了适配。

2. sp

sp 是 Android 中的一个适配的单位,用来表示字体大小。

可以理解为和 dp 是一样的,区别是,它是以 scaledDensity 作为缩放系数,默认情况和 density 的值是一样的,但是在调整系统字体大小时,就会随着字体大小的改变而缩放了。

3. TypedValue

上面我说了,dp、sp 转换 px 会乘以缩放比例,计算出真实的 px 值,口说无凭,来看下 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;// dp乘以density缩放系数

case COMPLEX_UNIT_SP:

return value * metrics.scaledDensity;// sp乘以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;

}

如何使用 dp 达到完美适配

理解了这些知识,有什么用?除了面试时可以装一波,更重要的事,可以完善 Android 的 dp 适配机制。

经常使用 dp 适配会发现,只是能达到大致的适配效果,并不完美,会有多个方面造成不准确:

设备厂商设置的 DPI 是否准确。

Android 提供的 DPI 有限,不一定会覆盖到所有设备厂商。

DPI 的计算涉及到分辨率和屏幕尺寸,计算出来的值,都会与系统提供的 DPI 有所偏差。

问题说完了,那么如何利用这套 dp 适配的机制,达到完美适配呢?

density、densityDpi、scaledDensity 这些属性都是可以进行修改的,我们要利用这个搞点事情。

dp 适配偏差都是由于 DPI 不准确,导致 density 缩放系数不准确,出现的问题,如果我们拿到精准的缩放比例,是不是就能将 dp 完美转换为 px 了啊?

我们可以换一下思路,我们要求美工给出一套以 dp 为单位的基准图,例如:基准宽为 320dp,运行在 1080*1920 的设备上。

1. 运行时获取到设备精准的 density

我们可以很轻易的得到缩放系数是 1080 / 320,也就是3.375。

float newDensity = dm.widthPixels / 320;

不要问我,如何在运行时获取到宽高这样的参数,直接看 DisplayMetrics 里面的属性。

/**

* The absolute width of the available display size in pixels.

*/

public int widthPixels;

/**

* The absolute height of the available display size in pixels.

*/

public int heightPixels;

这是就很清晰了吧,有了缩放系数,我们就可以精准的将 1dp 转换为 3.375px 了,达到完美适配。

2. 运行时获取到设备精准的 scaledDensity

得到 newDensity 以后,同样也要获取 newScaledDensity 的值,才能做到对字体 sp 的适配。

float newScaledDensity = newDensity * (dm.density / dm.scaledDensity);

这个还好理解的吧,因为默认状态 density 和 scaledDensity 是相等的,在修改过系统字体大小后,scaledDensity 会进行缩放,所以要将该比例考虑进去。

3. 计算 densityDpi

得到了缩放系数,在设置到 DisplayMetrics 之前,千万不要忘记对 densityDpi 也要进行计算,如果不修改 densityDpi 的话,程序内部计算会出现问题,因为源码中:

density = DENSITY_DEVICE / (float) DENSITY_DEFAULT;

所以我们要反向计算出 densityDpi 的值:

int newDensityDpi = (int) (targetDensity * DisplayMetrics.DENSITY_DEFAULT);

4. 设置 DisplayMetrics 参数

终于到了最后一步,修改属性值:

dm.density = newDensity;

dm.scaledDensity = newScaleDensity;

dm.densityDpi = newDensityDpi;

这样,就可以使用 dp 值完美的适配各种屏幕了。

声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号