Android getDimension()、getDimensionPixelSize()和getDimensionPixelOffset()的详解

最近这一段时间比较忙,都没怎么写博客了,这篇文章主要想介绍几个有关dp、sp和px相互转换的系统api:getDimension()、getDimensionPixelSize()和getDimensionPixelOffset()的区别。
回想起刚学android时,dp转px或sp转px都是用的网上的工具类DensityUtils,如下:

   /**
     * dp转px
     */
    public static int dp2px(float dpVal) {
        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dpVal, Resources.getSystem().getDisplayMetrics());
    }

    /**
     * sp转px
     */
    public static int sp2px(float spVal) {
        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, spVal, Resources.getSystem().getDisplayMetrics());
    }

    /**
     * px转dp
     */
    public static float px2dp(float pxVal) {
        final float scale = Resources.getSystem().getDisplayMetrics().density;
        return (pxVal / scale);
    }

    /**
     * px转sp
     */
    public static float px2sp(float pxVal) {
        return (pxVal / Resources.getSystem().getDisplayMetrics().scaledDensity);
    }
    
其实它们的实现逻辑也是一样的,都是根据系统的api来换算出来的,接下来验证一下,我们来引入utilcodex库:

    implementation 'com.blankj:utilcodex:1.30.6'
    
然后在dimens文件定义几个值:

    <dimen name="dp_41">41dp</dimen>
    <dimen name="px_41">41px</dimen>
    <dimen name="sp_41">41sp</dimen>
    
最后在Activity里打印日志验证我们的结果:

        float density = ScreenUtils.getScreenDensity();
        float scaledDensity = Resources.getSystem().getDisplayMetrics().scaledDensity;
        int densityDpi = ScreenUtils.getScreenDensityDpi();
        Log.e(TAG, "density:" + density);
        Log.e(TAG, "scaledDensity:" + scaledDensity);
        Log.e(TAG, "densityDpi:" + densityDpi);

        float floatDp = this.getResources().getDimension(R.dimen.dp_41);
        int pixelSizeDp = this.getResources().getDimensionPixelSize(R.dimen.dp_41);
        int pixelOffsetDp = this.getResources().getDimensionPixelOffset(R.dimen.dp_41);

        Log.e(TAG, "floatDp:" + floatDp);
        Log.e(TAG, "pixelSizeDp:" + pixelSizeDp);
        Log.e(TAG, "pixelOffsetDp:" + pixelOffsetDp);

        float floatPx = this.getResources().getDimension(R.dimen.px_41);
        int pixelSizePx = this.getResources().getDimensionPixelSize(R.dimen.px_41);
        int pixelOffsetPx = this.getResources().getDimensionPixelOffset(R.dimen.px_41);

        Log.e(TAG, "floatPx:" + floatPx);
        Log.e(TAG, "pixelSizePx:" + pixelSizePx);
        Log.e(TAG, "pixelOffsetPx:" + pixelOffsetPx);

        float floatSp = this.getResources().getDimension(R.dimen.sp_41);
        int pixelSizeSp = this.getResources().getDimensionPixelSize(R.dimen.sp_41);
        int pixelOffsetSp = this.getResources().getDimensionPixelOffset(R.dimen.sp_41);

        Log.e(TAG, "floatSp:" + floatSp);
        Log.e(TAG, "pixelSizeSp:" + pixelSizeSp);
        Log.e(TAG, "pixelOffsetSp:" + pixelOffsetSp);

        Log.e(TAG, "sp2px:" + DensityUtil.sp2px(40f));
        Log.e(TAG, "dp2px:" + DensityUtil.dp2px(40f));
        
其中density 是我们测试手机的屏幕密度,scaledDensity是我们手机的缩放密度,而densityDpi 是像素密度,不太了解的可去度娘看看。 日志结果

2021-08-16 16:52:58.943 17590-17590/com.littlejerk.sample E/DpActivity: density:2.75
2021-08-16 16:52:58.943 17590-17590/com.littlejerk.sample E/DpActivity: scaledDensity:2.75
2021-08-16 16:52:58.943 17590-17590/com.littlejerk.sample E/DpActivity: densityDpi:440
2021-08-16 16:52:58.944 17590-17590/com.littlejerk.sample E/DpActivity: floatDp:112.75
2021-08-16 16:52:58.944 17590-17590/com.littlejerk.sample E/DpActivity: pixelSizeDp:113
2021-08-16 16:52:58.944 17590-17590/com.littlejerk.sample E/DpActivity: pixelOffsetDp:112
2021-08-16 16:52:58.944 17590-17590/com.littlejerk.sample E/DpActivity: floatPx:41.0
2021-08-16 16:52:58.944 17590-17590/com.littlejerk.sample E/DpActivity: pixelSizePx:41
2021-08-16 16:52:58.944 17590-17590/com.littlejerk.sample E/DpActivity: pixelOffsetPx:41
2021-08-16 16:52:58.944 17590-17590/com.littlejerk.sample E/DpActivity: floatSp:112.75
2021-08-16 16:52:58.944 17590-17590/com.littlejerk.sample E/DpActivity: pixelSizeSp:113
2021-08-16 16:52:58.944 17590-17590/com.littlejerk.sample E/DpActivity: pixelOffsetSp:112
2021-08-16 16:52:58.944 17590-17590/com.littlejerk.sample E/DpActivity: sp2px:110
2021-08-16 16:52:58.944 17590-17590/com.littlejerk.sample E/DpActivity: dp2px:110

从打印结果就可以推知
  1. getDimension()、getDimensionPixelSize和getDimensionPixelOffset方法传入的参数单位不同,换算也不同;对于小数点的精确也不同。
  2. getDimension()方法返回的是float类型,不会对小数点进行额外的操作,getDimensionPixelSize()和getDimensionPixelOffset()方法返回的虽然都是int类型,但是前者会进行四舍五入操作,而后者会直接舍弃小数点后面的值。
  3. 三个方法传入参数的单位不同,它们的换算方式也不同。
  4. 对于dp单位的值,换算方式为density乘以该dp值;对于sp单位的值,换算方式为scaledDensity乘以该sp的值;而对于px来说,没做任何运算,直接等于该px值
可能有人会对sp单位的换算有疑问,为何是scaledDensity而不是density呢?确实是会有这样的问题,因为在测试机上scaledDensity和density的值是一样呀。

其实要想验证这个问题也不难,可以直接去手机设置修改字体的大小,这样scaledDensity和density就不一样了。还有一个方法就是直接看这些方法里面的实现,这样就可以百分百确认我们的推论了,进入这三个方法就可知道,它们最后都调用了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;
    }
看到这就一目了然了吧,这完全说明上面的结论是对的。
还有个问题是为啥工具类换算出来的值相差了一点呢?其实这也是小数点和取整造成的,1px左右的差距倒影响不大,具体使用哪几个方法获取px,看自己喜好了。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

黄小梁

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值