Android px density densityDpi dp 之间的关系和区别

Posted guangdeshishe

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android px density densityDpi dp 之间的关系和区别相关的知识,希望对你有一定的参考价值。

px

表示屏幕的物理像素,例如 1080×1920

densityDpi

简称dpi,我们常说的像素密度,表示1英寸上对应有多少个像素,例如 160dpi、320dpi、440dpi、480dpi.
以1080×1920的5英寸手机为例:
宽:1080像素
高:1920像素
对角线(5英寸):根据勾股定理算出大约2203像素
每英寸上大约有:440像素 = 2203/5
所以这个手机的像素密度是440dpi

dp

dip的别称,dp是android上为了适配不同屏幕设备而定义的一种抽象单位,1dp可以理解为屏幕上的一个点,这个点在不同屏幕分辨率上占用的像素不一样,比如在屏幕分辨率高的设备上占用更多像素,在分辨率小的设备上占用更少的像素,从而让它整体上来看显示的比例相对屏幕大小是一样的

density

与densityDpi类似,densityDpi表示1英寸上对应有多少个像素(对应屏幕物理像素密度); 而density则表示1dp上有多少个像素,可以理解为dp密度(对应抽象的屏幕密度);它可以用于dp和px之间转换
计算公式:density = px/dp;
它与densityDpi之间的关系是固定的:density = densityDpi / 160
例如:
当densityDpi=160时,density=1,也就是1英寸上有160个像素,1dp就代表1像素
当densityDpi=320时,density=2,也就是1英寸上有320个像素,1dp就代表2像素

为什么dip用160作为标准呢?主要有两个原因

  • Android 主流设备的 dpi 有:120 dpi、160 dpi、240 dpi、320 dpi、440dpi、480dpi;在开发过程中,因为需要对多个主流设备尺寸适配,在以某个dpi标准设计好后,需要转换成其他主流dpi尺寸;上面主流dpi它们分别是160的0.75、1、1.5、2、2.75、3倍,也就是它们倍数是有限小数;
    而如果假设以 240 dpi 作为标准,它们分别是240的0.5、0.66666666666…、1、1.3333333…、1.8333…、2倍,也就是算出来没有那么精准,误差更大
  • 在Google的官方文档中有给出了解释,因为第一款Android设备(HTC的T-Mobile G1)是属于160dpi的

小结

  • px和densityDpi代表真实的物理屏幕尺寸和密度
  • dp和density表示Android中为了让不同屏幕尺寸上显示效果接近一致而抽象出来的,表示抽象的屏幕尺寸和密度

扩展

获取屏幕宽高和dpi以及density

//kotlin
var displayMetrics = DisplayMetrics()
windowManager.defaultDisplay.getRealMetrics(displayMetrics) //从WindowManager获取DisplayMetrics

displayMetrics = resources.displayMetrics //从Resources获取DisplayMetrics

displayMetrics.widthPixels //屏幕宽度
displayMetrics.heightPixels //屏幕高度
displayMetrics.density //dp和px比例
displayMetrics.densityDpi //像素密度

val point = Point()
windowManager.defaultDisplay.getRealSize(point);
point.x //屏幕宽度
point.y //屏幕高度

px和dp互转

  • 之所以要将结果+0.5再转int,是因为float类型直接转int会向下取整,加上0.5再转就能达到四舍五入效果;例如:
    1.3直接转int后结果是1,加上0.5后是1.8,再转int后结果也还是1
    1.6直接转int后结果是1,加上0.5后是2.1,再转int后就是四舍五入效果2了
fun dp2px(context: Context, dpValue: Float): Int {
 val density = context.resources.displayMetrics.density
    return (dpValue * density + 0.5).toInt()
}
fun px2dp(context: Context, pxValue: Float): Int {
    val density = context.resources.displayMetrics.density
    return (pxValue / density + 0.5).toInt()
}

屏幕适配方案

fun setCustomDensity(activity :Activity){
    val designWidthDp = 360 //假设设计图按照宽度360dp大小进行设计
    var displayMetrics = activity.resources.displayMetrics
    //计算出每个dp上有多少个像素,也就是density值
    val targetDensity = displayMetrics.widthPixels / designWidthDp
    // 重新计算dpi值
    val targetDensityDpi = targetDensity * 160
    displayMetrics.density = targetDensity.toFloat()
    displayMetrics.densityDpi = targetDensityDpi
    
    //Application的DisplayMetrics也要设置
    displayMetrics = activity.application.resources.displayMetrics
    displayMetrics.density = targetDensity.toFloat()
    displayMetrics.densityDpi = targetDensityDpi
}

为什么要根据设计图dp宽度调整density值?

dp在Android只是个抽象单位,最终还是会根据density转换成px值;
假如按照屏幕宽度540dp设计UI图,绘制一条长度为540dp的线条,代码中设置该线条长度为540dp

  • 在设备1上:
    分辨率1080×1920
    转换dp: 540dp x 960dp
    像素密度:320dpi
    density = 2
    线条实际长度:540dp * 2(density) = 1080px
    显示效果:长度刚好是屏幕的宽度,与设计图效果一致

  • 在设备2上:
    分辨率:1080×1920
    转换dp: 1080dp x 1920dp
    像素密度:160dpi
    density = 1
    线条实际长度:540dp * 1(density) = 540px
    显示效果:长度只有屏幕宽度的一半,与设计图差别很大

  • 经过适配重新调整设备2的DisplayMetrics参数后:
    newDensity=1080(屏幕分辨率)/540(设计图屏幕宽度)=2
    线条实际长度:540dp * 2(newDensity) = 1080px
    显示效果:长度刚好是屏幕的宽度,与设计图效果一致

以上是关于Android px density densityDpi dp 之间的关系和区别的主要内容,如果未能解决你的问题,请参考以下文章

Android:屏幕显示适配实战 , 详解 PX DPI DP/DIP Density的区别。

Android app开发知识小结

android: getDimension, getDimensionPixelOffset 和getDimensionPixelSize 区别

Android屏幕适配

android px,dp,sp大小转换工具

Android 单位dp和px之间相互转换