我们一直知道 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 值完美的适配各种屏幕了。

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

  1. 摩客怎么设置安卓的dp_Android屏幕适配之单位DP

    基本概念 名词概念 名称 解释 px 像素点,一个像素点为1px dp 即dip,像素密度 sp 同dp,但是可以根据系统字体偏好缩放 dpi 每英寸的像素数,也叫做屏幕密度 换算关系 根据换算关系: ...

  2. 摩客怎么设置安卓的dp_Android中sp和dp区别

    --- title: Android中sp和dp区别 date: 2016-09-08 17:04:15 tags: Android categories: Android开发 --- ## 理解an ...

  3. 摩客怎么设置安卓的dp_Android中的dp

    在Android开发中,我们在描述View的宽.高时通常使用dp,但是设计在UI中进行标注时,却使用的是px.所以很多时候就导致UI和实际效果不一致.史记开发中,给设计和开发人员带来了很多困扰. dp ...

  4. 摩客怎么设置安卓的dp_Android中dp和px之间进行转换

    在xml布局文件中,我们既可以设置px,也可以设置dp(或者dip).一般情况下,我们都会选择使用dp,这样可以保证不同屏幕分辨率的机器上布局一致.但是在代码中,如何处理呢?很多控件的方法中都只提供了 ...

  5. 摩客怎么设置安卓的dp_Android下设置文字大小用sp还是dp

    相信很多人看到标题会大吃一惊,长度宽度的数值要用dp,字体的大小用sp,这个理论恐怕在大家心目中早已根深蒂固,官方文档 上也是这样写的 结论: 1.当修改系统字体大小时,字体大小以dp为单位时,大小不 ...

  6. 摩客怎么设置安卓的dp_安卓屏幕完美适配方案——独家秘笈

    一.为什么要适配 由于Android系统的开放性,任何用户.开发者.硬件厂商.运营商都可以对Android系统和硬件进行定制,修改成他们想要的样子. 但是这种"碎片化"到达什么程度 ...

  7. 摩客怎么设置安卓的dp_摩客【操作攻略】

    很多小伙伴都遇到过摩客的困惑吧,一些朋友看过网上零散的摩客的处理方法,并没有完完全全明白摩客是如何解决的,今天小编准备了简单的解决办法,只需要按照 1:如图所示,我们所做的是一个登陆的简单的线框图,现 ...

  8. 摩客怎么设置安卓的dp_安卓的显示单位dp、dip、px、sp

    dp==dip:设备独立像素 .(device independent pixels) dip是年迈的dp,已经退休,在比较老的代码中还可以看到dip.目前谷歌推荐用dp,在我们开发安卓程序的时候,除 ...

  9. 摩客怎么设置安卓的dp_简单谈谈Android中SP与DP的区别

    从一开始写Android程序,就被告知这些常识 一.dp(或者dip device independent pixels) 一种基于屏幕密度的抽象单位.在每英寸160点的显示器上,1dp=1px.不同 ...

最新文章

  1. 延展信息按单制造ERP荣获2012中国软件优秀解决方案奖
  2. 网球hcc http catcher使用方法以及规则分享
  3. 将JPG文件作为EXE执行
  4. ARM和NEON指令 very gooooooood.............
  5. 酒店wifi代理服务器没有响应,wn10连接酒店wifi的登录界面无法弹出如何处理
  6. 前端学习(1411):多人管理31数据分页2
  7. # 生成单色位二维码图_如何2个小时内学会ps抠图-纯干货
  8. 基于注解的Spring MVC与JPA如何解决实体的延时加载问题
  9. python学习之路(10)--难点
  10. blade利刃出鞘】一起进入移动端webapp开发吧
  11. 发送需要smtp认证的邮件
  12. CSS标签选择器→教你如何使用
  13. Vijos P1423 最佳路线
  14. 计算机怎么把日历和时间放到桌面上,怎么把日历放到电脑桌面
  15. 用计算机技术辅助语文教学,利用计算机技术辅助拼音学习“潜力无限”
  16. php转jsp,阿里西西Html多功能代码转换器(html转js/jsp/php工具)
  17. CenterNet: Keypoint Triplets for Object Detection论文详解
  18. 使用CM快速搭建CDH集群
  19. 数据库事务的四大特性和隔离级别,一文带你看通透
  20. ktt算法 约化_深度学习面试题

热门文章

  1. group by内部排序
  2. 学习HTML简单设计登录网页
  3. Python 基础学习 locals()用法
  4. ggplot2箱式图两两比较_如何在ggplot2图形上添加显著性差异注释?
  5. 【bzoj2563】阿狸和桃子的游戏 脑洞
  6. RS485接口modbus协议RTU方式
  7. 如何成为企业急需的技术人才:掌握这些技能,提升你的实力和竞争力
  8. android小作业,Android studio写的备忘录(记事本)| CSDN打卡
  9. glPushMatrix()和glPopMatrix()
  10. 后现代婚礼机器人显神通_预见机器人:各显神通 各国机器人军团炫目来袭(一)...